ncurses 5.9 - patch 20130105
[ncurses.git] / ncurses / win32con / win_driver.c
1 /****************************************************************************
2  * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /****************************************************************************
30  *  Author: Juergen Pfeifer                                                 *
31  ****************************************************************************/
32
33 /*
34  * TODO - GetMousePos(POINT * result) from ntconio.c
35  * TODO - implement nodelay
36  */
37
38 #include <curses.priv.h>
39 #define CUR my_term.type.
40
41 MODULE_ID("$Id: win_driver.c,v 1.16 2013/01/05 23:16:54 tom Exp $")
42
43 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
44
45 #define EXP_OPTIMIZE 0
46
47 #define AssertTCB() assert(TCB!=0 && TCB->magic==WINMAGIC)
48 #define SetSP() assert(TCB->csp!=0); sp = TCB->csp; (void) sp
49
50 #define GenMap(vKey,key) MAKELONG(key, vKey)
51
52 static const LONG keylist[] =
53 {
54     GenMap(VK_PRIOR, KEY_PPAGE),
55     GenMap(VK_NEXT, KEY_NPAGE),
56     GenMap(VK_END, KEY_END),
57     GenMap(VK_HOME, KEY_HOME),
58     GenMap(VK_LEFT, KEY_LEFT),
59     GenMap(VK_UP, KEY_UP),
60     GenMap(VK_RIGHT, KEY_RIGHT),
61     GenMap(VK_DOWN, KEY_DOWN),
62     GenMap(VK_DELETE, KEY_DC),
63     GenMap(VK_INSERT, KEY_IC)
64 };
65 #define N_INI ((int)(sizeof(keylist)/sizeof(keylist[0])))
66 #define FKEYS 24
67 #define MAPSIZE (FKEYS + N_INI)
68 #define NUMPAIRS 64
69
70 typedef struct props {
71     CONSOLE_SCREEN_BUFFER_INFO SBI;
72     bool progMode;
73     DWORD map[MAPSIZE];
74     DWORD rmap[MAPSIZE];
75     WORD pairs[NUMPAIRS];
76 } Properties;
77
78 #define PropOf(TCB) ((Properties*)TCB->prop)
79
80 int
81 _nc_mingw_ioctl(int fd GCC_UNUSED,
82                 long int request GCC_UNUSED,
83                 struct termios *arg GCC_UNUSED)
84 {
85     return 0;
86     endwin();
87     fprintf(stderr, "TERMINFO currently not supported on Windows.\n");
88     exit(1);
89 }
90
91 static WORD
92 MapColor(bool fore, int color)
93 {
94     static const int _cmap[] =
95     {0, 4, 2, 6, 1, 5, 3, 7};
96     int a;
97     if (color < 0 || color > 7)
98         a = fore ? 7 : 0;
99     else
100         a = _cmap[color];
101     if (!fore)
102         a = a << 4;
103     return (WORD) a;
104 }
105
106 static WORD
107 MapAttr(TERMINAL_CONTROL_BLOCK * TCB, WORD res, attr_t ch)
108 {
109     if (ch & A_COLOR) {
110         int p;
111         SCREEN *sp;
112
113         AssertTCB();
114         SetSP();
115         p = PairNumber(ch);
116         if (p > 0 && p < NUMPAIRS && TCB != 0 && sp != 0) {
117             WORD a;
118             a = PropOf(TCB)->pairs[p];
119             res = (res & 0xff00) | a;
120         }
121     }
122
123     if (ch & A_REVERSE)
124         res = ((res & 0xff00) | (((res & 0x07) << 4) | ((res & 0x70) >> 4)));
125
126     if (ch & A_STANDOUT)
127         res = ((res & 0xff00) | (((res & 0x07) << 4) | ((res & 0x70) >> 4))
128                | BACKGROUND_INTENSITY);
129
130     if (ch & A_BOLD)
131         res |= FOREGROUND_INTENSITY;
132
133     if (ch & A_DIM)
134         res |= BACKGROUND_INTENSITY;
135
136     return res;
137 }
138
139 #if USE_WIDEC_SUPPORT
140 /*
141  * TODO: support surrogate pairs
142  * TODO: support combining characters
143  * TODO: support acsc
144  * TODO: check wcwidth of base character, fill if needed for double-width
145  * TODO: _nc_wacs should be part of sp.
146  */
147 static BOOL
148 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
149 {
150     int actual = 0;
151     CHAR_INFO ci[limit];
152     COORD loc, siz;
153     SMALL_RECT rec;
154     int i;
155     cchar_t ch;
156     SCREEN *sp;
157
158     AssertTCB();
159
160     if (TCB == 0 || InvalidConsoleHandle(TCB->hdl))
161         return FALSE;
162
163     SetSP();
164
165     for (i = actual = 0; i < limit; i++) {
166         ch = str[i];
167         if (isWidecExt(ch))
168             continue;
169         ci[actual].Char.UnicodeChar = CharOf(ch);
170         ci[actual].Attributes = MapAttr(TCB,
171                                         PropOf(TCB)->SBI.wAttributes,
172                                         AttrOf(ch));
173         if (AttrOf(ch) & A_ALTCHARSET) {
174             if (_nc_wacs) {
175                 int which = CharOf(ch);
176                 if (which > 0
177                     && which < ACS_LEN
178                     && CharOf(_nc_wacs[which]) != 0) {
179                     ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
180                 } else {
181                     ci[actual].Char.UnicodeChar = ' ';
182                 }
183             }
184         }
185         ++actual;
186     }
187
188     loc.X = (short) 0;
189     loc.Y = (short) 0;
190     siz.X = (short) actual;
191     siz.Y = 1;
192
193     rec.Left = (short) x;
194     rec.Top = (short) y;
195     rec.Right = (short) (x + limit - 1);
196     rec.Bottom = rec.Top;
197
198     return WriteConsoleOutputW(TCB->hdl, ci, siz, loc, &rec);
199 }
200 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
201 #else
202 static BOOL
203 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
204 {
205     CHAR_INFO ci[n];
206     COORD loc, siz;
207     SMALL_RECT rec;
208     int i;
209     chtype ch;
210     SCREEN *sp;
211
212     AssertTCB();
213
214     if (TCB == 0 || InvalidConsoleHandle(TCB->hdl))
215         return FALSE;
216
217     SetSP();
218
219     for (i = 0; i < n; i++) {
220         ch = str[i];
221         ci[i].Char.AsciiChar = ChCharOf(ch);
222         ci[i].Attributes = MapAttr(TCB,
223                                    PropOf(TCB)->SBI.wAttributes,
224                                    ChAttrOf(ch));
225         if (ChAttrOf(ch) & A_ALTCHARSET) {
226             if (sp->_acs_map)
227                 ci[i].Char.AsciiChar =
228                     ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
229         }
230     }
231
232     loc.X = (short) 0;
233     loc.Y = (short) 0;
234     siz.X = (short) n;
235     siz.Y = 1;
236
237     rec.Left = (short) x;
238     rec.Top = (short) y;
239     rec.Right = (short) (x + n - 1);
240     rec.Bottom = rec.Top;
241
242     return WriteConsoleOutput(TCB->hdl, ci, siz, loc, &rec);
243 }
244 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
245 #endif
246
247 #if EXP_OPTIMIZE
248 /*
249  * Comparing new/current screens, determine the last column-index for a change
250  * beginning on the given row,col position.  Unlike a serial terminal, there is
251  * no cost for "moving" the "cursor" on the line as we update it.
252  */
253 static int
254 find_end_of_change(SCREEN *sp, int row, int col)
255 {
256     int result = col;
257     struct ldat *curdat = CurScreen(sp)->_line + row;
258     struct ldat *newdat = NewScreen(sp)->_line + row;
259
260     while (col <= newdat->lastchar) {
261 #if USE_WIDEC_SUPPORT
262         if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
263             result = col;
264         } else if (memcmp(&curdat->text[col],
265                           &newdat->text[col],
266                           sizeof(curdat->text[0]))) {
267             result = col;
268         } else {
269             break;
270         }
271 #else
272         if (curdat->text[col] != newdat->text[col]) {
273             result = col;
274         } else {
275             break;
276         }
277 #endif
278         ++col;
279     }
280     return result;
281 }
282
283 /*
284  * Given a row,col position at the end of a change-chunk, look for the
285  * beginning of the next change-chunk.
286  */
287 static int
288 find_next_change(SCREEN *sp, int row, int col)
289 {
290     struct ldat *curdat = CurScreen(sp)->_line + row;
291     struct ldat *newdat = NewScreen(sp)->_line + row;
292     int result = newdat->lastchar + 1;
293
294     while (++col <= newdat->lastchar) {
295 #if USE_WIDEC_SUPPORT
296         if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
297             result = col;
298             break;
299         } else if (memcmp(&curdat->text[col],
300                           &newdat->text[col],
301                           sizeof(curdat->text[0]))) {
302             result = col;
303             break;
304         }
305 #else
306         if (curdat->text[col] != newdat->text[col]) {
307             result = col;
308             break;
309         }
310 #endif
311     }
312     return result;
313 }
314
315 #define EndChange(first) \
316         find_end_of_change(sp, y, first)
317 #define NextChange(last) \
318         find_next_change(sp, y, last)
319
320 #endif /* EXP_OPTIMIZE */
321
322 #define MARK_NOCHANGE(win,row) \
323                 win->_line[row].firstchar = _NOCHANGE; \
324                 win->_line[row].lastchar  = _NOCHANGE
325
326 static int
327 drv_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
328 {
329     int y, nonempty, n, x0, x1, Width, Height;
330     SCREEN *sp;
331
332     AssertTCB();
333     SetSP();
334
335     Width = screen_columns(sp);
336     Height = screen_lines(sp);
337     nonempty = min(Height, NewScreen(sp)->_maxy + 1);
338
339     if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
340         int x;
341 #if USE_WIDEC_SUPPORT
342         cchar_t empty[Width];
343         wchar_t blank[2] =
344         {
345             L' ', L'\0'
346         };
347
348         for (x = 0; x < Width; x++)
349             setcchar(&empty[x], blank, 0, 0, 0);
350 #else
351         chtype empty[Width];
352
353         for (x = 0; x < Width; x++)
354             empty[x] = ' ';
355 #endif
356
357         for (y = 0; y < nonempty; y++) {
358             con_write(TCB, y, 0, empty, Width);
359             memcpy(empty,
360                    CurScreen(sp)->_line[y].text,
361                    Width * sizeof(empty[0]));
362         }
363         CurScreen(sp)->_clear = FALSE;
364         NewScreen(sp)->_clear = FALSE;
365         touchwin(NewScreen(sp));
366     }
367
368     for (y = 0; y < nonempty; y++) {
369         x0 = NewScreen(sp)->_line[y].firstchar;
370         if (x0 != _NOCHANGE) {
371 #if EXP_OPTIMIZE
372             int x2;
373             int limit = NewScreen(sp)->_line[y].lastchar;
374             while ((x1 = EndChange(x0)) <= limit) {
375                 while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
376                     x1 = x2;
377                 }
378                 n = x1 - x0 + 1;
379                 memcpy(&CurScreen(sp)->_line[y].text[x0],
380                        &NewScreen(sp)->_line[y].text[x0],
381                        n * sizeof(CurScreen(sp)->_line[y].text[x0]));
382                 con_write(TCB,
383                           y,
384                           x0,
385                           &CurScreen(sp)->_line[y].text[x0], n);
386                 x0 = NextChange(x1);
387             }
388
389             /* mark line changed successfully */
390             if (y <= NewScreen(sp)->_maxy) {
391                 MARK_NOCHANGE(NewScreen(sp), y);
392             }
393             if (y <= CurScreen(sp)->_maxy) {
394                 MARK_NOCHANGE(CurScreen(sp), y);
395             }
396 #else
397             x1 = NewScreen(sp)->_line[y].lastchar;
398             n = x1 - x0 + 1;
399             if (n > 0) {
400                 memcpy(&CurScreen(sp)->_line[y].text[x0],
401                        &NewScreen(sp)->_line[y].text[x0],
402                        n * sizeof(CurScreen(sp)->_line[y].text[x0]));
403                 con_write(TCB,
404                           y,
405                           x0,
406                           &CurScreen(sp)->_line[y].text[x0], n);
407
408                 /* mark line changed successfully */
409                 if (y <= NewScreen(sp)->_maxy) {
410                     MARK_NOCHANGE(NewScreen(sp), y);
411                 }
412                 if (y <= CurScreen(sp)->_maxy) {
413                     MARK_NOCHANGE(CurScreen(sp), y);
414                 }
415             }
416 #endif
417         }
418     }
419
420     /* put everything back in sync */
421     for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
422         MARK_NOCHANGE(NewScreen(sp), y);
423     }
424     for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
425         MARK_NOCHANGE(CurScreen(sp), y);
426     }
427
428     if (!NewScreen(sp)->_leaveok) {
429         CurScreen(sp)->_curx = NewScreen(sp)->_curx;
430         CurScreen(sp)->_cury = NewScreen(sp)->_cury;
431
432         TCB->drv->hwcur(TCB, 0, 0, CurScreen(sp)->_cury, CurScreen(sp)->_curx);
433     }
434     SetConsoleActiveScreenBuffer(TCB->hdl);
435     return OK;
436 }
437
438 static bool
439 drv_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
440               const char *tname,
441               int *errret GCC_UNUSED)
442 {
443     bool code = FALSE;
444
445     T((T_CALLED("win32con::drv_CanHandle(%p)"), TCB));
446
447     assert(TCB != 0);
448     assert(tname != 0);
449
450     TCB->magic = WINMAGIC;
451     if (*tname == 0 || *tname == 0 || *tname == '#') {
452         code = TRUE;
453     } else {
454         TERMINAL my_term;
455         int status;
456
457         code = FALSE;
458 #if (USE_DATABASE || USE_TERMCAP)
459         status = _nc_setup_tinfo(tname, &my_term.type);
460 #else
461         status = TGETENT_NO;
462 #endif
463         if (status != TGETENT_YES) {
464             const TERMTYPE *fallback = _nc_fallback(tname);
465
466             if (fallback) {
467                 my_term.type = *fallback;
468                 status = TGETENT_YES;
469             } else if (!strcmp(tname, "unknown")) {
470                 code = TRUE;
471             }
472         }
473         if (status == TGETENT_YES) {
474             if (generic_type || hard_copy)
475                 code = TRUE;
476         }
477     }
478
479     if (code) {
480         if ((TCB->term.type.Booleans) == 0) {
481             _nc_init_entry(&(TCB->term.type));
482         }
483     }
484
485     returnBool(code);
486 }
487
488 static int
489 drv_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
490                 int beepFlag GCC_UNUSED)
491 {
492     SCREEN *sp;
493     int res = ERR;
494
495     AssertTCB();
496     SetSP();
497
498     return res;
499 }
500
501 static int
502 drv_print(TERMINAL_CONTROL_BLOCK * TCB,
503           char *data GCC_UNUSED,
504           int len GCC_UNUSED)
505 {
506     SCREEN *sp;
507
508     AssertTCB();
509     SetSP();
510
511     return ERR;
512 }
513
514 static int
515 drv_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
516                   int fg GCC_UNUSED,
517                   int bg GCC_UNUSED)
518 {
519     SCREEN *sp;
520     int code = ERR;
521
522     AssertTCB();
523     SetSP();
524
525     return (code);
526 }
527
528 static void
529 drv_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
530              int fore,
531              int color,
532              int (*outc) (SCREEN *, int) GCC_UNUSED)
533 {
534     AssertTCB();
535
536     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
537         WORD a = MapColor(fore, color);
538         a = ((PropOf(TCB)->SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f)) | a;
539         SetConsoleTextAttribute(TCB->hdl, a);
540         GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
541     }
542 }
543
544 static bool
545 drv_rescol(TERMINAL_CONTROL_BLOCK * TCB)
546 {
547     bool res = FALSE;
548
549     AssertTCB();
550     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
551         WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
552         SetConsoleTextAttribute(TCB->hdl, a);
553         GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
554         res = TRUE;
555     }
556     return res;
557 }
558
559 static bool
560 drv_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
561 {
562     int result = FALSE;
563     SCREEN *sp;
564
565     AssertTCB();
566     SetSP();
567
568     return result;
569 }
570
571 static int
572 drv_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
573 {
574     AssertTCB();
575
576     if (TCB == NULL || Lines == NULL || Cols == NULL || InvalidConsoleHandle(TCB->hdl))
577         return ERR;
578
579     *Lines = (int) (PropOf(TCB)->SBI.dwSize.Y);
580     *Cols = (int) (PropOf(TCB)->SBI.dwSize.X);
581     return OK;
582 }
583
584 static int
585 drv_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
586             int l GCC_UNUSED,
587             int c GCC_UNUSED)
588 {
589     AssertTCB();
590     return ERR;
591 }
592
593 static int
594 drv_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
595 {
596     DWORD dwFlag = 0;
597     tcflag_t iflag;
598     tcflag_t lflag;
599
600     AssertTCB();
601
602     if (TCB == 0 || buf == NULL)
603         return ERR;
604
605     if (setFlag) {
606         iflag = buf->c_iflag;
607         lflag = buf->c_lflag;
608
609         GetConsoleMode(TCB->inp, &dwFlag);
610
611         if (lflag & ICANON)
612             dwFlag |= ENABLE_LINE_INPUT;
613         else
614             dwFlag &= ~ENABLE_LINE_INPUT;
615
616         if (lflag & ECHO)
617             dwFlag |= ENABLE_ECHO_INPUT;
618         else
619             dwFlag &= ~ENABLE_ECHO_INPUT;
620
621         if (iflag & BRKINT)
622             dwFlag |= ENABLE_PROCESSED_INPUT;
623         else
624             dwFlag &= ~ENABLE_PROCESSED_INPUT;
625
626         dwFlag |= ENABLE_MOUSE_INPUT;
627
628         buf->c_iflag = iflag;
629         buf->c_lflag = lflag;
630         SetConsoleMode(TCB->inp, dwFlag);
631         TCB->term.Nttyb = *buf;
632     } else {
633         iflag = TCB->term.Nttyb.c_iflag;
634         lflag = TCB->term.Nttyb.c_lflag;
635         GetConsoleMode(TCB->inp, &dwFlag);
636
637         if (dwFlag & ENABLE_LINE_INPUT)
638             lflag |= ICANON;
639         else
640             lflag &= ~ICANON;
641
642         if (dwFlag & ENABLE_ECHO_INPUT)
643             lflag |= ECHO;
644         else
645             lflag &= ~ECHO;
646
647         if (dwFlag & ENABLE_PROCESSED_INPUT)
648             iflag |= BRKINT;
649         else
650             iflag &= ~BRKINT;
651
652         TCB->term.Nttyb.c_iflag = iflag;
653         TCB->term.Nttyb.c_lflag = lflag;
654
655         *buf = TCB->term.Nttyb;
656     }
657     return OK;
658 }
659
660 static int
661 drv_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
662 {
663     SCREEN *sp;
664     TERMINAL *_term = (TERMINAL *) TCB;
665     int code = ERR;
666
667     AssertTCB();
668     sp = TCB->csp;
669
670     PropOf(TCB)->progMode = progFlag;
671     SetConsoleActiveScreenBuffer(progFlag ? TCB->hdl : TCB->out);
672
673     if (progFlag) /* prog mode */  {
674         if (defFlag) {
675             if ((drv_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
676                 _term->Nttyb.c_oflag &= ~OFLAGS_TABS;
677                 code = OK;
678             }
679         } else {
680             /* reset_prog_mode */
681             if (drv_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
682                 if (sp) {
683                     if (sp->_keypad_on)
684                         _nc_keypad(sp, TRUE);
685                     NC_BUFFERED(sp, TRUE);
686                 }
687                 code = OK;
688             }
689         }
690     } else {                    /* shell mode */
691         if (defFlag) {
692             /* def_shell_mode */
693             if (drv_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
694                 code = OK;
695             }
696         } else {
697             /* reset_shell_mode */
698             if (sp) {
699                 _nc_keypad(sp, FALSE);
700                 NCURSES_SP_NAME(_nc_flush) (sp);
701                 NC_BUFFERED(sp, FALSE);
702             }
703             code = drv_sgmode(TCB, TRUE, &(_term->Ottyb));
704         }
705     }
706
707     return (code);
708 }
709
710 static void
711 drv_screen_init(SCREEN *sp GCC_UNUSED)
712 {
713 }
714
715 static void
716 drv_wrap(SCREEN *sp GCC_UNUSED)
717 {
718 }
719
720 static int
721 rkeycompare(const void *el1, const void *el2)
722 {
723     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
724     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
725
726     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
727 }
728
729 static int
730 keycompare(const void *el1, const void *el2)
731 {
732     WORD key1 = HIWORD((*((const LONG *) el1)));
733     WORD key2 = HIWORD((*((const LONG *) el2)));
734
735     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
736 }
737
738 static int
739 MapKey(TERMINAL_CONTROL_BLOCK * TCB, WORD vKey)
740 {
741     WORD nKey = 0;
742     void *res;
743     LONG key = GenMap(vKey, 0);
744     int code = -1;
745
746     AssertTCB();
747
748     res = bsearch(&key,
749                   PropOf(TCB)->map,
750                   (size_t) (N_INI + FKEYS),
751                   sizeof(keylist[0]),
752                   keycompare);
753     if (res) {
754         key = *((LONG *) res);
755         nKey = LOWORD(key);
756         code = (int) (nKey & 0x7fff);
757         if (nKey & 0x8000)
758             code = -code;
759     }
760     return code;
761 }
762
763 static void
764 drv_release(TERMINAL_CONTROL_BLOCK * TCB)
765 {
766     T((T_CALLED("win32con::drv_release(%p)"), TCB));
767
768     AssertTCB();
769     if (TCB->prop)
770         free(TCB->prop);
771
772     returnVoid;
773 }
774
775 static void
776 drv_init(TERMINAL_CONTROL_BLOCK * TCB)
777 {
778     DWORD num_buttons;
779
780     T((T_CALLED("win32con::drv_init(%p)"), TCB));
781
782     AssertTCB();
783
784     if (TCB) {
785         BOOL b = AllocConsole();
786         WORD a;
787         int i;
788
789         if (!b)
790             b = AttachConsole(ATTACH_PARENT_PROCESS);
791
792         TCB->inp = GetStdHandle(STD_INPUT_HANDLE);
793         TCB->out = GetStdHandle(STD_OUTPUT_HANDLE);
794
795         if (getenv("NCGDB"))
796             TCB->hdl = TCB->out;
797         else
798             TCB->hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
799                                                  0,
800                                                  NULL,
801                                                  CONSOLE_TEXTMODE_BUFFER,
802                                                  NULL);
803
804         if (!InvalidConsoleHandle(TCB->hdl)) {
805             TCB->prop = typeCalloc(Properties, 1);
806             GetConsoleScreenBufferInfo(TCB->hdl, &(PropOf(TCB)->SBI));
807         }
808
809         TCB->info.initcolor = TRUE;
810         TCB->info.canchange = FALSE;
811         TCB->info.hascolor = TRUE;
812         TCB->info.caninit = TRUE;
813
814         TCB->info.maxpairs = NUMPAIRS;
815         TCB->info.maxcolors = 8;
816         TCB->info.numlabels = 0;
817         TCB->info.labelwidth = 0;
818         TCB->info.labelheight = 0;
819         TCB->info.nocolorvideo = 1;
820         TCB->info.tabsize = 8;
821
822         if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
823             T(("mouse has %ld buttons", num_buttons));
824             TCB->info.numbuttons = num_buttons;
825         } else {
826             TCB->info.numbuttons = 1;
827         }
828
829         TCB->info.defaultPalette = _nc_cga_palette;
830
831         for (i = 0; i < (N_INI + FKEYS); i++) {
832             if (i < N_INI)
833                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] = keylist[i];
834             else
835                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] =
836                     GenMap((VK_F1 + (i - N_INI)), (KEY_F(1) + (i - N_INI)));
837         }
838         qsort(PropOf(TCB)->map,
839               (size_t) (MAPSIZE),
840               sizeof(keylist[0]),
841               keycompare);
842         qsort(PropOf(TCB)->rmap,
843               (size_t) (MAPSIZE),
844               sizeof(keylist[0]),
845               rkeycompare);
846
847         a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
848         for (i = 0; i < NUMPAIRS; i++)
849             PropOf(TCB)->pairs[i] = a;
850     }
851     returnVoid;
852 }
853
854 static void
855 drv_initpair(TERMINAL_CONTROL_BLOCK * TCB,
856              int pair,
857              int f,
858              int b)
859 {
860     SCREEN *sp;
861
862     AssertTCB();
863     SetSP();
864
865     if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
866         && (b >= 0) && (b < 8)) {
867         PropOf(TCB)->pairs[pair] = MapColor(true, f) | MapColor(false, b);
868     }
869 }
870
871 static void
872 drv_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
873               int color GCC_UNUSED,
874               int r GCC_UNUSED,
875               int g GCC_UNUSED,
876               int b GCC_UNUSED)
877 {
878     SCREEN *sp;
879
880     AssertTCB();
881     SetSP();
882 }
883
884 static void
885 drv_do_color(TERMINAL_CONTROL_BLOCK * TCB,
886              int old_pair GCC_UNUSED,
887              int pair GCC_UNUSED,
888              int reverse GCC_UNUSED,
889              int (*outc) (SCREEN *, int) GCC_UNUSED
890 )
891 {
892     SCREEN *sp;
893
894     AssertTCB();
895     SetSP();
896 }
897
898 static void
899 drv_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
900 {
901     SCREEN *sp;
902
903     AssertTCB();
904     SetSP();
905
906     sp->_mouse_type = M_TERM_DRIVER;
907 }
908
909 static int
910 drv_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
911 {
912     int rc = 0;
913     SCREEN *sp;
914
915     AssertTCB();
916     SetSP();
917
918     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
919         rc = TW_MOUSE;
920     } else {
921         rc = TCBOf(sp)->drv->twait(TCBOf(sp),
922                                    TWAIT_MASK,
923                                    delay,
924                                    (int *) 0
925                                    EVENTLIST_2nd(evl));
926     }
927
928     return rc;
929 }
930
931 static int
932 drv_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
933           int yold GCC_UNUSED, int xold GCC_UNUSED,
934           int y, int x)
935 {
936     int ret = ERR;
937     if (TCB && !InvalidConsoleHandle(TCB->hdl)) {
938         COORD loc;
939         loc.X = (short) x;
940         loc.Y = (short) y;
941         SetConsoleCursorPosition(TCB->hdl, loc);
942         ret = OK;
943     }
944     return ret;
945 }
946
947 static void
948 drv_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
949             int labnum GCC_UNUSED,
950             char *text GCC_UNUSED)
951 {
952     SCREEN *sp;
953
954     AssertTCB();
955     SetSP();
956 }
957
958 static void
959 drv_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
960                  int OnFlag GCC_UNUSED)
961 {
962     SCREEN *sp;
963
964     AssertTCB();
965     SetSP();
966 }
967
968 static chtype
969 drv_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
970 {
971     chtype res = A_NORMAL;
972     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
973     return res;
974 }
975
976 static void
977 drv_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
978 {
979     SCREEN *sp;
980
981     AssertTCB();
982     SetSP();
983 }
984
985 static void
986 drv_initacs(TERMINAL_CONTROL_BLOCK * TCB,
987             chtype *real_map GCC_UNUSED,
988             chtype *fake_map GCC_UNUSED)
989 {
990 #define DATA(a,b) { a, b }
991     static struct {
992         int acs_code;
993         int use_code;
994     } table[] = {
995         DATA('a', 0xb1),        /* ACS_CKBOARD  */
996             DATA('f', 0xf8),    /* ACS_DEGREE   */
997             DATA('g', 0xf1),    /* ACS_PLMINUS  */
998             DATA('j', 0xd9),    /* ACS_LRCORNER */
999             DATA('l', 0xda),    /* ACS_ULCORNER */
1000             DATA('k', 0xbf),    /* ACS_URCORNER */
1001             DATA('m', 0xc0),    /* ACS_LLCORNER */
1002             DATA('n', 0xc5),    /* ACS_PLUS     */
1003             DATA('q', 0xc4),    /* ACS_HLINE    */
1004             DATA('t', 0xc3),    /* ACS_LTEE     */
1005             DATA('u', 0xb4),    /* ACS_RTEE     */
1006             DATA('v', 0xc1),    /* ACS_BTEE     */
1007             DATA('w', 0xc2),    /* ACS_TTEE     */
1008             DATA('x', 0xb3),    /* ACS_VLINE    */
1009             DATA('y', 0xf3),    /* ACS_LEQUAL   */
1010             DATA('z', 0xf2),    /* ACS_GEQUAL   */
1011             DATA('0', 0xdb),    /* ACS_BLOCK    */
1012             DATA('{', 0xe3),    /* ACS_PI       */
1013             DATA('}', 0x9c),    /* ACS_STERLING */
1014             DATA(',', 0xae),    /* ACS_LARROW   */
1015             DATA('+', 0xaf),    /* ACS_RARROW   */
1016             DATA('~', 0xf9),    /* ACS_BULLET   */
1017     };
1018 #undef DATA
1019     unsigned n;
1020
1021     SCREEN *sp;
1022     AssertTCB();
1023     SetSP();
1024
1025     for (n = 0; n < SIZEOF(table); ++n) {
1026         real_map[table[n].acs_code] = table[n].use_code | A_ALTCHARSET;
1027         if (sp != 0)
1028             sp->_screen_acs_map[table[n].acs_code] = TRUE;
1029     }
1030 }
1031
1032 static ULONGLONG
1033 tdiff(FILETIME fstart, FILETIME fend)
1034 {
1035     ULARGE_INTEGER ustart;
1036     ULARGE_INTEGER uend;
1037     ULONGLONG diff;
1038
1039     ustart.LowPart = fstart.dwLowDateTime;
1040     ustart.HighPart = fstart.dwHighDateTime;
1041     uend.LowPart = fend.dwLowDateTime;
1042     uend.HighPart = fend.dwHighDateTime;
1043
1044     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1045     return diff;
1046 }
1047
1048 static int
1049 Adjust(int milliseconds, int diff)
1050 {
1051     if (milliseconds == INFINITY)
1052         return milliseconds;
1053     milliseconds -= diff;
1054     if (milliseconds < 0)
1055         milliseconds = 0;
1056     return milliseconds;
1057 }
1058
1059 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1060                      FROM_LEFT_2ND_BUTTON_PRESSED | \
1061                      FROM_LEFT_3RD_BUTTON_PRESSED | \
1062                      FROM_LEFT_4TH_BUTTON_PRESSED | \
1063                      RIGHTMOST_BUTTON_PRESSED)
1064
1065 static int
1066 decode_mouse(TERMINAL_CONTROL_BLOCK * TCB, int mask)
1067 {
1068     SCREEN *sp;
1069     int result = 0;
1070
1071     AssertTCB();
1072     SetSP();
1073
1074     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1075         result |= BUTTON1_PRESSED;
1076     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1077         result |= BUTTON2_PRESSED;
1078     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1079         result |= BUTTON3_PRESSED;
1080     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1081         result |= BUTTON4_PRESSED;
1082
1083     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1084         switch (TCB->info.numbuttons) {
1085         case 1:
1086             result |= BUTTON1_PRESSED;
1087             break;
1088         case 2:
1089             result |= BUTTON2_PRESSED;
1090             break;
1091         case 3:
1092             result |= BUTTON3_PRESSED;
1093             break;
1094         case 4:
1095             result |= BUTTON4_PRESSED;
1096             break;
1097         }
1098     }
1099
1100     return result;
1101 }
1102
1103 static int
1104 drv_twait(TERMINAL_CONTROL_BLOCK * TCB,
1105           int mode,
1106           int milliseconds,
1107           int *timeleft
1108           EVENTLIST_2nd(_nc_eventlist * evl))
1109 {
1110     SCREEN *sp;
1111     INPUT_RECORD inp_rec;
1112     BOOL b;
1113     DWORD nRead = 0, rc = -1;
1114     int code = 0;
1115     FILETIME fstart;
1116     FILETIME fend;
1117     int diff;
1118     bool isImmed = (milliseconds == 0);
1119
1120 #define CONSUME() ReadConsoleInput(TCB->inp,&inp_rec,1,&nRead)
1121
1122     AssertTCB();
1123     SetSP();
1124
1125     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1126                       milliseconds, mode));
1127
1128     if (milliseconds < 0)
1129         milliseconds = INFINITY;
1130
1131     memset(&inp_rec, 0, sizeof(inp_rec));
1132
1133     while (true) {
1134         GetSystemTimeAsFileTime(&fstart);
1135         rc = WaitForSingleObject(TCB->inp, milliseconds);
1136         GetSystemTimeAsFileTime(&fend);
1137         diff = (int) tdiff(fstart, fend);
1138         milliseconds = Adjust(milliseconds, diff);
1139
1140         if (!isImmed && milliseconds == 0)
1141             break;
1142
1143         if (rc == WAIT_OBJECT_0) {
1144             if (mode) {
1145                 b = GetNumberOfConsoleInputEvents(TCB->inp, &nRead);
1146                 if (b && nRead > 0) {
1147                     b = PeekConsoleInput(TCB->inp, &inp_rec, 1, &nRead);
1148                     if (b && nRead > 0) {
1149                         switch (inp_rec.EventType) {
1150                         case KEY_EVENT:
1151                             if (mode & TW_INPUT) {
1152                                 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1153                                 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1154
1155                                 if (inp_rec.Event.KeyEvent.bKeyDown) {
1156                                     if (0 == ch) {
1157                                         int nKey = MapKey(TCB, vk);
1158                                         if ((nKey < 0) || FALSE == sp->_keypad_on) {
1159                                             CONSUME();
1160                                             continue;
1161                                         }
1162                                     }
1163                                     code = TW_INPUT;
1164                                     goto end;
1165                                 } else {
1166                                     CONSUME();
1167                                 }
1168                             }
1169                             continue;
1170                         case MOUSE_EVENT:
1171                             if (decode_mouse(TCB,
1172                                              (inp_rec.Event.MouseEvent.dwButtonState
1173                                               & BUTTON_MASK)) == 0) {
1174                                 CONSUME();
1175                             } else if (mode & TW_MOUSE) {
1176                                 code = TW_MOUSE;
1177                                 goto end;
1178                             }
1179                             continue;
1180                         default:
1181                             SetConsoleActiveScreenBuffer(!PropOf(TCB)->progMode ?
1182                                                          TCB->hdl : TCB->out);
1183                             continue;
1184                         }
1185                     }
1186                 }
1187             }
1188             continue;
1189         } else {
1190             if (rc != WAIT_TIMEOUT) {
1191                 code = -1;
1192                 break;
1193             } else {
1194                 code = 0;
1195                 break;
1196             }
1197         }
1198     }
1199   end:
1200
1201     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1202                       code, errno, milliseconds));
1203
1204     if (timeleft)
1205         *timeleft = milliseconds;
1206
1207     return code;
1208 }
1209
1210 static bool
1211 handle_mouse(TERMINAL_CONTROL_BLOCK * TCB, MOUSE_EVENT_RECORD mer)
1212 {
1213     SCREEN *sp;
1214     MEVENT work;
1215     bool result = FALSE;
1216
1217     AssertTCB();
1218     SetSP();
1219
1220     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1221     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1222
1223     /*
1224      * We're only interested if the button is pressed or released.
1225      * FIXME: implement continuous event-tracking.
1226      */
1227     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1228
1229         memset(&work, 0, sizeof(work));
1230
1231         if (sp->_drv_mouse_new_buttons) {
1232
1233             work.bstate |= decode_mouse(TCB, sp->_drv_mouse_new_buttons);
1234
1235         } else {
1236
1237             /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1238             work.bstate |= (decode_mouse(TCB, sp->_drv_mouse_old_buttons) >> 1);
1239
1240             result = TRUE;
1241         }
1242
1243         work.x = mer.dwMousePosition.X;
1244         work.y = mer.dwMousePosition.Y;
1245
1246         sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1247         sp->_drv_mouse_tail += 1;
1248     }
1249
1250     return result;
1251 }
1252
1253 static int
1254 drv_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1255 {
1256     SCREEN *sp;
1257     int n = 1;
1258     INPUT_RECORD inp_rec;
1259     BOOL b;
1260     DWORD nRead;
1261     WORD vk;
1262
1263     AssertTCB();
1264     assert(buf);
1265     SetSP();
1266
1267     memset(&inp_rec, 0, sizeof(inp_rec));
1268
1269     T((T_CALLED("win32con::drv_read(%p)"), TCB));
1270     while ((b = ReadConsoleInput(TCB->inp, &inp_rec, 1, &nRead))) {
1271         if (b && nRead > 0) {
1272             if (inp_rec.EventType == KEY_EVENT) {
1273                 if (!inp_rec.Event.KeyEvent.bKeyDown)
1274                     continue;
1275                 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
1276                 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1277                 if (*buf == 0) {
1278                     if (sp->_keypad_on) {
1279                         *buf = MapKey(TCB, vk);
1280                         if (0 > (*buf))
1281                             continue;
1282                         else
1283                             break;
1284                     } else
1285                         continue;
1286                 } else {        /* *buf != 0 */
1287                     break;
1288                 }
1289             } else if (inp_rec.EventType == MOUSE_EVENT) {
1290                 if (handle_mouse(TCB, inp_rec.Event.MouseEvent)) {
1291                     *buf = KEY_MOUSE;
1292                     break;
1293                 }
1294             }
1295             continue;
1296         }
1297     }
1298     returnCode(n);
1299 }
1300
1301 static int
1302 drv_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1303 {
1304     Sleep(ms);
1305     return OK;
1306 }
1307
1308 static bool
1309 drv_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int keycode)
1310 {
1311     SCREEN *sp;
1312     WORD nKey;
1313     void *res;
1314     bool found = FALSE;
1315     LONG key = GenMap(0, (WORD) keycode);
1316
1317     AssertTCB();
1318     SetSP();
1319
1320     AssertTCB();
1321
1322     res = bsearch(&key,
1323                   PropOf(TCB)->rmap,
1324                   (size_t) (N_INI + FKEYS),
1325                   sizeof(keylist[0]),
1326                   rkeycompare);
1327     if (res) {
1328         key = *((LONG *) res);
1329         nKey = LOWORD(key);
1330         if (!(nKey & 0x8000))
1331             found = TRUE;
1332     }
1333     return found;
1334 }
1335
1336 static int
1337 drv_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1338 {
1339     SCREEN *sp;
1340     int code = ERR;
1341
1342     AssertTCB();
1343     sp = TCB->csp;
1344
1345     if (sp) {
1346         code = OK;
1347     }
1348     return code;
1349 }
1350
1351 static int
1352 drv_keyok(TERMINAL_CONTROL_BLOCK * TCB, int keycode, int flag)
1353 {
1354     int code = ERR;
1355     SCREEN *sp;
1356     WORD nKey;
1357     WORD vKey;
1358     void *res;
1359     LONG key = GenMap(0, (WORD) keycode);
1360
1361     AssertTCB();
1362     SetSP();
1363
1364     if (sp) {
1365         res = bsearch(&key,
1366                       PropOf(TCB)->rmap,
1367                       (size_t) (N_INI + FKEYS),
1368                       sizeof(keylist[0]),
1369                       rkeycompare);
1370         if (res) {
1371             key = *((LONG *) res);
1372             vKey = HIWORD(key);
1373             nKey = (LOWORD(key)) & 0x7fff;
1374             if (!flag)
1375                 nKey |= 0x8000;
1376             *(LONG *) res = GenMap(vKey, nKey);
1377         }
1378     }
1379     return code;
1380 }
1381
1382 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1383     FALSE,
1384         drv_CanHandle,          /* CanHandle */
1385         drv_init,               /* init */
1386         drv_release,            /* release */
1387         drv_size,               /* size */
1388         drv_sgmode,             /* sgmode */
1389         drv_conattr,            /* conattr */
1390         drv_mvcur,              /* hwcur */
1391         drv_mode,               /* mode */
1392         drv_rescol,             /* rescol */
1393         drv_rescolors,          /* rescolors */
1394         drv_setcolor,           /* color */
1395         drv_dobeepflash,        /* DoBeepFlash */
1396         drv_initpair,           /* initpair */
1397         drv_initcolor,          /* initcolor */
1398         drv_do_color,           /* docolor */
1399         drv_initmouse,          /* initmouse */
1400         drv_testmouse,          /* testmouse */
1401         drv_setfilter,          /* setfilter */
1402         drv_hwlabel,            /* hwlabel */
1403         drv_hwlabelOnOff,       /* hwlabelOnOff */
1404         drv_doupdate,           /* update */
1405         drv_defaultcolors,      /* defaultcolors */
1406         drv_print,              /* print */
1407         drv_size,               /* getsize */
1408         drv_setsize,            /* setsize */
1409         drv_initacs,            /* initacs */
1410         drv_screen_init,        /* scinit */
1411         drv_wrap,               /* scexit */
1412         drv_twait,              /* twait */
1413         drv_read,               /* read */
1414         drv_nap,                /* nap */
1415         drv_kpad,               /* kpad */
1416         drv_keyok,              /* kyOk */
1417         drv_kyExist             /* kyExist */
1418 };