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