1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <tui/extern.h>
28 #include <tui/tui.h>
29 #include "../tui-priv.h"
30 #include <klib/rc.h>
31 #include <sysalloc.h>
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <windows.h>
36 #include <assert.h>
37 
38 
39 /**********************************************************************************/
40 
tui_color_to_win_fg(KTUI_color c)41 static WORD tui_color_to_win_fg( KTUI_color c)
42 {
43     switch ( c )
44     {
45         case KTUI_c_light_gray      : return FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED;
46         case KTUI_c_gray            : return FOREGROUND_INTENSITY;
47         case KTUI_c_white           : return FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_INTENSITY;
48 
49         case KTUI_c_dark_red        : return FOREGROUND_RED;
50         case KTUI_c_red             : return FOREGROUND_RED|FOREGROUND_INTENSITY;
51 
52         case KTUI_c_dark_green      : return FOREGROUND_GREEN;
53         case KTUI_c_green           : return FOREGROUND_GREEN|FOREGROUND_INTENSITY;
54 
55         case KTUI_c_brown           : return FOREGROUND_RED|FOREGROUND_GREEN;
56         case KTUI_c_yellow          : return FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY;
57 
58         case KTUI_c_dark_blue       : return FOREGROUND_BLUE;
59         case KTUI_c_blue            : return FOREGROUND_BLUE|FOREGROUND_INTENSITY;
60 
61         case KTUI_c_dark_magenta    : return FOREGROUND_RED|FOREGROUND_BLUE;
62         case KTUI_c_magenta         : return FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
63 
64         case KTUI_c_dark_cyan       : return FOREGROUND_GREEN|FOREGROUND_BLUE;
65         case KTUI_c_cyan            : return FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY;
66 
67 
68         case KTUI_c_black           :
69         default                     : return 0;
70     }
71 }
72 
73 
tui_color_to_win_bg(KTUI_color c)74 static WORD tui_color_to_win_bg( KTUI_color c )
75 {
76     switch ( c )
77     {
78         case KTUI_c_light_gray      : return BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED;
79         case KTUI_c_gray            : return BACKGROUND_INTENSITY;
80         case KTUI_c_white           : return BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED|BACKGROUND_INTENSITY;
81 
82         case KTUI_c_dark_red        : return BACKGROUND_RED;
83         case KTUI_c_red             : return BACKGROUND_RED|BACKGROUND_INTENSITY;
84 
85         case KTUI_c_dark_green      : return BACKGROUND_GREEN;
86         case KTUI_c_green           : return BACKGROUND_GREEN|BACKGROUND_INTENSITY;
87 
88         case KTUI_c_brown           : return BACKGROUND_RED|BACKGROUND_GREEN;
89         case KTUI_c_yellow          : return BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_INTENSITY;
90 
91         case KTUI_c_dark_blue       : return BACKGROUND_BLUE;
92         case KTUI_c_blue            : return BACKGROUND_BLUE|BACKGROUND_INTENSITY;
93 
94         case KTUI_c_dark_magenta    : return BACKGROUND_RED|BACKGROUND_BLUE;
95         case KTUI_c_magenta         : return BACKGROUND_RED|BACKGROUND_BLUE|BACKGROUND_INTENSITY;
96 
97         case KTUI_c_dark_cyan       : return BACKGROUND_GREEN|BACKGROUND_BLUE;
98         case KTUI_c_cyan            : return BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY;
99 
100 
101         case KTUI_c_black           :
102         default                     : return 0;
103     }
104 }
105 
106 
set_tui_attrib_and_colors(HANDLE h,tui_ac * curr,KTUI_attrib attr,KTUI_color color_fg,KTUI_color color_bg)107 static void set_tui_attrib_and_colors( HANDLE h, tui_ac * curr,
108                                        KTUI_attrib attr,
109                                        KTUI_color color_fg,
110                                        KTUI_color color_bg )
111 {
112     if ( curr->attr != attr || curr->fg != color_fg || curr->bg != color_bg )
113     {
114         bool reverse = ( attr & KTUI_a_underline ) || ( attr & KTUI_a_inverse );
115 
116         if ( reverse )
117             SetConsoleTextAttribute( h,  tui_color_to_win_fg( color_bg ) |
118                                          tui_color_to_win_bg( color_fg ) );
119         else
120             SetConsoleTextAttribute( h,  tui_color_to_win_fg( color_fg ) |
121                                          tui_color_to_win_bg( color_bg ) );
122 
123         curr->attr = attr;
124         curr->fg = color_fg;
125         curr->bg = color_bg;
126     }
127 }
128 
129 
tui_send_strip(int x,int y,int count,tui_ac * curr,tui_ac * v,const char * s)130 void CC tui_send_strip( int x, int y, int count, tui_ac * curr, tui_ac * v,
131                         const char * s )
132 {
133     HANDLE h = GetStdHandle( STD_OUTPUT_HANDLE );
134     if ( h != INVALID_HANDLE_VALUE )
135     {
136         DWORD nBytesWritten;
137         COORD curpos = { (SHORT)x, (SHORT)y };
138 
139         set_tui_attrib_and_colors( h, curr, v->attr, v->fg, v->bg );
140         SetConsoleCursorPosition( h, curpos );
141         WriteConsoleA( h, s, ( DWORD )count, &nBytesWritten, NULL );
142     }
143 }
144 
145 
146 /**********************************************************************************/
147 
148 
149 typedef struct KTUI_pf
150 {
151     CONSOLE_SCREEN_BUFFER_INFO  sbinfo;
152     CONSOLE_CURSOR_INFO         curinfo;
153     DWORD                       bitsConsoleInputMode;
154     int                         esc_count;
155 
156 	int							prev_x, prev_y;
157     KTUI_mouse_button			prev_button;
158 	KTUI_mouse_action			prev_action;
159 } KTUI_pf;
160 
161 
store_mouse_event(struct KTUI_pf * pf,int x,int y,KTUI_mouse_button b,KTUI_mouse_action a)162 static void store_mouse_event( struct KTUI_pf * pf, int x, int y, KTUI_mouse_button b, KTUI_mouse_action a )
163 {
164 	pf -> prev_x = x;
165 	pf -> prev_y = y;
166 	pf -> prev_button = b;
167 	pf -> prev_action = a;
168 }
169 
different_mouse_event(struct KTUI_pf * pf,int x,int y,KTUI_mouse_button b,KTUI_mouse_action a)170 static bool different_mouse_event( struct KTUI_pf * pf, int x, int y, KTUI_mouse_button b, KTUI_mouse_action a )
171 {
172 	return ( pf -> prev_x != x ||
173 			 pf -> prev_y != y ||
174 			 pf -> prev_button != b ||
175 			 pf -> prev_action != a );
176 }
177 
178 
179 static KTUI_key g_VirtualCodeTable[ 256 ]; /* if max VK_* constant value is greater than 0xFF, then this code must be revised */
180 
181 
InitTableRange(int * table,size_t offset_first,size_t offset_last,int val)182 static void InitTableRange( int* table, size_t offset_first, size_t offset_last, int val )
183 {
184     size_t i;
185     assert( offset_first < 256 );
186     assert( offset_last < 256 );
187 
188     for ( i = offset_first; i <= offset_last; ++i )
189         table[ i ] = val;
190 }
191 
192 
InitVKTable(KTUI_key * table,size_t size)193 static void InitVKTable( KTUI_key* table, size_t size )
194 {
195     /*
196         the table is supposed to be used in a "white-list approach",
197         so it initializes every code that is supposed to be processed
198     */
199     size_t i;
200     for ( i = 0; i < size; ++i )
201         table[ i ] = ktui_none;
202 
203     /* special key virtual codes */
204     table[VK_DOWN]   = ktui_down;
205     table[VK_UP]     = ktui_up;
206     table[VK_LEFT]   = ktui_left;
207     table[VK_RIGHT]  = ktui_right;
208     table[VK_HOME]   = ktui_home;
209     table[VK_END]    = ktui_end;
210     table[VK_BACK]   = ktui_bksp;
211     table[VK_F1]     = ktui_F1;
212     table[VK_F2]     = ktui_F2;
213     table[VK_F3]     = ktui_F3;
214     table[VK_F4]     = ktui_F4;
215     table[VK_F5]     = ktui_F5;
216     table[VK_F6]     = ktui_F6;
217     table[VK_F7]     = ktui_F7;
218     table[VK_F8]     = ktui_F8;
219     table[VK_F9]     = ktui_F9;
220     table[VK_F10]    = ktui_F10;
221     table[VK_F11]    = ktui_F11;
222     table[VK_F12]    = ktui_F12;
223     table[VK_DELETE] = ktui_del;
224     table[VK_INSERT] = ktui_ins;
225     table[VK_NEXT]   = ktui_pgdn;
226     table[VK_PRIOR]  = ktui_pgup;
227     table[VK_RETURN] = ktui_enter;
228     table[VK_TAB]    = ktui_tab;
229     /*
230         for the numpad windows reports the same ascii characters
231         as for standart number keys, so treat them as ktui_alpha
232     */
233     table[VK_NUMPAD0]  = ktui_alpha;
234     table[VK_NUMPAD1]  = ktui_alpha;
235     table[VK_NUMPAD2]  = ktui_alpha;
236     table[VK_NUMPAD3]  = ktui_alpha;
237     table[VK_NUMPAD4]  = ktui_alpha;
238     table[VK_NUMPAD5]  = ktui_alpha;
239     table[VK_NUMPAD6]  = ktui_alpha;
240     table[VK_NUMPAD7]  = ktui_alpha;
241     table[VK_NUMPAD8]  = ktui_alpha;
242     table[VK_NUMPAD9]  = ktui_alpha;
243     table[VK_MULTIPLY] = ktui_alpha;
244     table[VK_ADD]      = ktui_alpha;
245     table[VK_SUBTRACT] = ktui_alpha;
246     table[VK_DECIMAL]  = ktui_alpha;
247     table[VK_DIVIDE]   = ktui_alpha;
248 
249     /* some other keys translated to ASCII */
250     table[VK_SPACE]      = ktui_alpha;
251     table[VK_OEM_1]      = ktui_alpha;
252     table[VK_OEM_PLUS]   = ktui_alpha;
253     table[VK_OEM_COMMA]  = ktui_alpha;
254     table[VK_OEM_MINUS]  = ktui_alpha;
255     table[VK_OEM_PERIOD] = ktui_alpha;
256     table[VK_OEM_2]      = ktui_alpha;
257     table[VK_OEM_3]      = ktui_alpha;
258     table[VK_OEM_4]      = ktui_alpha;
259     table[VK_OEM_5]      = ktui_alpha;
260     table[VK_OEM_6]      = ktui_alpha;
261     table[VK_OEM_7]      = ktui_alpha;
262     table[VK_OEM_8]      = ktui_alpha;
263     table[VK_OEM_102]    = ktui_alpha;
264     table[VK_OEM_CLEAR]  = ktui_alpha;
265     table[VK_ESCAPE]     = ktui_alpha;
266 
267     /* keys */
268     InitTableRange( table, 0x30, 0x39, ktui_alpha );
269     InitTableRange( table, 0x41, 0x5A, ktui_alpha );
270 }
271 
272 
TranslateVKtoKTUI(WORD wVirtualKeyCode)273 static KTUI_key TranslateVKtoKTUI( WORD wVirtualKeyCode )
274 {
275 	KTUI_key res;
276     if ( wVirtualKeyCode < _countof( g_VirtualCodeTable ) )
277         res = g_VirtualCodeTable[ wVirtualKeyCode ];
278     else
279         res = ktui_none;
280 	return res;
281 }
282 
283 
save_current_console_settings(HANDLE hStdOut,HANDLE hStdIn,KTUI_pf * pWinSettings,KTUI * pCommonSettings)284 static void save_current_console_settings( HANDLE hStdOut, HANDLE hStdIn, KTUI_pf * pWinSettings, KTUI * pCommonSettings )
285 {
286     GetConsoleScreenBufferInfo( hStdOut, &pWinSettings->sbinfo );
287     GetConsoleCursorInfo( hStdOut, &pWinSettings->curinfo );
288     GetConsoleMode( hStdIn, &pWinSettings->bitsConsoleInputMode );
289 
290     pCommonSettings->lines = ( pWinSettings->sbinfo.srWindow.Bottom - pWinSettings->sbinfo.srWindow.Top );
291     pCommonSettings->cols  = ( pWinSettings->sbinfo.srWindow.Right - pWinSettings->sbinfo.srWindow.Left );
292 }
293 
294 
set_tui_settings(HANDLE hStdOut,HANDLE hStdIn,KTUI_pf const * pWinSettings)295 static void set_tui_settings( HANDLE hStdOut, HANDLE hStdIn, KTUI_pf const * pWinSettings )
296 {
297     DWORD bitsMode = pWinSettings->bitsConsoleInputMode; /* use mostly default windows settings */
298     CONSOLE_CURSOR_INFO curinfo = { 1, FALSE };
299 
300     bitsMode &= ~ENABLE_ECHO_INPUT;      /* disable echo */
301     bitsMode &= ~ENABLE_LINE_INPUT;      /* something like raw mode? TODO: ask Wolfgang */
302     bitsMode &= ~ENABLE_PROCESSED_INPUT; /* capture Ctrl-C by application rather than system, shold be reset along with ENABLE_LINE_INPUT */
303 
304     bitsMode |= ENABLE_MOUSE_INPUT;      /* explicitly enabling mouse for the case when it was disabled in windows */
305     /*bitsMode |= ENABLE_QUICK_EDIT_MODE;  /* explicitly enabling user to use the mouse for text selection and editing*/
306     bitsMode |= ENABLE_WINDOW_INPUT;     /* process console screen buffer changes (?)*/
307 
308     SetConsoleMode( hStdIn, bitsMode );
309     SetConsoleCursorInfo( hStdOut, &curinfo );   /* cursor off */
310 }
311 
312 
restore_console_settings(HANDLE hStdOut,HANDLE hStdIn,KTUI_pf const * pWinSettings)313 static void restore_console_settings( HANDLE hStdOut, HANDLE hStdIn, KTUI_pf const * pWinSettings )
314 {
315     SetConsoleMode( hStdIn, pWinSettings->bitsConsoleInputMode );
316 
317     SetConsoleCursorPosition( hStdOut, pWinSettings->sbinfo.dwCursorPosition );
318     SetConsoleCursorInfo( hStdOut, &pWinSettings->curinfo );
319     SetConsoleTextAttribute( hStdOut, pWinSettings->sbinfo.wAttributes );
320     SetConsoleMode( hStdIn, pWinSettings->bitsConsoleInputMode );
321 }
322 
323 
324 /* This is from MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682022%28v=vs.85%29.aspx */
cls(HANDLE hConsole)325 void cls( HANDLE hConsole )
326 {
327    COORD coordScreen = { 0, 0 };    // home for the cursor
328    DWORD cCharsWritten;
329    CONSOLE_SCREEN_BUFFER_INFO csbi;
330    DWORD dwConSize;
331 
332 /* Get the number of character cells in the current buffer. */
333 
334    if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
335    {
336       return;
337    }
338 
339    dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
340 
341    /* Fill the entire screen with blanks. */
342 
343    if( !FillConsoleOutputCharacter( hConsole,        /* Handle to console screen buffer */
344                                     (TCHAR) ' ',     /* Character to write to the buffer */
345                                     dwConSize,       /* Number of cells to write */
346                                     coordScreen,     /* Coordinates of first cell */
347                                     &cCharsWritten ))/* Receive number of characters written */
348    {
349       return;
350    }
351 
352    /* Get the current text attribute. */
353 
354    if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
355    {
356       return;
357    }
358 
359    /* Set the buffer's attributes accordingly. */
360 
361    if( !FillConsoleOutputAttribute( hConsole,         /* Handle to console screen buffer */
362                                     csbi.wAttributes, /* Character attributes to use */
363                                     dwConSize,        /* Number of cells to set attribute */
364                                     coordScreen,      /* Coordinates of first cell */
365                                     &cCharsWritten )) /* Receive number of characters written */
366    {
367       return;
368    }
369 
370    /* Put the cursor at its home coordinates. */
371 
372    SetConsoleCursorPosition( hConsole, coordScreen );
373 }
374 
375 
KTUI_Init_platform(KTUI * self)376 rc_t CC KTUI_Init_platform( KTUI * self )
377 {
378     HANDLE hStdOut = INVALID_HANDLE_VALUE;
379     HANDLE hStdIn  = INVALID_HANDLE_VALUE;
380     rc_t rc = 0;
381     struct KTUI_pf * pf = malloc( sizeof(*pf) );
382     if ( pf == NULL )
383         rc = RC( rcApp, rcAttr, rcCreating, rcMemory, rcExhausted );
384     else
385     {
386         memset( pf, 0, sizeof( *pf ) );
387         hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
388         hStdIn  = GetStdHandle( STD_INPUT_HANDLE );
389         if ( hStdOut != INVALID_HANDLE_VALUE && hStdIn != INVALID_HANDLE_VALUE )
390         {
391             save_current_console_settings( hStdOut, hStdIn, pf, self );
392             set_tui_settings( hStdOut, hStdIn, pf );
393         }
394 
395         self->pf = pf;
396         InitVKTable( g_VirtualCodeTable, _countof( g_VirtualCodeTable ) );
397 		store_mouse_event( pf, 0, 0, ktui_mouse_button_none, ktui_mouse_action_none );
398     }
399     return rc;
400 }
401 
402 
KTUI_Destroy_platform(struct KTUI_pf * pf)403 rc_t CC KTUI_Destroy_platform ( struct KTUI_pf * pf )
404 {
405     HANDLE hStdOut = INVALID_HANDLE_VALUE;
406     HANDLE hStdIn  = INVALID_HANDLE_VALUE;
407 
408     if ( pf != NULL )
409     {
410         hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
411         hStdIn  = GetStdHandle( STD_INPUT_HANDLE );
412         if ( hStdOut != INVALID_HANDLE_VALUE && hStdIn != INVALID_HANDLE_VALUE )
413         {
414             restore_console_settings( hStdOut, hStdIn, pf );
415             cls( hStdOut );
416         }
417         free( ( void * ) pf );
418     }
419 
420     return 0;
421 }
422 
423 
KeyEventProc(KEY_EVENT_RECORD const * pEvent,struct KTUI * self)424 static BOOL KeyEventProc( KEY_EVENT_RECORD const* pEvent, struct KTUI * self )
425 {
426     BOOL ret = FALSE;
427     if ( pEvent->bKeyDown )
428     {
429         struct KTUI_pf * pf = ( self->pf );
430         int key = 0;
431 
432         KTUI_key code = TranslateVKtoKTUI( pEvent->wVirtualKeyCode );
433 
434         if ( code == ktui_alpha )
435         {
436             key = ( int )( pEvent->uChar.AsciiChar );
437 
438             /* we artificially let the user press ESC twice to make it do the same as a posix console */
439             if ( key == 27 )
440             {
441                 if ( pf->esc_count == 0 )
442                 {
443                     code = ktui_none;
444                     pf->esc_count = 1;
445                 }
446                 else
447                     pf->esc_count = 0;
448             }
449             else
450                 pf->esc_count = 0;
451         }
452         else
453             pf->esc_count = 0;
454 
455         if ( code == ktui_tab && ( ( pEvent->dwControlKeyState & SHIFT_PRESSED ) == SHIFT_PRESSED ) )
456             code = ktui_shift_tab;
457 
458         if ( code != ktui_none )
459         {
460             size_t i;
461             for ( i = 0; i < pEvent->wRepeatCount; ++i )
462             {
463                 put_kb_event( self, key, code );
464                 ret = TRUE;
465             }
466         }
467     }
468     return ret;
469 }
470 
471 
get_button(DWORD btn_flags)472 static KTUI_mouse_button get_button( DWORD btn_flags )
473 {
474 	KTUI_mouse_button res = ktui_mouse_button_none;
475 	switch( btn_flags )
476 	{
477         case FROM_LEFT_1ST_BUTTON_PRESSED : res = ktui_mouse_button_left; break;
478         case FROM_LEFT_2ND_BUTTON_PRESSED : res = ktui_mouse_button_middle; break;
479         case RIGHTMOST_BUTTON_PRESSED     : res = ktui_mouse_button_right; break;
480 		case 0 : res = ktui_mouse_button_up; break;
481 	}
482 	return res;
483 }
484 
get_action(DWORD ev_flags,KTUI_mouse_button button)485 static KTUI_mouse_action get_action( DWORD ev_flags, KTUI_mouse_button button )
486 {
487 	KTUI_mouse_action res = ktui_mouse_action_none;
488 
489 	if ( ev_flags == 0 || ( ( ev_flags & DOUBLE_CLICK ) == DOUBLE_CLICK ) )
490 	{
491 		res = ktui_mouse_action_button;
492 	}
493 	else if ( ( ev_flags & MOUSE_MOVED ) == MOUSE_MOVED )
494 	{
495 		/* to make the behavior the same as on posix:
496 		   if not mouse-buttons is pressed, do not report a move action... */
497 		if ( button != ktui_mouse_button_up )
498 			res = ktui_mouse_action_move;
499 	}
500 	else if ( ( ev_flags & MOUSE_WHEELED ) == MOUSE_WHEELED )
501 	{
502 		res = ktui_mouse_action_scroll;
503 	}
504 	return res;
505 }
506 
MouseEventProc(MOUSE_EVENT_RECORD const * pEvent,struct KTUI * self)507 static void MouseEventProc( MOUSE_EVENT_RECORD const* pEvent, struct KTUI* self )
508 {
509     KTUI_mouse_button button = get_button( pEvent->dwButtonState );
510 	KTUI_mouse_action action = get_action( pEvent->dwEventFlags, button );
511 
512 	if ( button != ktui_mouse_button_none && action != ktui_mouse_action_none )
513 	{
514 		int x = pEvent->dwMousePosition.X;
515 		int y = pEvent->dwMousePosition.Y;
516 		if ( different_mouse_event( self->pf, x, y, button, action ) )
517 		{
518 			put_mouse_event( self, x, y, button, action,
519 						    ( uint32_t )( pEvent->dwEventFlags & 0xFFFFFFFF ) );
520 			store_mouse_event( self->pf, x, y, button, action );
521 		}
522 	}
523 }
524 
525 
WindowBufferSizeEventProc(WINDOW_BUFFER_SIZE_RECORD const * pEvent,struct KTUI * self)526 static void WindowBufferSizeEventProc( WINDOW_BUFFER_SIZE_RECORD const* pEvent, struct KTUI* self )
527 {
528     put_window_event( self, pEvent->dwSize.Y, pEvent->dwSize.X );
529 }
530 
531 
532 #define INPUT_EVENT_BUF_SIZE 10
533 
ReadAndProcessEvents(struct KTUI * self,HANDLE h)534 static BOOL ReadAndProcessEvents( struct KTUI * self, HANDLE h )
535 {
536     DWORD nEventsRead;
537     INPUT_RECORD arrInputEvents[ INPUT_EVENT_BUF_SIZE ];
538     PINPUT_RECORD pInputEvents = arrInputEvents;
539 	BOOL res = ReadConsoleInput( h, pInputEvents, INPUT_EVENT_BUF_SIZE, &nEventsRead );
540     if ( res )
541 	{
542 		DWORD i;
543 		for ( i = 0; i < nEventsRead; ++i )
544 		{
545 			switch ( pInputEvents[ i ].EventType )
546 			{
547 				case KEY_EVENT					: KeyEventProc( &pInputEvents[ i ].Event.KeyEvent, self ); break;
548 				case MOUSE_EVENT				: MouseEventProc( &pInputEvents[ i ].Event.MouseEvent, self ); break;
549 				case WINDOW_BUFFER_SIZE_EVENT	: WindowBufferSizeEventProc( &pInputEvents[ i ].Event.WindowBufferSizeEvent, self ); break;
550 			}
551 		}
552     }
553     return res;
554 }
555 
556 
read_from_stdin(struct KTUI * self,uint32_t timeout)557 void CC read_from_stdin( struct KTUI * self, uint32_t timeout )
558 {
559     BOOL resEventProc;
560     DWORD dwStartedWaitingTime, dwEffectiveTimeout, dwTimeElapsed, dwWaitRes;
561     HANDLE h = INVALID_HANDLE_VALUE;
562     DWORD const dwMinimumTimeout = 10;
563 
564     h = GetStdHandle( STD_INPUT_HANDLE );
565     if ( h == INVALID_HANDLE_VALUE )
566         return;
567 
568     /* blocking waiting with the timeout */
569     dwEffectiveTimeout = min( ( DWORD )( timeout / 1000 ), dwMinimumTimeout );
570     dwStartedWaitingTime = GetTickCount();
571     dwTimeElapsed = 0;
572     for ( ; ; )
573     {
574         dwWaitRes = WaitForSingleObject( h, dwEffectiveTimeout - dwTimeElapsed );
575         if ( dwWaitRes == WAIT_OBJECT_0 )
576         {
577             resEventProc = ReadAndProcessEvents( self, h );
578             if ( resEventProc )
579                 break;
580             /*continue waiting for the remaining time */
581             dwTimeElapsed = GetTickCount() - dwStartedWaitingTime;
582             if ( dwTimeElapsed >= dwEffectiveTimeout )
583                 break;
584         }
585         else /* timeout, error */
586         {
587             break;
588         }
589     }
590 }
591