ncurses 5.9 - patch 20140426
[ncurses.git] / ncurses / win32con / win_driver.c
1 /****************************************************************************
2  * Copyright (c) 1998-2013,2014 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  *     and: Thomas E. Dickey                                                *
32  ****************************************************************************/
33
34 /*
35  * TODO - GetMousePos(POINT * result) from ntconio.c
36  * TODO - implement nodelay
37  */
38
39 #include <curses.priv.h>
40 #define CUR my_term.type.
41
42 MODULE_ID("$Id: win_driver.c,v 1.33 2014/04/26 19:32:05 juergen Exp $")
43
44 #define WINMAGIC NCDRV_MAGIC(NCDRV_WINCONSOLE)
45
46 #define EXP_OPTIMIZE 0
47
48 #define okConsoleHandle(TCB) (TCB != 0 && !InvalidConsoleHandle(TCB->hdl))
49
50 #define AssertTCB() assert(TCB != 0 && (TCB->magic == WINMAGIC))
51 #define SetSP()     assert(TCB->csp != 0); sp = TCB->csp; (void) sp
52
53 #define GenMap(vKey,key) MAKELONG(key, vKey)
54
55 #define AdjustY(p) ((p)->buffered ? 0 : (int) (p)->SBI.srWindow.Top)
56
57 static const LONG keylist[] =
58 {
59     GenMap(VK_PRIOR, KEY_PPAGE),
60     GenMap(VK_NEXT, KEY_NPAGE),
61     GenMap(VK_END, KEY_END),
62     GenMap(VK_HOME, KEY_HOME),
63     GenMap(VK_LEFT, KEY_LEFT),
64     GenMap(VK_UP, KEY_UP),
65     GenMap(VK_RIGHT, KEY_RIGHT),
66     GenMap(VK_DOWN, KEY_DOWN),
67     GenMap(VK_DELETE, KEY_DC),
68     GenMap(VK_INSERT, KEY_IC)
69 };
70 #define N_INI ((int)(sizeof(keylist)/sizeof(keylist[0])))
71 #define FKEYS 24
72 #define MAPSIZE (FKEYS + N_INI)
73 #define NUMPAIRS 64
74 #define HANDLE_CAST(f) (HANDLE)(intptr_t)(f)
75
76 typedef struct props {
77     CONSOLE_SCREEN_BUFFER_INFO SBI;
78     bool progMode;
79     TERM_HANDLE lastOut;
80     DWORD map[MAPSIZE];
81     DWORD rmap[MAPSIZE];
82     WORD pairs[NUMPAIRS];
83     bool buffered;              /* normally allocate console-buffer */
84     bool window_only;           /* ..if not, we save buffer or window-only */
85     COORD origin;
86     CHAR_INFO *save_screen;
87 } Properties;
88
89 #define PropOf(TCB) ((Properties*)TCB->prop)
90
91 #if WINVER >= 0x0600
92 /*   This function tests, whether or not the ncurses application
93      is running as a descendant of MSYS2/cygwin mintty terminal
94      application. mintty doesn't use Windows Console for it's screen
95      I/O, so the native Windows _isatty doesn't recognize it as
96      character device. But we can discover we are at the end of an
97      Pipe and can query to server side of the pipe, looking whether
98      or not this is mintty.
99  */
100 static int
101 _ismintty(int fd, LPHANDLE pMinTTY)
102 {
103     HANDLE handle;
104     DWORD dw;
105
106     handle = HANDLE_CAST(_get_osfhandle(fd));
107     if (handle == INVALID_HANDLE_VALUE)
108         return 0;
109
110     dw = GetFileType(handle);
111     if (dw == FILE_TYPE_PIPE) {
112         if (GetNamedPipeInfo(handle, 0, 0, 0, 0)) {
113             ULONG pPid;
114             /* Requires NT6 */
115             if (GetNamedPipeServerProcessId(handle, &pPid)) {
116                 TCHAR buf[MAX_PATH];
117                 DWORD len = 0;
118                 /* These security attributes may allow us to
119                    create a remote thread in mintty to manipulate
120                    the terminal state remotely */
121                 HANDLE pHandle = OpenProcess(PROCESS_CREATE_THREAD
122                                              | PROCESS_QUERY_INFORMATION
123                                              | PROCESS_VM_OPERATION
124                                              | PROCESS_VM_WRITE
125                                              | PROCESS_VM_READ,
126                                              FALSE,
127                                              pPid);
128                 if (pMinTTY)
129                     *pMinTTY = INVALID_HANDLE_VALUE;
130                 if (pHandle == INVALID_HANDLE_VALUE)
131                     return 0;
132                 if (len = GetProcessImageFileName(pHandle,
133                                                   buf,
134                                                   (DWORD) (sizeof(buf) /
135                                                            sizeof(*buf)))) {
136                     TCHAR *pos = _tcsrchr(buf, _T('\\'));
137                     if (pos) {
138                         pos++;
139                         if (_tcsnicmp(pos, _TEXT("mintty.exe"), 10) == 0) {
140                             if (pMinTTY)
141                                 *pMinTTY = pHandle;
142                             return 1;
143                         }
144                     }
145                 }
146             }
147         }
148     }
149     return 0;
150 }
151 #endif
152
153 /*   Our replacement for the systems _isatty to include also
154      a test for mintty. This is called from the NC_ISATTY macro
155      defined in curses.priv.h
156  */
157 int
158 _nc_mingw_isatty(int fd)
159 {
160     if (_isatty(fd))
161         return 1;
162 #if WINVER < 0x0600
163     return 0;
164 #else
165     return _ismintty(fd, NULL);
166 #endif
167 }
168
169 /*   Borrowed from ansicon project.
170      Check whether or not a I/O handle is associated with
171      a Windows console.
172 */
173 static BOOL
174 IsConsoleHandle(HANDLE hdl)
175 {
176     DWORD dwFlag = 0;
177     if (!GetConsoleMode(hdl, &dwFlag)) {
178         return (int) WriteConsoleA(hdl, NULL, 0, &dwFlag, NULL);
179     }
180     return (int) (dwFlag & ENABLE_PROCESSED_OUTPUT);
181 }
182
183 /*   This is used when running in terminfo mode to discover,
184      whether or not the "terminal" is actually a Windows
185      Console. It's the responsibilty of the console to deal
186      with the terminal escape sequences that are sent by
187      terminfo.
188  */
189 int
190 _nc_mingw_isconsole(int fd)
191 {
192     HANDLE hdl = HANDLE_CAST(_get_osfhandle(fd));
193     return (int) IsConsoleHandle(hdl);
194 }
195
196 int
197 _nc_mingw_ioctl(int fd GCC_UNUSED,
198                 long int request GCC_UNUSED,
199                 struct termios *arg GCC_UNUSED)
200 {
201     return 0;
202     endwin();
203     fprintf(stderr, "TERMINFO currently not supported on Windows.\n");
204     exit(1);
205 }
206
207 static WORD
208 MapColor(bool fore, int color)
209 {
210     static const int _cmap[] =
211     {0, 4, 2, 6, 1, 5, 3, 7};
212     int a;
213     if (color < 0 || color > 7)
214         a = fore ? 7 : 0;
215     else
216         a = _cmap[color];
217     if (!fore)
218         a = a << 4;
219     return (WORD) a;
220 }
221
222 static WORD
223 MapAttr(TERMINAL_CONTROL_BLOCK * TCB, WORD res, attr_t ch)
224 {
225     if (ch & A_COLOR) {
226         int p;
227         SCREEN *sp;
228
229         AssertTCB();
230         SetSP();
231         p = PairNumber(ch);
232         if (p > 0 && p < NUMPAIRS && TCB != 0 && sp != 0) {
233             WORD a;
234             a = PropOf(TCB)->pairs[p];
235             res = (WORD) ((res & 0xff00) | a);
236         }
237     }
238
239     if (ch & A_REVERSE) {
240         res = (WORD) ((res & 0xff00) |
241                       (((res & 0x07) << 4) |
242                        ((res & 0x70) >> 4)));
243     }
244
245     if (ch & A_STANDOUT) {
246         res = (WORD) ((res & 0xff00) |
247                       (((res & 0x07) << 4) |
248                        ((res & 0x70) >> 4)) |
249                       BACKGROUND_INTENSITY);
250     }
251
252     if (ch & A_BOLD)
253         res |= FOREGROUND_INTENSITY;
254
255     if (ch & A_DIM)
256         res |= BACKGROUND_INTENSITY;
257
258     return res;
259 }
260
261 #if USE_WIDEC_SUPPORT
262 /*
263  * TODO: support surrogate pairs
264  * TODO: support combining characters
265  * TODO: support acsc
266  * TODO: check wcwidth of base character, fill if needed for double-width
267  * TODO: _nc_wacs should be part of sp.
268  */
269 static BOOL
270 con_write16(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, cchar_t *str, int limit)
271 {
272     int actual = 0;
273     CHAR_INFO ci[limit];
274     COORD loc, siz;
275     SMALL_RECT rec;
276     int i;
277     cchar_t ch;
278     SCREEN *sp;
279     Properties *p = PropOf(TCB);
280
281     AssertTCB();
282
283     SetSP();
284
285     for (i = actual = 0; i < limit; i++) {
286         ch = str[i];
287         if (isWidecExt(ch))
288             continue;
289         ci[actual].Char.UnicodeChar = CharOf(ch);
290         ci[actual].Attributes = MapAttr(TCB,
291                                         PropOf(TCB)->SBI.wAttributes,
292                                         AttrOf(ch));
293         if (AttrOf(ch) & A_ALTCHARSET) {
294             if (_nc_wacs) {
295                 int which = CharOf(ch);
296                 if (which > 0
297                     && which < ACS_LEN
298                     && CharOf(_nc_wacs[which]) != 0) {
299                     ci[actual].Char.UnicodeChar = CharOf(_nc_wacs[which]);
300                 } else {
301                     ci[actual].Char.UnicodeChar = ' ';
302                 }
303             }
304         }
305         ++actual;
306     }
307
308     loc.X = (short) 0;
309     loc.Y = (short) 0;
310     siz.X = (short) actual;
311     siz.Y = 1;
312
313     rec.Left = (short) x;
314     rec.Top = (SHORT) (y + AdjustY(p));
315     rec.Right = (short) (x + limit - 1);
316     rec.Bottom = rec.Top;
317
318     return WriteConsoleOutputW(TCB->hdl, ci, siz, loc, &rec);
319 }
320 #define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
321 #else
322 static BOOL
323 con_write8(TERMINAL_CONTROL_BLOCK * TCB, int y, int x, chtype *str, int n)
324 {
325     CHAR_INFO ci[n];
326     COORD loc, siz;
327     SMALL_RECT rec;
328     int i;
329     chtype ch;
330     SCREEN *sp;
331
332     AssertTCB();
333
334     SetSP();
335
336     for (i = 0; i < n; i++) {
337         ch = str[i];
338         ci[i].Char.AsciiChar = ChCharOf(ch);
339         ci[i].Attributes = MapAttr(TCB,
340                                    PropOf(TCB)->SBI.wAttributes,
341                                    ChAttrOf(ch));
342         if (ChAttrOf(ch) & A_ALTCHARSET) {
343             if (sp->_acs_map)
344                 ci[i].Char.AsciiChar =
345                 ChCharOf(NCURSES_SP_NAME(_nc_acs_char) (sp, ChCharOf(ch)));
346         }
347     }
348
349     loc.X = (short) 0;
350     loc.Y = (short) 0;
351     siz.X = (short) n;
352     siz.Y = 1;
353
354     rec.Left = (short) x;
355     rec.Top = (short) y;
356     rec.Right = (short) (x + n - 1);
357     rec.Bottom = rec.Top;
358
359     return WriteConsoleOutput(TCB->hdl, ci, siz, loc, &rec);
360 }
361 #define con_write(tcb, y, x, str, n) con_write8(tcb, y, x, str, n)
362 #endif
363
364 #if EXP_OPTIMIZE
365 /*
366  * Comparing new/current screens, determine the last column-index for a change
367  * beginning on the given row,col position.  Unlike a serial terminal, there is
368  * no cost for "moving" the "cursor" on the line as we update it.
369  */
370 static int
371 find_end_of_change(SCREEN *sp, int row, int col)
372 {
373     int result = col;
374     struct ldat *curdat = CurScreen(sp)->_line + row;
375     struct ldat *newdat = NewScreen(sp)->_line + row;
376
377     while (col <= newdat->lastchar) {
378 #if USE_WIDEC_SUPPORT
379         if (isWidecExt(curdat->text[col]) || isWidecExt(newdat->text[col])) {
380             result = col;
381         } else if (memcmp(&curdat->text[col],
382                           &newdat->text[col],
383                           sizeof(curdat->text[0]))) {
384             result = col;
385         } else {
386             break;
387         }
388 #else
389         if (curdat->text[col] != newdat->text[col]) {
390             result = col;
391         } else {
392             break;
393         }
394 #endif
395         ++col;
396     }
397     return result;
398 }
399
400 /*
401  * Given a row,col position at the end of a change-chunk, look for the
402  * beginning of the next change-chunk.
403  */
404 static int
405 find_next_change(SCREEN *sp, int row, int col)
406 {
407     struct ldat *curdat = CurScreen(sp)->_line + row;
408     struct ldat *newdat = NewScreen(sp)->_line + row;
409     int result = newdat->lastchar + 1;
410
411     while (++col <= newdat->lastchar) {
412 #if USE_WIDEC_SUPPORT
413         if (isWidecExt(curdat->text[col]) != isWidecExt(newdat->text[col])) {
414             result = col;
415             break;
416         } else if (memcmp(&curdat->text[col],
417                           &newdat->text[col],
418                           sizeof(curdat->text[0]))) {
419             result = col;
420             break;
421         }
422 #else
423         if (curdat->text[col] != newdat->text[col]) {
424             result = col;
425             break;
426         }
427 #endif
428     }
429     return result;
430 }
431
432 #define EndChange(first) \
433         find_end_of_change(sp, y, first)
434 #define NextChange(last) \
435         find_next_change(sp, y, last)
436
437 #endif /* EXP_OPTIMIZE */
438
439 #define MARK_NOCHANGE(win,row) \
440                 win->_line[row].firstchar = _NOCHANGE; \
441                 win->_line[row].lastchar  = _NOCHANGE
442
443 static void
444 selectActiveHandle(TERMINAL_CONTROL_BLOCK * TCB)
445 {
446     if (PropOf(TCB)->lastOut != TCB->hdl) {
447         PropOf(TCB)->lastOut = TCB->hdl;
448         SetConsoleActiveScreenBuffer(PropOf(TCB)->lastOut);
449     }
450 }
451
452 static bool
453 restore_original_screen(TERMINAL_CONTROL_BLOCK * TCB)
454 {
455     COORD bufferCoord;
456     SMALL_RECT writeRegion;
457     Properties *p = PropOf(TCB);
458     bool result = FALSE;
459
460     if (p->window_only) {
461         writeRegion.Top = p->SBI.srWindow.Top;
462         writeRegion.Left = p->SBI.srWindow.Left;
463         writeRegion.Bottom = p->SBI.srWindow.Bottom;
464         writeRegion.Right = p->SBI.srWindow.Right;
465         T(("... restoring window"));
466     } else {
467         writeRegion.Top = 0;
468         writeRegion.Left = 0;
469         writeRegion.Bottom = (SHORT) (p->SBI.dwSize.Y - 1);
470         writeRegion.Right = (SHORT) (p->SBI.dwSize.X - 1);
471         T(("... restoring entire buffer"));
472     }
473
474     bufferCoord.X = bufferCoord.Y = 0;
475
476     if (WriteConsoleOutput(TCB->hdl,
477                            p->save_screen,
478                            p->SBI.dwSize,
479                            bufferCoord,
480                            &writeRegion)) {
481         result = TRUE;
482         mvcur(-1, -1, LINES - 2, 0);
483     }
484     T(("... restore original screen contents %s", result ? "ok" : "err"));
485     return result;
486 }
487
488 static const char *
489 wcon_name(TERMINAL_CONTROL_BLOCK * TCB)
490 {
491     (void) TCB;
492     return "win32console";
493 }
494
495 static int
496 wcon_doupdate(TERMINAL_CONTROL_BLOCK * TCB)
497 {
498     int result = ERR;
499     int y, nonempty, n, x0, x1, Width, Height;
500     SCREEN *sp;
501
502     AssertTCB();
503     SetSP();
504
505     T((T_CALLED("win32con::wcon_doupdate(%p)"), TCB));
506     if (okConsoleHandle(TCB)) {
507
508         Width = screen_columns(sp);
509         Height = screen_lines(sp);
510         nonempty = min(Height, NewScreen(sp)->_maxy + 1);
511
512         if ((CurScreen(sp)->_clear || NewScreen(sp)->_clear)) {
513             int x;
514 #if USE_WIDEC_SUPPORT
515             cchar_t empty[Width];
516             wchar_t blank[2] =
517             {
518                 L' ', L'\0'
519             };
520
521             for (x = 0; x < Width; x++)
522                 setcchar(&empty[x], blank, 0, 0, 0);
523 #else
524             chtype empty[Width];
525
526             for (x = 0; x < Width; x++)
527                 empty[x] = ' ';
528 #endif
529
530             for (y = 0; y < nonempty; y++) {
531                 con_write(TCB, y, 0, empty, Width);
532                 memcpy(empty,
533                        CurScreen(sp)->_line[y].text,
534                        (size_t) Width * sizeof(empty[0]));
535             }
536             CurScreen(sp)->_clear = FALSE;
537             NewScreen(sp)->_clear = FALSE;
538             touchwin(NewScreen(sp));
539         }
540
541         for (y = 0; y < nonempty; y++) {
542             x0 = NewScreen(sp)->_line[y].firstchar;
543             if (x0 != _NOCHANGE) {
544 #if EXP_OPTIMIZE
545                 int x2;
546                 int limit = NewScreen(sp)->_line[y].lastchar;
547                 while ((x1 = EndChange(x0)) <= limit) {
548                     while ((x2 = NextChange(x1)) <= limit && x2 <= (x1 + 2)) {
549                         x1 = x2;
550                     }
551                     n = x1 - x0 + 1;
552                     memcpy(&CurScreen(sp)->_line[y].text[x0],
553                            &NewScreen(sp)->_line[y].text[x0],
554                            n * sizeof(CurScreen(sp)->_line[y].text[x0]));
555                     con_write(TCB,
556                               y,
557                               x0,
558                               &CurScreen(sp)->_line[y].text[x0], n);
559                     x0 = NextChange(x1);
560                 }
561
562                 /* mark line changed successfully */
563                 if (y <= NewScreen(sp)->_maxy) {
564                     MARK_NOCHANGE(NewScreen(sp), y);
565                 }
566                 if (y <= CurScreen(sp)->_maxy) {
567                     MARK_NOCHANGE(CurScreen(sp), y);
568                 }
569 #else
570                 x1 = NewScreen(sp)->_line[y].lastchar;
571                 n = x1 - x0 + 1;
572                 if (n > 0) {
573                     memcpy(&CurScreen(sp)->_line[y].text[x0],
574                            &NewScreen(sp)->_line[y].text[x0],
575                            (size_t) n * sizeof(CurScreen(sp)->_line[y].text[x0]));
576                     con_write(TCB,
577                               y,
578                               x0,
579                               &CurScreen(sp)->_line[y].text[x0], n);
580
581                     /* mark line changed successfully */
582                     if (y <= NewScreen(sp)->_maxy) {
583                         MARK_NOCHANGE(NewScreen(sp), y);
584                     }
585                     if (y <= CurScreen(sp)->_maxy) {
586                         MARK_NOCHANGE(CurScreen(sp), y);
587                     }
588                 }
589 #endif
590             }
591         }
592
593         /* put everything back in sync */
594         for (y = nonempty; y <= NewScreen(sp)->_maxy; y++) {
595             MARK_NOCHANGE(NewScreen(sp), y);
596         }
597         for (y = nonempty; y <= CurScreen(sp)->_maxy; y++) {
598             MARK_NOCHANGE(CurScreen(sp), y);
599         }
600
601         if (!NewScreen(sp)->_leaveok) {
602             CurScreen(sp)->_curx = NewScreen(sp)->_curx;
603             CurScreen(sp)->_cury = NewScreen(sp)->_cury;
604
605             TCB->drv->td_hwcur(TCB,
606                                0, 0,
607                                CurScreen(sp)->_cury, CurScreen(sp)->_curx);
608         }
609         selectActiveHandle(TCB);
610         result = OK;
611     }
612     returnCode(result);
613 }
614
615 static bool
616 wcon_CanHandle(TERMINAL_CONTROL_BLOCK * TCB,
617                const char *tname,
618                int *errret GCC_UNUSED)
619 {
620     bool code = FALSE;
621
622     T((T_CALLED("win32con::wcon_CanHandle(%p)"), TCB));
623
624     assert((TCB != 0) && (tname != 0));
625
626     TCB->magic = WINMAGIC;
627
628     if (tname == 0 || *tname == 0)
629         code = TRUE;
630     else if (tname != 0 && *tname == '#') {
631         /*
632          * Use "#" (a character which cannot begin a terminal's name) to
633          * select specific driver from the table.
634          *
635          * In principle, we could have more than one non-terminfo driver,
636          * e.g., "win32gui".
637          */
638         size_t n = strlen(tname + 1);
639         if (n != 0
640             && (strncmp(tname + 1, "win32console", n) == 0)) {
641             code = TRUE;
642         }
643     } else if (tname != 0 && stricmp(tname, "unknown") == 0) {
644         code = TRUE;
645     }
646
647     /*
648      * This is intentional, to avoid unnecessary breakage of applications
649      * using <term.h> symbols.
650      */
651     if (code && (TCB->term.type.Booleans == 0)) {
652         _nc_init_termtype(&(TCB->term.type));
653     }
654     returnBool(code);
655 }
656
657 static int
658 wcon_dobeepflash(TERMINAL_CONTROL_BLOCK * TCB,
659                  int beepFlag GCC_UNUSED)
660 {
661     SCREEN *sp;
662     int res = ERR;
663
664     AssertTCB();
665     SetSP();
666
667     return res;
668 }
669
670 static int
671 wcon_print(TERMINAL_CONTROL_BLOCK * TCB,
672            char *data GCC_UNUSED,
673            int len GCC_UNUSED)
674 {
675     SCREEN *sp;
676
677     AssertTCB();
678     SetSP();
679
680     return ERR;
681 }
682
683 static int
684 wcon_defaultcolors(TERMINAL_CONTROL_BLOCK * TCB,
685                    int fg GCC_UNUSED,
686                    int bg GCC_UNUSED)
687 {
688     SCREEN *sp;
689     int code = ERR;
690
691     AssertTCB();
692     SetSP();
693
694     return (code);
695 }
696
697 static bool
698 get_SBI(TERMINAL_CONTROL_BLOCK * TCB)
699 {
700     bool rc = FALSE;
701     Properties *p = PropOf(TCB);
702     if (GetConsoleScreenBufferInfo(TCB->hdl, &(p->SBI))) {
703         T(("GetConsoleScreenBufferInfo"));
704         T(("... buffer(X:%d Y:%d)",
705            p->SBI.dwSize.X,
706            p->SBI.dwSize.Y));
707         T(("... window(X:%d Y:%d)",
708            p->SBI.dwMaximumWindowSize.X,
709            p->SBI.dwMaximumWindowSize.Y));
710         T(("... cursor(X:%d Y:%d)",
711            p->SBI.dwCursorPosition.X,
712            p->SBI.dwCursorPosition.Y));
713         T(("... display(Top:%d Bottom:%d Left:%d Right:%d)",
714            p->SBI.srWindow.Top,
715            p->SBI.srWindow.Bottom,
716            p->SBI.srWindow.Left,
717            p->SBI.srWindow.Right));
718         if (p->buffered) {
719             p->origin.X = 0;
720             p->origin.Y = 0;
721         } else {
722             p->origin.X = p->SBI.srWindow.Left;
723             p->origin.Y = p->SBI.srWindow.Top;
724         }
725         rc = TRUE;
726     } else {
727         T(("GetConsoleScreenBufferInfo ERR"));
728     }
729     return rc;
730 }
731
732 static void
733 wcon_setcolor(TERMINAL_CONTROL_BLOCK * TCB,
734               int fore,
735               int color,
736               int (*outc) (SCREEN *, int) GCC_UNUSED)
737 {
738     AssertTCB();
739
740     if (okConsoleHandle(TCB) &&
741         PropOf(TCB) != 0) {
742         WORD a = MapColor(fore, color);
743         a |= (WORD) ((PropOf(TCB)->SBI.wAttributes) & (fore ? 0xfff8 : 0xff8f));
744         SetConsoleTextAttribute(TCB->hdl, a);
745         get_SBI(TCB);
746     }
747 }
748
749 static bool
750 wcon_rescol(TERMINAL_CONTROL_BLOCK * TCB)
751 {
752     bool res = FALSE;
753
754     AssertTCB();
755     if (okConsoleHandle(TCB)) {
756         WORD a = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
757         SetConsoleTextAttribute(TCB->hdl, a);
758         get_SBI(TCB);
759         res = TRUE;
760     }
761     return res;
762 }
763
764 static bool
765 wcon_rescolors(TERMINAL_CONTROL_BLOCK * TCB)
766 {
767     int result = FALSE;
768     SCREEN *sp;
769
770     AssertTCB();
771     SetSP();
772
773     return result;
774 }
775
776 static int
777 wcon_size(TERMINAL_CONTROL_BLOCK * TCB, int *Lines, int *Cols)
778 {
779     int result = ERR;
780
781     AssertTCB();
782
783     T((T_CALLED("win32con::wcon_size(%p)"), TCB));
784
785     if (okConsoleHandle(TCB) &&
786         PropOf(TCB) != 0 &&
787         Lines != NULL &&
788         Cols != NULL) {
789         if (PropOf(TCB)->buffered) {
790             *Lines = (int) (PropOf(TCB)->SBI.dwSize.Y);
791             *Cols = (int) (PropOf(TCB)->SBI.dwSize.X);
792         } else {
793             *Lines = (int) (PropOf(TCB)->SBI.srWindow.Bottom + 1 -
794                             PropOf(TCB)->SBI.srWindow.Top);
795             *Cols = (int) (PropOf(TCB)->SBI.srWindow.Right + 1 -
796                            PropOf(TCB)->SBI.srWindow.Left);
797         }
798         result = OK;
799     }
800     returnCode(result);
801 }
802
803 static int
804 wcon_setsize(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED,
805              int l GCC_UNUSED,
806              int c GCC_UNUSED)
807 {
808     AssertTCB();
809     return ERR;
810 }
811
812 static int
813 wcon_sgmode(TERMINAL_CONTROL_BLOCK * TCB, int setFlag, TTY * buf)
814 {
815     DWORD dwFlag = 0;
816     tcflag_t iflag;
817     tcflag_t lflag;
818
819     AssertTCB();
820
821     if (TCB == 0 || buf == NULL)
822         return ERR;
823
824     if (setFlag) {
825         iflag = buf->c_iflag;
826         lflag = buf->c_lflag;
827
828         GetConsoleMode(TCB->inp, &dwFlag);
829
830         if (lflag & ICANON)
831             dwFlag |= ENABLE_LINE_INPUT;
832         else
833             dwFlag &= (DWORD) (~ENABLE_LINE_INPUT);
834
835         if (lflag & ECHO)
836             dwFlag |= ENABLE_ECHO_INPUT;
837         else
838             dwFlag &= (DWORD) (~ENABLE_ECHO_INPUT);
839
840         if (iflag & BRKINT)
841             dwFlag |= ENABLE_PROCESSED_INPUT;
842         else
843             dwFlag &= (DWORD) (~ENABLE_PROCESSED_INPUT);
844
845         dwFlag |= ENABLE_MOUSE_INPUT;
846
847         buf->c_iflag = iflag;
848         buf->c_lflag = lflag;
849         SetConsoleMode(TCB->inp, dwFlag);
850         TCB->term.Nttyb = *buf;
851     } else {
852         iflag = TCB->term.Nttyb.c_iflag;
853         lflag = TCB->term.Nttyb.c_lflag;
854         GetConsoleMode(TCB->inp, &dwFlag);
855
856         if (dwFlag & ENABLE_LINE_INPUT)
857             lflag |= ICANON;
858         else
859             lflag &= (tcflag_t) (~ICANON);
860
861         if (dwFlag & ENABLE_ECHO_INPUT)
862             lflag |= ECHO;
863         else
864             lflag &= (tcflag_t) (~ECHO);
865
866         if (dwFlag & ENABLE_PROCESSED_INPUT)
867             iflag |= BRKINT;
868         else
869             iflag &= (tcflag_t) (~BRKINT);
870
871         TCB->term.Nttyb.c_iflag = iflag;
872         TCB->term.Nttyb.c_lflag = lflag;
873
874         *buf = TCB->term.Nttyb;
875     }
876     return OK;
877 }
878
879 static int
880 wcon_mode(TERMINAL_CONTROL_BLOCK * TCB, int progFlag, int defFlag)
881 {
882     SCREEN *sp;
883     TERMINAL *_term = (TERMINAL *) TCB;
884     int code = ERR;
885
886     AssertTCB();
887     sp = TCB->csp;
888
889     T((T_CALLED("win32con::wcon_mode(%p, prog=%d, def=%d)"), TCB, progFlag, defFlag));
890     PropOf(TCB)->progMode = progFlag;
891     PropOf(TCB)->lastOut = progFlag ? TCB->hdl : TCB->out;
892     SetConsoleActiveScreenBuffer(PropOf(TCB)->lastOut);
893
894     if (progFlag) /* prog mode */  {
895         if (defFlag) {
896             if ((wcon_sgmode(TCB, FALSE, &(_term->Nttyb)) == OK)) {
897                 _term->Nttyb.c_oflag &= (tcflag_t) (~OFLAGS_TABS);
898                 code = OK;
899             }
900         } else {
901             /* reset_prog_mode */
902             if (wcon_sgmode(TCB, TRUE, &(_term->Nttyb)) == OK) {
903                 if (sp) {
904                     if (sp->_keypad_on)
905                         _nc_keypad(sp, TRUE);
906                 }
907                 code = OK;
908             }
909         }
910     } else {                    /* shell mode */
911         if (defFlag) {
912             /* def_shell_mode */
913             if (wcon_sgmode(TCB, FALSE, &(_term->Ottyb)) == OK) {
914                 code = OK;
915             }
916         } else {
917             /* reset_shell_mode */
918             if (sp) {
919                 _nc_keypad(sp, FALSE);
920                 NCURSES_SP_NAME(_nc_flush) (sp);
921             }
922             code = wcon_sgmode(TCB, TRUE, &(_term->Ottyb));
923             if (!PropOf(TCB)->buffered) {
924                 if (!restore_original_screen(TCB))
925                     code = ERR;
926             }
927         }
928     }
929
930     return (code);
931 }
932
933 static void
934 wcon_screen_init(SCREEN *sp GCC_UNUSED)
935 {
936 }
937
938 static void
939 wcon_wrap(SCREEN *sp GCC_UNUSED)
940 {
941 }
942
943 static int
944 rkeycompare(const void *el1, const void *el2)
945 {
946     WORD key1 = (LOWORD((*((const LONG *) el1)))) & 0x7fff;
947     WORD key2 = (LOWORD((*((const LONG *) el2)))) & 0x7fff;
948
949     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
950 }
951
952 static int
953 keycompare(const void *el1, const void *el2)
954 {
955     WORD key1 = HIWORD((*((const LONG *) el1)));
956     WORD key2 = HIWORD((*((const LONG *) el2)));
957
958     return ((key1 < key2) ? -1 : ((key1 == key2) ? 0 : 1));
959 }
960
961 static int
962 MapKey(TERMINAL_CONTROL_BLOCK * TCB, WORD vKey)
963 {
964     WORD nKey = 0;
965     void *res;
966     LONG key = GenMap(vKey, 0);
967     int code = -1;
968
969     AssertTCB();
970
971     res = bsearch(&key,
972                   PropOf(TCB)->map,
973                   (size_t) (N_INI + FKEYS),
974                   sizeof(keylist[0]),
975                   keycompare);
976     if (res) {
977         key = *((LONG *) res);
978         nKey = LOWORD(key);
979         code = (int) (nKey & 0x7fff);
980         if (nKey & 0x8000)
981             code = -code;
982     }
983     return code;
984 }
985
986 static void
987 wcon_release(TERMINAL_CONTROL_BLOCK * TCB)
988 {
989     T((T_CALLED("win32con::wcon_release(%p)"), TCB));
990
991     AssertTCB();
992     if (TCB->prop)
993         free(TCB->prop);
994
995     returnVoid;
996 }
997
998 /*
999  * Attempt to save the screen contents.  PDCurses does this if
1000  * PDC_RESTORE_SCREEN is set, giving the same visual appearance on restoration
1001  * as if the library had allocated a console buffer.
1002  */
1003 static bool
1004 save_original_screen(TERMINAL_CONTROL_BLOCK * TCB)
1005 {
1006     bool result = FALSE;
1007     Properties *p = PropOf(TCB);
1008     COORD bufferSize;
1009     COORD bufferCoord;
1010     SMALL_RECT readRegion;
1011     size_t want;
1012
1013     bufferSize.X = p->SBI.dwSize.X;
1014     bufferSize.Y = p->SBI.dwSize.Y;
1015     want = (size_t) (bufferSize.X * bufferSize.Y);
1016
1017     if ((p->save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1018         bufferCoord.X = bufferCoord.Y = 0;
1019
1020         readRegion.Top = 0;
1021         readRegion.Left = 0;
1022         readRegion.Bottom = (SHORT) (bufferSize.Y - 1);
1023         readRegion.Right = (SHORT) (bufferSize.X - 1);
1024
1025         T(("... reading console buffer %dx%d into %d,%d - %d,%d at %d,%d",
1026            bufferSize.Y, bufferSize.X,
1027            readRegion.Top,
1028            readRegion.Left,
1029            readRegion.Bottom,
1030            readRegion.Right,
1031            bufferCoord.Y,
1032            bufferCoord.X));
1033
1034         if (ReadConsoleOutput(TCB->hdl,
1035                               p->save_screen,
1036                               bufferSize,
1037                               bufferCoord,
1038                               &readRegion)) {
1039             result = TRUE;
1040         } else {
1041             T((" error %#lx", (unsigned long) GetLastError()));
1042             FreeAndNull(p->save_screen);
1043
1044             bufferSize.X = (SHORT) (p->SBI.srWindow.Right
1045                                     - p->SBI.srWindow.Left + 1);
1046             bufferSize.Y = (SHORT) (p->SBI.srWindow.Bottom
1047                                     - p->SBI.srWindow.Top + 1);
1048             want = (size_t) (bufferSize.X * bufferSize.Y);
1049
1050             if ((p->save_screen = malloc(want * sizeof(CHAR_INFO))) != 0) {
1051                 bufferCoord.X = bufferCoord.Y = 0;
1052
1053                 readRegion.Top = p->SBI.srWindow.Top;
1054                 readRegion.Left = p->SBI.srWindow.Left;
1055                 readRegion.Bottom = p->SBI.srWindow.Bottom;
1056                 readRegion.Right = p->SBI.srWindow.Right;
1057
1058                 T(("... reading console window %dx%d into %d,%d - %d,%d at %d,%d",
1059                    bufferSize.Y, bufferSize.X,
1060                    readRegion.Top,
1061                    readRegion.Left,
1062                    readRegion.Bottom,
1063                    readRegion.Right,
1064                    bufferCoord.Y,
1065                    bufferCoord.X));
1066
1067                 if (ReadConsoleOutput(TCB->hdl,
1068                                       p->save_screen,
1069                                       bufferSize,
1070                                       bufferCoord,
1071                                       &readRegion)) {
1072                     result = TRUE;
1073                     p->window_only = TRUE;
1074                 } else {
1075                     T((" error %#lx", (unsigned long) GetLastError()));
1076                 }
1077             }
1078         }
1079     }
1080
1081     T(("... save original screen contents %s", result ? "ok" : "err"));
1082     return result;
1083 }
1084
1085 static void
1086 wcon_init(TERMINAL_CONTROL_BLOCK * TCB)
1087 {
1088     DWORD num_buttons;
1089
1090     T((T_CALLED("win32con::wcon_init(%p)"), TCB));
1091
1092     AssertTCB();
1093
1094     if (TCB) {
1095         BOOL b = AllocConsole();
1096         WORD a;
1097         int i;
1098         bool buffered = TRUE;
1099
1100         if (!b)
1101             b = AttachConsole(ATTACH_PARENT_PROCESS);
1102
1103         TCB->inp = GetStdHandle(STD_INPUT_HANDLE);
1104         TCB->out = GetStdHandle(STD_OUTPUT_HANDLE);
1105
1106         if (getenv("NCGDB") || getenv("NCURSES_CONSOLE2")) {
1107             TCB->hdl = TCB->out;
1108             buffered = FALSE;
1109         } else {
1110             TCB->hdl = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
1111                                                  0,
1112                                                  NULL,
1113                                                  CONSOLE_TEXTMODE_BUFFER,
1114                                                  NULL);
1115         }
1116
1117         if (InvalidConsoleHandle(TCB->hdl)) {
1118             returnVoid;
1119         } else if ((TCB->prop = typeCalloc(Properties, 1)) != 0) {
1120             PropOf(TCB)->buffered = buffered;
1121             PropOf(TCB)->window_only = FALSE;
1122             if (!get_SBI(TCB)) {
1123                 FreeAndNull(TCB->prop);         /* force error in wcon_size */
1124                 returnVoid;
1125             }
1126             if (!buffered) {
1127                 if (!save_original_screen(TCB)) {
1128                     FreeAndNull(TCB->prop);     /* force error in wcon_size */
1129                     returnVoid;
1130                 }
1131             }
1132         }
1133
1134         TCB->info.initcolor = TRUE;
1135         TCB->info.canchange = FALSE;
1136         TCB->info.hascolor = TRUE;
1137         TCB->info.caninit = TRUE;
1138
1139         TCB->info.maxpairs = NUMPAIRS;
1140         TCB->info.maxcolors = 8;
1141         TCB->info.numlabels = 0;
1142         TCB->info.labelwidth = 0;
1143         TCB->info.labelheight = 0;
1144         TCB->info.nocolorvideo = 1;
1145         TCB->info.tabsize = 8;
1146
1147         if (GetNumberOfConsoleMouseButtons(&num_buttons)) {
1148             T(("mouse has %ld buttons", num_buttons));
1149             TCB->info.numbuttons = (int) num_buttons;
1150         } else {
1151             TCB->info.numbuttons = 1;
1152         }
1153
1154         TCB->info.defaultPalette = _nc_cga_palette;
1155
1156         for (i = 0; i < (N_INI + FKEYS); i++) {
1157             if (i < N_INI)
1158                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] = (DWORD) keylist[i];
1159             else
1160                 PropOf(TCB)->rmap[i] = PropOf(TCB)->map[i] =
1161                     (DWORD) GenMap((VK_F1 + (i - N_INI)), (KEY_F(1) + (i - N_INI)));
1162         }
1163         qsort(PropOf(TCB)->map,
1164               (size_t) (MAPSIZE),
1165               sizeof(keylist[0]),
1166               keycompare);
1167         qsort(PropOf(TCB)->rmap,
1168               (size_t) (MAPSIZE),
1169               sizeof(keylist[0]),
1170               rkeycompare);
1171
1172         a = MapColor(true, COLOR_WHITE) | MapColor(false, COLOR_BLACK);
1173         for (i = 0; i < NUMPAIRS; i++)
1174             PropOf(TCB)->pairs[i] = a;
1175     }
1176     returnVoid;
1177 }
1178
1179 static void
1180 wcon_initpair(TERMINAL_CONTROL_BLOCK * TCB,
1181               int pair,
1182               int f,
1183               int b)
1184 {
1185     SCREEN *sp;
1186
1187     AssertTCB();
1188     SetSP();
1189
1190     if ((pair > 0) && (pair < NUMPAIRS) && (f >= 0) && (f < 8)
1191         && (b >= 0) && (b < 8)) {
1192         PropOf(TCB)->pairs[pair] = MapColor(true, f) | MapColor(false, b);
1193     }
1194 }
1195
1196 static void
1197 wcon_initcolor(TERMINAL_CONTROL_BLOCK * TCB,
1198                int color GCC_UNUSED,
1199                int r GCC_UNUSED,
1200                int g GCC_UNUSED,
1201                int b GCC_UNUSED)
1202 {
1203     SCREEN *sp;
1204
1205     AssertTCB();
1206     SetSP();
1207 }
1208
1209 static void
1210 wcon_do_color(TERMINAL_CONTROL_BLOCK * TCB,
1211               int old_pair GCC_UNUSED,
1212               int pair GCC_UNUSED,
1213               int reverse GCC_UNUSED,
1214               int (*outc) (SCREEN *, int) GCC_UNUSED
1215 )
1216 {
1217     SCREEN *sp;
1218
1219     AssertTCB();
1220     SetSP();
1221 }
1222
1223 static void
1224 wcon_initmouse(TERMINAL_CONTROL_BLOCK * TCB)
1225 {
1226     SCREEN *sp;
1227
1228     AssertTCB();
1229     SetSP();
1230
1231     sp->_mouse_type = M_TERM_DRIVER;
1232 }
1233
1234 static int
1235 wcon_testmouse(TERMINAL_CONTROL_BLOCK * TCB, int delay)
1236 {
1237     int rc = 0;
1238     SCREEN *sp;
1239
1240     AssertTCB();
1241     SetSP();
1242
1243     if (sp->_drv_mouse_head < sp->_drv_mouse_tail) {
1244         rc = TW_MOUSE;
1245     } else {
1246         rc = TCBOf(sp)->drv->td_twait(TCBOf(sp),
1247                                       TWAIT_MASK,
1248                                       delay,
1249                                       (int *) 0
1250                                       EVENTLIST_2nd(evl));
1251     }
1252
1253     return rc;
1254 }
1255
1256 static int
1257 wcon_mvcur(TERMINAL_CONTROL_BLOCK * TCB,
1258            int yold GCC_UNUSED, int xold GCC_UNUSED,
1259            int y, int x)
1260 {
1261     int ret = ERR;
1262     if (okConsoleHandle(TCB)) {
1263         Properties *p = PropOf(TCB);
1264         COORD loc;
1265         loc.X = (short) x;
1266         loc.Y = (short) (y + AdjustY(p));
1267         SetConsoleCursorPosition(TCB->hdl, loc);
1268         ret = OK;
1269     }
1270     return ret;
1271 }
1272
1273 static void
1274 wcon_hwlabel(TERMINAL_CONTROL_BLOCK * TCB,
1275              int labnum GCC_UNUSED,
1276              char *text GCC_UNUSED)
1277 {
1278     SCREEN *sp;
1279
1280     AssertTCB();
1281     SetSP();
1282 }
1283
1284 static void
1285 wcon_hwlabelOnOff(TERMINAL_CONTROL_BLOCK * TCB,
1286                   int OnFlag GCC_UNUSED)
1287 {
1288     SCREEN *sp;
1289
1290     AssertTCB();
1291     SetSP();
1292 }
1293
1294 static chtype
1295 wcon_conattr(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED)
1296 {
1297     chtype res = A_NORMAL;
1298     res |= (A_BOLD | A_DIM | A_REVERSE | A_STANDOUT | A_COLOR);
1299     return res;
1300 }
1301
1302 static void
1303 wcon_setfilter(TERMINAL_CONTROL_BLOCK * TCB)
1304 {
1305     SCREEN *sp;
1306
1307     AssertTCB();
1308     SetSP();
1309 }
1310
1311 static void
1312 wcon_initacs(TERMINAL_CONTROL_BLOCK * TCB,
1313              chtype *real_map GCC_UNUSED,
1314              chtype *fake_map GCC_UNUSED)
1315 {
1316 #define DATA(a,b) { a, b }
1317     static struct {
1318         int acs_code;
1319         int use_code;
1320     } table[] = {
1321         DATA('a', 0xb1),        /* ACS_CKBOARD  */
1322             DATA('f', 0xf8),    /* ACS_DEGREE   */
1323             DATA('g', 0xf1),    /* ACS_PLMINUS  */
1324             DATA('j', 0xd9),    /* ACS_LRCORNER */
1325             DATA('l', 0xda),    /* ACS_ULCORNER */
1326             DATA('k', 0xbf),    /* ACS_URCORNER */
1327             DATA('m', 0xc0),    /* ACS_LLCORNER */
1328             DATA('n', 0xc5),    /* ACS_PLUS     */
1329             DATA('q', 0xc4),    /* ACS_HLINE    */
1330             DATA('t', 0xc3),    /* ACS_LTEE     */
1331             DATA('u', 0xb4),    /* ACS_RTEE     */
1332             DATA('v', 0xc1),    /* ACS_BTEE     */
1333             DATA('w', 0xc2),    /* ACS_TTEE     */
1334             DATA('x', 0xb3),    /* ACS_VLINE    */
1335             DATA('y', 0xf3),    /* ACS_LEQUAL   */
1336             DATA('z', 0xf2),    /* ACS_GEQUAL   */
1337             DATA('0', 0xdb),    /* ACS_BLOCK    */
1338             DATA('{', 0xe3),    /* ACS_PI       */
1339             DATA('}', 0x9c),    /* ACS_STERLING */
1340             DATA(',', 0xae),    /* ACS_LARROW   */
1341             DATA('+', 0xaf),    /* ACS_RARROW   */
1342             DATA('~', 0xf9),    /* ACS_BULLET   */
1343     };
1344 #undef DATA
1345     unsigned n;
1346
1347     SCREEN *sp;
1348     AssertTCB();
1349     SetSP();
1350
1351     for (n = 0; n < SIZEOF(table); ++n) {
1352         real_map[table[n].acs_code] = (chtype) table[n].use_code | A_ALTCHARSET;
1353         if (sp != 0)
1354             sp->_screen_acs_map[table[n].acs_code] = TRUE;
1355     }
1356 }
1357
1358 static ULONGLONG
1359 tdiff(FILETIME fstart, FILETIME fend)
1360 {
1361     ULARGE_INTEGER ustart;
1362     ULARGE_INTEGER uend;
1363     ULONGLONG diff;
1364
1365     ustart.LowPart = fstart.dwLowDateTime;
1366     ustart.HighPart = fstart.dwHighDateTime;
1367     uend.LowPart = fend.dwLowDateTime;
1368     uend.HighPart = fend.dwHighDateTime;
1369
1370     diff = (uend.QuadPart - ustart.QuadPart) / 10000;
1371     return diff;
1372 }
1373
1374 static int
1375 Adjust(int milliseconds, int diff)
1376 {
1377     if (milliseconds == INFINITY)
1378         return milliseconds;
1379     milliseconds -= diff;
1380     if (milliseconds < 0)
1381         milliseconds = 0;
1382     return milliseconds;
1383 }
1384
1385 #define BUTTON_MASK (FROM_LEFT_1ST_BUTTON_PRESSED | \
1386                      FROM_LEFT_2ND_BUTTON_PRESSED | \
1387                      FROM_LEFT_3RD_BUTTON_PRESSED | \
1388                      FROM_LEFT_4TH_BUTTON_PRESSED | \
1389                      RIGHTMOST_BUTTON_PRESSED)
1390
1391 static int
1392 decode_mouse(TERMINAL_CONTROL_BLOCK * TCB, int mask)
1393 {
1394     SCREEN *sp;
1395     int result = 0;
1396
1397     AssertTCB();
1398     SetSP();
1399
1400     if (mask & FROM_LEFT_1ST_BUTTON_PRESSED)
1401         result |= BUTTON1_PRESSED;
1402     if (mask & FROM_LEFT_2ND_BUTTON_PRESSED)
1403         result |= BUTTON2_PRESSED;
1404     if (mask & FROM_LEFT_3RD_BUTTON_PRESSED)
1405         result |= BUTTON3_PRESSED;
1406     if (mask & FROM_LEFT_4TH_BUTTON_PRESSED)
1407         result |= BUTTON4_PRESSED;
1408
1409     if (mask & RIGHTMOST_BUTTON_PRESSED) {
1410         switch (TCB->info.numbuttons) {
1411         case 1:
1412             result |= BUTTON1_PRESSED;
1413             break;
1414         case 2:
1415             result |= BUTTON2_PRESSED;
1416             break;
1417         case 3:
1418             result |= BUTTON3_PRESSED;
1419             break;
1420         case 4:
1421             result |= BUTTON4_PRESSED;
1422             break;
1423         }
1424     }
1425
1426     return result;
1427 }
1428
1429 static int
1430 wcon_twait(TERMINAL_CONTROL_BLOCK * TCB,
1431            int mode,
1432            int milliseconds,
1433            int *timeleft
1434            EVENTLIST_2nd(_nc_eventlist * evl))
1435 {
1436     SCREEN *sp;
1437     INPUT_RECORD inp_rec;
1438     BOOL b;
1439     DWORD nRead = 0, rc = (DWORD) (-1);
1440     int code = 0;
1441     FILETIME fstart;
1442     FILETIME fend;
1443     int diff;
1444     bool isImmed = (milliseconds == 0);
1445
1446 #define CONSUME() ReadConsoleInput(TCB->inp,&inp_rec,1,&nRead)
1447
1448     AssertTCB();
1449     SetSP();
1450
1451     TR(TRACE_IEVENT, ("start twait: %d milliseconds, mode: %d",
1452                       milliseconds, mode));
1453
1454     if (milliseconds < 0)
1455         milliseconds = INFINITY;
1456
1457     memset(&inp_rec, 0, sizeof(inp_rec));
1458
1459     while (true) {
1460         GetSystemTimeAsFileTime(&fstart);
1461         rc = WaitForSingleObject(TCB->inp, (DWORD) milliseconds);
1462         GetSystemTimeAsFileTime(&fend);
1463         diff = (int) tdiff(fstart, fend);
1464         milliseconds = Adjust(milliseconds, diff);
1465
1466         if (!isImmed && milliseconds == 0)
1467             break;
1468
1469         if (rc == WAIT_OBJECT_0) {
1470             if (mode) {
1471                 b = GetNumberOfConsoleInputEvents(TCB->inp, &nRead);
1472                 if (b && nRead > 0) {
1473                     b = PeekConsoleInput(TCB->inp, &inp_rec, 1, &nRead);
1474                     if (b && nRead > 0) {
1475                         switch (inp_rec.EventType) {
1476                         case KEY_EVENT:
1477                             if (mode & TW_INPUT) {
1478                                 WORD vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1479                                 char ch = inp_rec.Event.KeyEvent.uChar.AsciiChar;
1480
1481                                 if (inp_rec.Event.KeyEvent.bKeyDown) {
1482                                     if (0 == ch) {
1483                                         int nKey = MapKey(TCB, vk);
1484                                         if ((nKey < 0) || FALSE == sp->_keypad_on) {
1485                                             CONSUME();
1486                                             continue;
1487                                         }
1488                                     }
1489                                     code = TW_INPUT;
1490                                     goto end;
1491                                 } else {
1492                                     CONSUME();
1493                                 }
1494                             }
1495                             continue;
1496                         case MOUSE_EVENT:
1497                             if (decode_mouse(TCB,
1498                                              (inp_rec.Event.MouseEvent.dwButtonState
1499                                               & BUTTON_MASK)) == 0) {
1500                                 CONSUME();
1501                             } else if (mode & TW_MOUSE) {
1502                                 code = TW_MOUSE;
1503                                 goto end;
1504                             }
1505                             continue;
1506                         default:
1507                             selectActiveHandle(TCB);
1508                             continue;
1509                         }
1510                     }
1511                 }
1512             }
1513             continue;
1514         } else {
1515             if (rc != WAIT_TIMEOUT) {
1516                 code = -1;
1517                 break;
1518             } else {
1519                 code = 0;
1520                 break;
1521             }
1522         }
1523     }
1524   end:
1525
1526     TR(TRACE_IEVENT, ("end twait: returned %d (%d), remaining time %d msec",
1527                       code, errno, milliseconds));
1528
1529     if (timeleft)
1530         *timeleft = milliseconds;
1531
1532     return code;
1533 }
1534
1535 static bool
1536 handle_mouse(TERMINAL_CONTROL_BLOCK * TCB, MOUSE_EVENT_RECORD mer)
1537 {
1538     SCREEN *sp;
1539     MEVENT work;
1540     bool result = FALSE;
1541
1542     AssertTCB();
1543     SetSP();
1544
1545     sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
1546     sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
1547
1548     /*
1549      * We're only interested if the button is pressed or released.
1550      * FIXME: implement continuous event-tracking.
1551      */
1552     if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
1553         Properties *p = PropOf(TCB);
1554
1555         memset(&work, 0, sizeof(work));
1556
1557         if (sp->_drv_mouse_new_buttons) {
1558
1559             work.bstate |= (mmask_t) decode_mouse(TCB, sp->_drv_mouse_new_buttons);
1560
1561         } else {
1562
1563             /* cf: BUTTON_PRESSED, BUTTON_RELEASED */
1564             work.bstate |= (mmask_t) (decode_mouse(TCB,
1565                                                    sp->_drv_mouse_old_buttons)
1566                                       >> 1);
1567
1568             result = TRUE;
1569         }
1570
1571         work.x = mer.dwMousePosition.X;
1572         work.y = mer.dwMousePosition.Y - AdjustY(p);
1573
1574         sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
1575         sp->_drv_mouse_tail += 1;
1576     }
1577
1578     return result;
1579 }
1580
1581 static int
1582 wcon_read(TERMINAL_CONTROL_BLOCK * TCB, int *buf)
1583 {
1584     SCREEN *sp;
1585     int n = 1;
1586     INPUT_RECORD inp_rec;
1587     BOOL b;
1588     DWORD nRead;
1589     WORD vk;
1590
1591     AssertTCB();
1592     assert(buf);
1593     SetSP();
1594
1595     memset(&inp_rec, 0, sizeof(inp_rec));
1596
1597     T((T_CALLED("win32con::wcon_read(%p)"), TCB));
1598     while ((b = ReadConsoleInput(TCB->inp, &inp_rec, 1, &nRead))) {
1599         if (b && nRead > 0) {
1600             if (inp_rec.EventType == KEY_EVENT) {
1601                 if (!inp_rec.Event.KeyEvent.bKeyDown)
1602                     continue;
1603                 *buf = (int) inp_rec.Event.KeyEvent.uChar.AsciiChar;
1604                 vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
1605                 if (*buf == 0) {
1606                     if (sp->_keypad_on) {
1607                         *buf = MapKey(TCB, vk);
1608                         if (0 > (*buf))
1609                             continue;
1610                         else
1611                             break;
1612                     } else
1613                         continue;
1614                 } else {        /* *buf != 0 */
1615                     break;
1616                 }
1617             } else if (inp_rec.EventType == MOUSE_EVENT) {
1618                 if (handle_mouse(TCB, inp_rec.Event.MouseEvent)) {
1619                     *buf = KEY_MOUSE;
1620                     break;
1621                 }
1622             }
1623             continue;
1624         }
1625     }
1626     returnCode(n);
1627 }
1628
1629 static int
1630 wcon_nap(TERMINAL_CONTROL_BLOCK * TCB GCC_UNUSED, int ms)
1631 {
1632     T((T_CALLED("win32con::wcon_nap(%p, %d)"), TCB, ms));
1633     Sleep((DWORD) ms);
1634     returnCode(OK);
1635 }
1636
1637 static bool
1638 wcon_kyExist(TERMINAL_CONTROL_BLOCK * TCB, int keycode)
1639 {
1640     SCREEN *sp;
1641     WORD nKey;
1642     void *res;
1643     bool found = FALSE;
1644     LONG key = GenMap(0, (WORD) keycode);
1645
1646     AssertTCB();
1647     SetSP();
1648
1649     AssertTCB();
1650
1651     T((T_CALLED("win32con::wcon_kyExist(%p, %d)"), TCB, keycode));
1652     res = bsearch(&key,
1653                   PropOf(TCB)->rmap,
1654                   (size_t) (N_INI + FKEYS),
1655                   sizeof(keylist[0]),
1656                   rkeycompare);
1657     if (res) {
1658         key = *((LONG *) res);
1659         nKey = LOWORD(key);
1660         if (!(nKey & 0x8000))
1661             found = TRUE;
1662     }
1663     returnCode(found);
1664 }
1665
1666 static int
1667 wcon_kpad(TERMINAL_CONTROL_BLOCK * TCB, int flag GCC_UNUSED)
1668 {
1669     SCREEN *sp;
1670     int code = ERR;
1671
1672     AssertTCB();
1673     sp = TCB->csp;
1674
1675     T((T_CALLED("win32con::wcon_kpad(%p, %d)"), TCB, flag));
1676     if (sp) {
1677         code = OK;
1678     }
1679     returnCode(code);
1680 }
1681
1682 static int
1683 wcon_keyok(TERMINAL_CONTROL_BLOCK * TCB, int keycode, int flag)
1684 {
1685     int code = ERR;
1686     SCREEN *sp;
1687     WORD nKey;
1688     WORD vKey;
1689     void *res;
1690     LONG key = GenMap(0, (WORD) keycode);
1691
1692     AssertTCB();
1693     SetSP();
1694
1695     T((T_CALLED("win32con::wcon_keyok(%p, %d, %d)"), TCB, keycode, flag));
1696     if (sp) {
1697         res = bsearch(&key,
1698                       PropOf(TCB)->rmap,
1699                       (size_t) (N_INI + FKEYS),
1700                       sizeof(keylist[0]),
1701                       rkeycompare);
1702         if (res) {
1703             key = *((LONG *) res);
1704             vKey = HIWORD(key);
1705             nKey = (LOWORD(key)) & 0x7fff;
1706             if (!flag)
1707                 nKey |= 0x8000;
1708             *(LONG *) res = GenMap(vKey, nKey);
1709         }
1710     }
1711     returnCode(code);
1712 }
1713
1714 NCURSES_EXPORT_VAR (TERM_DRIVER) _nc_WIN_DRIVER = {
1715     FALSE,
1716         wcon_name,              /* Name */
1717         wcon_CanHandle,         /* CanHandle */
1718         wcon_init,              /* init */
1719         wcon_release,           /* release */
1720         wcon_size,              /* size */
1721         wcon_sgmode,            /* sgmode */
1722         wcon_conattr,           /* conattr */
1723         wcon_mvcur,             /* hwcur */
1724         wcon_mode,              /* mode */
1725         wcon_rescol,            /* rescol */
1726         wcon_rescolors,         /* rescolors */
1727         wcon_setcolor,          /* color */
1728         wcon_dobeepflash,       /* DoBeepFlash */
1729         wcon_initpair,          /* initpair */
1730         wcon_initcolor,         /* initcolor */
1731         wcon_do_color,          /* docolor */
1732         wcon_initmouse,         /* initmouse */
1733         wcon_testmouse,         /* testmouse */
1734         wcon_setfilter,         /* setfilter */
1735         wcon_hwlabel,           /* hwlabel */
1736         wcon_hwlabelOnOff,      /* hwlabelOnOff */
1737         wcon_doupdate,          /* update */
1738         wcon_defaultcolors,     /* defaultcolors */
1739         wcon_print,             /* print */
1740         wcon_size,              /* getsize */
1741         wcon_setsize,           /* setsize */
1742         wcon_initacs,           /* initacs */
1743         wcon_screen_init,       /* scinit */
1744         wcon_wrap,              /* scexit */
1745         wcon_twait,             /* twait */
1746         wcon_read,              /* read */
1747         wcon_nap,               /* nap */
1748         wcon_kpad,              /* kpad */
1749         wcon_keyok,             /* kyOk */
1750         wcon_kyExist            /* kyExist */
1751 };