1 /* Public Domain Curses */
2 
3 #include "pdcwin.h"
4 #include <tchar.h>
5 #include <stdint.h>
6 #include <assert.h>
7 #include "pdccolor.h"
8 #ifdef WIN32_LEAN_AND_MEAN
9    #ifdef PDC_WIDE
10       #include <shellapi.h>
11    #endif
12    #include <stdlib.h>
13 #endif
14 
15 /* COLOR_PAIR to attribute encoding table. */
16 
17 static int menu_shown = 1;
18 static int min_lines = 25, max_lines = 25;
19 static int min_cols = 80, max_cols = 80;
20 
21 #if defined( CHTYPE_64) && defined( PDC_WIDE)
22     #define USING_COMBINING_CHARACTER_SCHEME
23     int PDC_expand_combined_characters( const cchar_t c, cchar_t *added);  /* addch.c */
24 #endif
25 
26 /* Some older versions of Microsoft C/C++ don't understand about
27 inlined functions.  Until we puzzle out which ones do and which
28 don't,  we'll just leave "inlined" functions as plain old static
29 functions.        */
30 
31 #ifdef _MSC_VER
32 #define INLINE static
33 #else
34 #define INLINE static inline
35 #endif
36 
37 static int add_mouse( int button, const int action, const int x, const int y);
38 static int keep_size_within_bounds( int *lines, int *cols);
39 INLINE int set_default_sizes_from_registry( const int n_cols, const int n_rows,
40                const int xloc, const int yloc, const int menu_shown);
41 void PDC_transform_line_given_hdc( const HDC hdc, const int lineno,
42                              int x, int len, const chtype *srcp);
43 
44 /* We have a 'base' standard palette of 256 colors,  plus a true-color
45 cube of 16 million colors. */
46 
47 #define N_COLORS 256 + 256 * 256 * 256;
48 
49 #ifdef A_OVERLINE
50 #define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE | A_OVERLINE | A_STRIKEOUT)
51 #else
52 #define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE)
53 #endif
54 
55    /* If PDC_MAX_MOUSE_BUTTONS is undefined,  it means the user hasn't     */
56    /* gotten a current 'curses.h' in which five-button mice are supported. */
57    /* To handle this gracefully,  we'll just fall back to three buttons.   */
58 
59 #ifndef PDC_MAX_MOUSE_BUTTONS
60    #define PDC_MAX_MOUSE_BUTTONS 3
61 #endif
62 
63 #define VERTICAL_WHEEL_EVENT      PDC_MAX_MOUSE_BUTTONS
64 #define HORIZONTAL_WHEEL_EVENT   (PDC_MAX_MOUSE_BUTTONS + 1)
65 
66 int PDC_show_ctrl_alts = 0;
67 
68 /* RR: Removed statis on next line */
69 bool PDC_bDone = FALSE;
70 static HWND originally_focussed_window;
71 
debug_printf(const char * format,...)72 int debug_printf( const char *format, ...)
73 {
74     static bool debugging = TRUE;
75 
76     if( debugging)
77     {
78         const char *output_filename = getenv( "PDC_DEBUG");
79 
80         if( !output_filename)
81             debugging = FALSE;       /* don't bother trying again */
82         else
83         {
84             FILE *ofile = fopen( output_filename, "a");
85 
86             if( ofile)
87             {
88                 va_list argptr;
89                 va_start( argptr, format);
90                 vfprintf( ofile, format, argptr);
91                 va_end( argptr);
92                 fclose( ofile);
93             }
94             else
95             {
96                 printf( "Opening '%s' failed\n", output_filename);
97                 exit( 0);
98                 debugging = FALSE;       /* don't bother trying again */
99             }
100         }
101     }
102     return( 0);
103 }
104 
105 HWND PDC_hWnd;
106 static int PDC_argc = 0;
107 static char **PDC_argv = NULL;
108 
final_cleanup(void)109 static void final_cleanup( void)
110 {
111     debug_printf( "final_cleanup: SP = %p\n", SP);
112     if (SP)
113     {
114         RECT rect;
115 
116         GetWindowRect( PDC_hWnd, &rect);
117         set_default_sizes_from_registry( SP->cols, SP->lines,
118                   rect.left, rect.top, menu_shown);
119     }
120     PDC_LOG(( "final_cleanup: freeing fonts\n"));
121     PDC_transform_line( 0, 0, 0, NULL);  /* free any fonts */
122     if( originally_focussed_window)
123         SetForegroundWindow( originally_focussed_window);
124     if( PDC_argc)
125     {
126         int i;
127 
128         for( i = 0; i < PDC_argc; i++)
129             free( PDC_argv[i]);
130         free( PDC_argv);
131         PDC_argc = 0;
132         PDC_argv = NULL;
133     }
134 #ifdef USING_COMBINING_CHARACTER_SCHEME
135     PDC_expand_combined_characters( 0, NULL);       /* free internal buffer */
136 #endif
137     debug_printf( "reset foreground window\n");
138 }
139 
PDC_scr_close(void)140 void PDC_scr_close(void)
141 {
142     PDC_LOG(("PDC_scr_close() - called\n"));
143     final_cleanup( );
144     PDC_bDone = TRUE;
145 }
146 
147 /* NOTE that PDC_scr_free( ) is called only from delscreen( ),    */
148 /* which is rarely called.  It appears that most programs simply  */
149 /* rely on the memory getting freed when the program terminates.  */
150 
PDC_scr_free(void)151 void PDC_scr_free(void)
152 {
153     PDC_free_palette( );
154 }
155 
156 int PDC_choose_a_new_font( void);                     /* pdcdisp.c */
157 
158 #define KEY_QUEUE_SIZE    30
159 
160        /* By default,  the PDC_shutdown_key[] array contains 0       */
161        /* (i.e., there's no key that's supposed to be returned for   */
162        /* exit handling), and 22 = Ctrl-V (i.e.,  hit Ctrl-V to      */
163        /* paste text from the clipboard into the key queue);  then   */
164        /* Ctl-= (enlarge font) and Ctl-Minus (decrease font);  then  */
165        /* Ctl-, (select font from dialog).                           */
166 
167 static int PDC_shutdown_key[PDC_MAX_FUNCTION_KEYS] = { 0, 22, CTL_EQUAL, CTL_MINUS,
168                                  CTL_COMMA };
169 int PDC_n_rows, PDC_n_cols;
170 int PDC_cxChar, PDC_cyChar, PDC_key_queue_low = 0, PDC_key_queue_high = 0;
171 int PDC_key_queue[KEY_QUEUE_SIZE];
172 
173    /* If the following is true,  you can enter Unicode values by hitting */
174    /* Alt and holding it down while typing the value of the character on */
175    /* the numeric keypad (for decimal entry);  _or_ you can hit Alt-Padplus */
176    /* and then enter a hex value,  while holding down the Alt key.  In   */
177    /* either case,  when you release the Alt key,  the Unicode character */
178    /* is added to the queue.  For hex entry,  0-9 can come either from   */
179    /* the numeric keypad or the "usual" keyboard.                        */
180 bool PDC_allow_numpad_unicode = TRUE;
181 static int numpad_unicode_value = 0;
182 
183 static void adjust_font_size( const int font_size_change);
184 
add_key_to_queue(const int new_key)185 static void add_key_to_queue( const int new_key)
186 {
187     const int new_idx = ((PDC_key_queue_high + 1) % KEY_QUEUE_SIZE);
188                 /* This is usually 10,  but is set to 16 if the user   */
189                 /* hits ALT_PADPLUS and is about to enter a hex value: */
190     static int unicode_radix = 10;
191 
192     if( PDC_allow_numpad_unicode)
193     {
194         int digit = -1;
195 
196         if( new_key >= ALT_PAD0 && new_key <= ALT_PAD9)
197             digit = new_key - ALT_PAD0;
198                   /* In hex Unicode entry,  you can enter digits on both */
199                   /* the numeric and "standard" keyboards :              */
200         if( unicode_radix == 16 && new_key >= ALT_0 && new_key <= ALT_9)
201             digit = new_key - ALT_0;
202         if( unicode_radix == 16 && new_key >= ALT_A && new_key <= ALT_F)
203             digit = new_key - ALT_A + 10;
204         if( digit >= 0)
205         {
206             numpad_unicode_value = numpad_unicode_value * unicode_radix + digit;
207             return;
208         }
209         if( new_key == ALT_PADPLUS)
210         {                            /* signal to begin hex Unicode entry */
211             unicode_radix = 16;
212             return;
213         }
214     }
215     unicode_radix = 10;
216     if( new_key && new_key == PDC_shutdown_key[FUNCTION_KEY_ABORT])
217         exit( -1);
218     else if( new_key && new_key == PDC_shutdown_key[FUNCTION_KEY_ENLARGE_FONT])
219         adjust_font_size( 1);
220     else if( new_key && new_key == PDC_shutdown_key[FUNCTION_KEY_SHRINK_FONT])
221         adjust_font_size( -1);
222     else if( new_key && new_key == PDC_shutdown_key[FUNCTION_KEY_CHOOSE_FONT])
223     {
224         if( PDC_choose_a_new_font( ))
225             adjust_font_size( 0);
226     }
227     else if( new_idx != PDC_key_queue_low)
228     {
229         PDC_key_queue[PDC_key_queue_high] = new_key;
230         PDC_key_queue_high = new_idx;
231     }
232 }
233 
234 /************************************************************************
235  *    Table for key code translation of function keys in keypad mode    *
236  *    These values are for strict IBM keyboard compatibles only         *
237  ************************************************************************/
238 
239 typedef struct
240 {
241     unsigned short normal;
242     unsigned short shift;
243     unsigned short control;
244     unsigned short alt;
245     unsigned short extended;
246 } KPTAB;
247 
248 
249 static const KPTAB kptab[] =
250 {
251     {0,          0,         0,           0,          0   }, /* 0  */
252     {0,          0,         0,           0,          0   }, /* 1   VK_LBUTTON */
253     {0,          0,         0,           0,          0   }, /* 2   VK_RBUTTON */
254     {CTL_PAUSE,  'a',       'b',         'c',        0   }, /* 3   VK_CANCEL  */
255     {0,          0,         0,           0,          0   }, /* 4   VK_MBUTTON */
256     {0,          0,         0,           0,          0   }, /* 5   */
257     {0,          0,         0,           0,          0   }, /* 6   */
258     {0,          0,         0,           0,          0   }, /* 7   */
259     {0x08,       0x08,      0x7F,        ALT_BKSP,   0   }, /* 8   VK_BACK    */
260     {0x09,       KEY_BTAB,  CTL_TAB,     ALT_TAB,    999 }, /* 9   VK_TAB     */
261     {0,          0,         0,           0,          0   }, /* 10  */
262     {0,          0,         0,           0,          0   }, /* 11  */
263     {KEY_B2,     0x35,      CTL_PAD5,    ALT_PAD5,   0   }, /* 12  VK_CLEAR   */
264     {0x0D,       0x0D,      CTL_ENTER,   ALT_ENTER,  1   }, /* 13  VK_RETURN  */
265     {0,          0,         0,           0,          0   }, /* 14  */
266     {0,          0,         0,           0,          0   }, /* 15  */
267     {0,          0,         0,           0,          0   }, /* 16  VK_SHIFT   HANDLED SEPARATELY */
268     {0,          0,         0,           0,          0   }, /* 17  VK_CONTROL HANDLED SEPARATELY */
269     {0,          0,         0,           0,          0   }, /* 18  VK_MENU    HANDLED SEPARATELY */
270     {KEY_PAUSE,  KEY_SPAUSE,CTL_PAUSE,   0,          0   }, /* 19  VK_PAUSE   */
271     {0,          0,         0,           0,          0   }, /* 20  VK_CAPITAL HANDLED SEPARATELY */
272     {0,          0,         0,           0,          0   }, /* 21  VK_HANGUL  */
273     {0,          0,         0,           0,          0   }, /* 22  */
274     {0,          0,         0,           0,          0   }, /* 23  VK_JUNJA   */
275     {0,          0,         0,           0,          0   }, /* 24  VK_FINAL   */
276     {0,          0,         0,           0,          0   }, /* 25  VK_HANJA   */
277     {0,          0,         0,           0,          0   }, /* 26  */
278     {0x1B,       0x1B,      0x1B,        ALT_ESC,    0   }, /* 27  VK_ESCAPE  */
279     {0,          0,         0,           0,          0   }, /* 28  VK_CONVERT */
280     {0,          0,         0,           0,          0   }, /* 29  VK_NONCONVERT */
281     {0,          0,         0,           0,          0   }, /* 30  VK_ACCEPT  */
282     {0,          0,         0,           0,          0   }, /* 31  VK_MODECHANGE */
283     {0x20,       0x20,      0x20,        0x20,       0   }, /* 32  VK_SPACE   */
284     {KEY_A3,     0x39,      CTL_PAD9,    ALT_PAD9,   3   }, /* 33  VK_PRIOR   */
285     {KEY_C3,     0x33,      CTL_PAD3,    ALT_PAD3,   4   }, /* 34  VK_NEXT    */
286     {KEY_C1,     0x31,      CTL_PAD1,    ALT_PAD1,   5   }, /* 35  VK_END     */
287     {KEY_A1,     0x37,      CTL_PAD7,    ALT_PAD7,   6   }, /* 36  VK_HOME    */
288     {KEY_B1,     0x34,      CTL_PAD4,    ALT_PAD4,   7   }, /* 37  VK_LEFT    */
289     {KEY_A2,     0x38,      CTL_PAD8,    ALT_PAD8,   8   }, /* 38  VK_UP      */
290     {KEY_B3,     0x36,      CTL_PAD6,    ALT_PAD6,   9   }, /* 39  VK_RIGHT   */
291     {KEY_C2,     0x32,      CTL_PAD2,    ALT_PAD2,   10  }, /* 40  VK_DOWN    */
292     {0,          0,         0,           0,          0   }, /* 41  VK_SELECT  */
293     {0,          0,         0,           0,          0   }, /* 42  VK_PRINT   */
294     {0,          0,         0,           0,          0   }, /* 43  VK_EXECUTE */
295     {KEY_PRINTSCREEN, 0,    0,       ALT_PRINTSCREEN, 0  }, /* 44  VK_SNAPSHOT*/
296     {PAD0,       0x30,      CTL_PAD0,    ALT_PAD0,   11  }, /* 45  VK_INSERT  */
297     {PADSTOP,    0x2E,      CTL_PADSTOP, ALT_PADSTOP,12  }, /* 46  VK_DELETE  */
298     {0,          0,         0,           0,          0   }, /* 47  VK_HELP    */
299     {0x30,       0x29,      CTL_0,       ALT_0,      0   }, /* 48  */
300     {0x31,       0x21,      CTL_1,       ALT_1,      0   }, /* 49  */
301     {0x32,       0x40,      CTL_2,       ALT_2,      0   }, /* 50  */
302     {0x33,       0x23,      CTL_3,       ALT_3,      0   }, /* 51  */
303     {0x34,       0x24,      CTL_4,       ALT_4,      0   }, /* 52  */
304     {0x35,       0x25,      CTL_5,       ALT_5,      0   }, /* 53  */
305     {0x36,       0x5E,      CTL_6,       ALT_6,      0   }, /* 54  */
306     {0x37,       0x26,      CTL_7,       ALT_7,      0   }, /* 55  */
307     {0x38,       0x2A,      CTL_8,       ALT_8,      0   }, /* 56  */
308     {0x39,       0x28,      CTL_9,       ALT_9,      0   }, /* 57  */
309     {0,          0,         0,           0,          0   }, /* 58  */
310     {0,          0,         0,           0,          0   }, /* 59  */
311     {0,          0,         0,           0,          0   }, /* 60  */
312     {0,          0,         0,           0,          0   }, /* 61  */
313     {0,          0,         0,           0,          0   }, /* 62  */
314     {0,          0,         0,           0,          0   }, /* 63  */
315     {0,          0,         0,           0,          0   }, /* 64  */
316     {0x61,       0x41,      0x01,        ALT_A,      0   }, /* 65  */
317     {0x62,       0x42,      0x02,        ALT_B,      0   }, /* 66  */
318     {0x63,       0x43,      0x03,        ALT_C,      0   }, /* 67  */
319     {0x64,       0x44,      0x04,        ALT_D,      0   }, /* 68  */
320     {0x65,       0x45,      0x05,        ALT_E,      0   }, /* 69  */
321     {0x66,       0x46,      0x06,        ALT_F,      0   }, /* 70  */
322     {0x67,       0x47,      0x07,        ALT_G,      0   }, /* 71  */
323     {0x68,       0x48,      0x08,        ALT_H,      0   }, /* 72  */
324     {0x69,       0x49,      0x09,        ALT_I,      0   }, /* 73  */
325     {0x6A,       0x4A,      0x0A,        ALT_J,      0   }, /* 74  */
326     {0x6B,       0x4B,      0x0B,        ALT_K,      0   }, /* 75  */
327     {0x6C,       0x4C,      0x0C,        ALT_L,      0   }, /* 76  */
328     {0x6D,       0x4D,      0x0D,        ALT_M,      0   }, /* 77  */
329     {0x6E,       0x4E,      0x0E,        ALT_N,      0   }, /* 78  */
330     {0x6F,       0x4F,      0x0F,        ALT_O,      0   }, /* 79  */
331     {0x70,       0x50,      0x10,        ALT_P,      0   }, /* 80  */
332     {0x71,       0x51,      0x11,        ALT_Q,      0   }, /* 81  */
333     {0x72,       0x52,      0x12,        ALT_R,      0   }, /* 82  */
334     {0x73,       0x53,      0x13,        ALT_S,      0   }, /* 83  */
335     {0x74,       0x54,      0x14,        ALT_T,      0   }, /* 84  */
336     {0x75,       0x55,      0x15,        ALT_U,      0   }, /* 85  */
337     {0x76,       0x56,      0x16,        ALT_V,      0   }, /* 86  */
338     {0x77,       0x57,      0x17,        ALT_W,      0   }, /* 87  */
339     {0x78,       0x58,      0x18,        ALT_X,      0   }, /* 88  */
340     {0x79,       0x59,      0x19,        ALT_Y,      0   }, /* 89  */
341     {0x7A,       0x5A,      0x1A,        ALT_Z,      0   }, /* 90  */
342     {0,          0,         0,           0,          0   }, /* 91  VK_LWIN    */
343     {0,          0,         0,           0,          0   }, /* 92  VK_RWIN    */
344     {KEY_APPS,   KEY_SAPPS, CTL_APPS,    ALT_APPS,   13  }, /* 93  VK_APPS    */
345     {0,          0,         0,           0,          0   }, /* 94  */
346     {0,          0,         0,           0,          0   }, /* 95  */
347     {0x30,       0,         CTL_PAD0,    ALT_PAD0,   0   }, /* 96  VK_NUMPAD0 */
348     {0x31,       0,         CTL_PAD1,    ALT_PAD1,   0   }, /* 97  VK_NUMPAD1 */
349     {0x32,       0,         CTL_PAD2,    ALT_PAD2,   0   }, /* 98  VK_NUMPAD2 */
350     {0x33,       0,         CTL_PAD3,    ALT_PAD3,   0   }, /* 99  VK_NUMPAD3 */
351     {0x34,       0,         CTL_PAD4,    ALT_PAD4,   0   }, /* 100 VK_NUMPAD4 */
352     {0x35,       0,         CTL_PAD5,    ALT_PAD5,   0   }, /* 101 VK_NUMPAD5 */
353     {0x36,       0,         CTL_PAD6,    ALT_PAD6,   0   }, /* 102 VK_NUMPAD6 */
354     {0x37,       0,         CTL_PAD7,    ALT_PAD7,   0   }, /* 103 VK_NUMPAD7 */
355     {0x38,       0,         CTL_PAD8,    ALT_PAD8,   0   }, /* 104 VK_NUMPAD8 */
356     {0x39,       0,         CTL_PAD9,    ALT_PAD9,   0   }, /* 105 VK_NUMPAD9 */
357     {PADSTAR,   SHF_PADSTAR,CTL_PADSTAR, ALT_PADSTAR,999 }, /* 106 VK_MULTIPLY*/
358     {PADPLUS,   SHF_PADPLUS,CTL_PADPLUS, ALT_PADPLUS,999 }, /* 107 VK_ADD     */
359     {0,          0,         0,           0,          0   }, /* 108 VK_SEPARATOR     */
360     {PADMINUS, SHF_PADMINUS,CTL_PADMINUS,ALT_PADMINUS,999}, /* 109 VK_SUBTRACT*/
361     {0x2E,       0,         CTL_PADSTOP, ALT_PADSTOP,0   }, /* 110 VK_DECIMAL */
362     {PADSLASH,  SHF_PADSLASH,CTL_PADSLASH,ALT_PADSLASH,2 }, /* 111 VK_DIVIDE  */
363     {KEY_F(1),   KEY_F(13), KEY_F(25),   KEY_F(37),  0   }, /* 112 VK_F1      */
364     {KEY_F(2),   KEY_F(14), KEY_F(26),   KEY_F(38),  0   }, /* 113 VK_F2      */
365     {KEY_F(3),   KEY_F(15), KEY_F(27),   KEY_F(39),  0   }, /* 114 VK_F3      */
366     {KEY_F(4),   KEY_F(16), KEY_F(28),   KEY_F(40),  0   }, /* 115 VK_F4      */
367     {KEY_F(5),   KEY_F(17), KEY_F(29),   KEY_F(41),  0   }, /* 116 VK_F5      */
368     {KEY_F(6),   KEY_F(18), KEY_F(30),   KEY_F(42),  0   }, /* 117 VK_F6      */
369     {KEY_F(7),   KEY_F(19), KEY_F(31),   KEY_F(43),  0   }, /* 118 VK_F7      */
370     {KEY_F(8),   KEY_F(20), KEY_F(32),   KEY_F(44),  0   }, /* 119 VK_F8      */
371     {KEY_F(9),   KEY_F(21), KEY_F(33),   KEY_F(45),  0   }, /* 120 VK_F9      */
372     {KEY_F(10),  KEY_F(22), KEY_F(34),   KEY_F(46),  0   }, /* 121 VK_F10     */
373     {KEY_F(11),  KEY_F(23), KEY_F(35),   KEY_F(47),  0   }, /* 122 VK_F11     */
374     {KEY_F(12),  KEY_F(24), KEY_F(36),   KEY_F(48),  0   }, /* 123 VK_F12     */
375 
376     /* 124 through 218 */
377 
378     {0, 0, 0, 0, 0},  /* 124 VK_F13 */
379     {0, 0, 0, 0, 0},  /* 125 VK_F14 */
380     {0, 0, 0, 0, 0},  /* 126 VK_F15 */
381     {0, 0, 0, 0, 0},  /* 127 VK_F16 */
382     {0, 0, 0, 0, 0},  /* 128 VK_F17 */
383     {0, 0, 0, 0, 0},  /* 129 VK_F18 */
384     {0, 0, 0, 0, 0},  /* 130 VK_F19 */
385     {0, 0, 0, 0, 0},  /* 131 VK_F20 */
386     {0, 0, 0, 0, 0},  /* 132 VK_F21 */
387     {0, 0, 0, 0, 0},  /* 133 VK_F22 */
388     {0, 0, 0, 0, 0},  /* 134 VK_F23 */
389     {0, 0, 0, 0, 0},  /* 135 VK_F24 */
390     {0, 0, 0, 0, 0},  /* 136 unassigned */
391     {0, 0, 0, 0, 0},  /* 137 unassigned */
392     {0, 0, 0, 0, 0},  /* 138 unassigned */
393     {0, 0, 0, 0, 0},  /* 139 unassigned */
394     {0, 0, 0, 0, 0},  /* 140 unassigned */
395     {0, 0, 0, 0, 0},  /* 141 unassigned */
396     {0, 0, 0, 0, 0},  /* 142 unassigned */
397     {0, 0, 0, 0, 0},  /* 143 unassigned */
398     {0, 0, 0, 0, 0},  /* 144 VK_NUMLOCK */
399     {KEY_SCROLLLOCK, 0, 0, ALT_SCROLLLOCK, 0},    /* 145 VKSCROLL */
400     {0, 0, 0, 0, 0},  /* 146 OEM specific */
401     {0, 0, 0, 0, 0},  /* 147 OEM specific */
402     {0, 0, 0, 0, 0},  /* 148 OEM specific */
403     {0, 0, 0, 0, 0},  /* 149 OEM specific */
404     {0, 0, 0, 0, 0},  /* 150 OEM specific */
405     {0, 0, 0, 0, 0},  /* 151 Unassigned */
406     {0, 0, 0, 0, 0},  /* 152 Unassigned */
407     {0, 0, 0, 0, 0},  /* 153 Unassigned */
408     {0, 0, 0, 0, 0},  /* 154 Unassigned */
409     {0, 0, 0, 0, 0},  /* 155 Unassigned */
410     {0, 0, 0, 0, 0},  /* 156 Unassigned */
411     {0, 0, 0, 0, 0},  /* 157 Unassigned */
412     {0, 0, 0, 0, 0},  /* 158 Unassigned */
413     {0, 0, 0, 0, 0},  /* 159 Unassigned */
414     {0, 0, 0, 0, 0},  /* 160 VK_LSHIFT */
415     {0, 0, 0, 0, 0},  /* 161 VK_RSHIFT */
416     {0, 0, 0, 0, 0},  /* 162 VK_LCONTROL */
417     {0, 0, 0, 0, 0},  /* 163 VK_RCONTROL */
418     {0, 0, 0, 0, 0},  /* 164 VK_LMENU */
419     {0, 0, 0, 0, 0},  /* 165 VK_RMENU */
420     {0, 0, 0, 0, 14},  /* 166 VK_BROWSER_BACK        */
421     {0, 0, 0, 0, 15},  /* 167 VK_BROWSER_FORWARD     */
422     {0, 0, 0, 0, 16},  /* 168 VK_BROWSER_REFRESH     */
423     {0, 0, 0, 0, 17},  /* 169 VK_BROWSER_STOP        */
424     {0, 0, 0, 0, 18},  /* 170 VK_BROWSER_SEARCH      */
425     {0, 0, 0, 0, 19},  /* 171 VK_BROWSER_FAVORITES   */
426     {0, 0, 0, 0, 20},  /* 172 VK_BROWSER_HOME        */
427     {0, 0, 0, 0, 21},  /* 173 VK_VOLUME_MUTE         */
428     {0, 0, 0, 0, 22},  /* 174 VK_VOLUME_DOWN         */
429     {0, 0, 0, 0, 23},  /* 175 VK_VOLUME_UP           */
430     {0, 0, 0, 0, 24},  /* 176 VK_MEDIA_NEXT_TRACK    */
431     {0, 0, 0, 0, 25},  /* 177 VK_MEDIA_PREV_TRACK    */
432     {0, 0, 0, 0, 26},  /* 178 VK_MEDIA_STOP          */
433     {0, 0, 0, 0, 27},  /* 179 VK_MEDIA_PLAY_PAUSE    */
434     {0, 0, 0, 0, 28},  /* 180 VK_LAUNCH_MAIL         */
435     {0, 0, 0, 0, 29},  /* 181 VK_LAUNCH_MEDIA_SELECT */
436     {0, 0, 0, 0, 30},  /* 182 VK_LAUNCH_APP1         */
437     {0, 0, 0, 0, 31},  /* 183 VK_LAUNCH_APP2         */
438     {0, 0, 0, 0, 0},  /* 184 Reserved */
439     {0, 0, 0, 0, 0},  /* 185 Reserved */
440     {';', ':', CTL_SEMICOLON, ALT_SEMICOLON, 0},  /* 186 VK_OEM_1      */
441     {'=', '+', CTL_EQUAL,     ALT_EQUAL,     0},  /* 187 VK_OEM_PLUS   */
442     {',', '<', CTL_COMMA,     ALT_COMMA,     0},  /* 188 VK_OEM_COMMA  */
443     {'-', '_', CTL_MINUS,     ALT_MINUS,     0},  /* 189 VK_OEM_MINUS  */
444     {'.', '>', CTL_STOP,      ALT_STOP,      0},  /* 190 VK_OEM_PERIOD */
445     {'/', '?', CTL_FSLASH,    ALT_FSLASH,    0},  /* 191 VK_OEM_2      */
446     {'`', '~', CTL_BQUOTE,    ALT_BQUOTE,    0},  /* 192 VK_OEM_3      */
447     {0, 0, 0, 0, 0},  /* 193 */
448     {0, 0, 0, 0, 0},  /* 194 */
449     {0, 0, 0, 0, 0},  /* 195 */
450     {0, 0, 0, 0, 0},  /* 196 */
451     {0, 0, 0, 0, 0},  /* 197 */
452     {0, 0, 0, 0, 0},  /* 198 */
453     {0, 0, 0, 0, 0},  /* 199 */
454     {0, 0, 0, 0, 0},  /* 200 */
455     {0, 0, 0, 0, 0},  /* 201 */
456     {0, 0, 0, 0, 0},  /* 202 */
457     {0, 0, 0, 0, 0},  /* 203 */
458     {0, 0, 0, 0, 0},  /* 204 */
459     {0, 0, 0, 0, 0},  /* 205 */
460     {0, 0, 0, 0, 0},  /* 206 */
461     {0, 0, 0, 0, 0},  /* 207 */
462     {0, 0, 0, 0, 0},  /* 208 */
463     {0, 0, 0, 0, 0},  /* 209 */
464     {0, 0, 0, 0, 0},  /* 210 */
465     {0, 0, 0, 0, 0},  /* 211 */
466     {0, 0, 0, 0, 0},  /* 212 */
467     {0, 0, 0, 0, 0},  /* 213 */
468     {0, 0, 0, 0, 0},  /* 214 */
469     {0, 0, 0, 0, 0},  /* 215 */
470     {0, 0, 0, 0, 0},  /* 216 */
471     {0, 0, 0, 0, 0},  /* 217 */
472     {0, 0, 0, 0, 0},  /* 218 */
473     {0x5B,       0x7B,      0x1B,        ALT_LBRACKET,0  }, /* 219 VK_OEM_4 */
474     {0x5C,       0x7C,      0x1C,        ALT_BSLASH, 0   }, /* 220 VK_OEM_5 */
475     {0x5D,       0x7D,      0x1D,        ALT_RBRACKET,0  }, /* 221 VK_OEM_6 */
476     {'\'',       '"',       0x27,        ALT_FQUOTE, 0   }, /* 222 VK_OEM_7 */
477     {0,          0,         0,           0,          0   }, /* 223 VK_OEM_8 */
478     {0,          0,         0,           0,          0   }, /* 224 */
479     {0,          0,         0,           0,          0   }  /* 225 */
480 };
481 /* End of kptab[] */
482 
483 static const KPTAB ext_kptab[] =
484 {
485    {0,          0,              0,              0,          }, /*  0  MUST BE EMPTY */
486    {PADENTER,   SHF_PADENTER,   CTL_PADENTER,   ALT_PADENTER}, /*  1  13 */
487    {PADSLASH,   SHF_PADSLASH,   CTL_PADSLASH,   ALT_PADSLASH}, /*  2 111 */
488    {KEY_PPAGE,  KEY_SPREVIOUS,  CTL_PGUP,       ALT_PGUP    }, /*  3  33 */
489    {KEY_NPAGE,  KEY_SNEXT,      CTL_PGDN,       ALT_PGDN    }, /*  4  34 */
490    {KEY_END,    KEY_SEND,       CTL_END,        ALT_END     }, /*  5  35 */
491    {KEY_HOME,   KEY_SHOME,      CTL_HOME,       ALT_HOME    }, /*  6  36 */
492    {KEY_LEFT,   KEY_SLEFT,      CTL_LEFT,       ALT_LEFT    }, /*  7  37 */
493    {KEY_UP,     KEY_SUP,        CTL_UP,         ALT_UP      }, /*  8  38 */
494    {KEY_RIGHT,  KEY_SRIGHT,     CTL_RIGHT,      ALT_RIGHT   }, /*  9  39 */
495    {KEY_DOWN,   KEY_SDOWN,      CTL_DOWN,       ALT_DOWN    }, /* 10  40 */
496    {KEY_IC,     KEY_SIC,        CTL_INS,        ALT_INS     }, /* 11  45 */
497    {KEY_DC,     KEY_SDC,        CTL_DEL,        ALT_DEL     }, /* 12  46 */
498    {KEY_APPS,   KEY_SAPPS     , CTL_APPS,       ALT_APPS    }, /* 13  93  VK_APPS    */
499    {KEY_BROWSER_BACK, KEY_SBROWSER_BACK, KEY_CBROWSER_BACK, KEY_ABROWSER_BACK, }, /* 14 166 VK_BROWSER_BACK        */
500    {KEY_BROWSER_FWD,  KEY_SBROWSER_FWD,  KEY_CBROWSER_FWD,  KEY_ABROWSER_FWD,  }, /* 15 167 VK_BROWSER_FORWARD     */
501    {KEY_BROWSER_REF,  KEY_SBROWSER_REF,  KEY_CBROWSER_REF,  KEY_ABROWSER_REF,  }, /* 16 168 VK_BROWSER_REFRESH     */
502    {KEY_BROWSER_STOP, KEY_SBROWSER_STOP, KEY_CBROWSER_STOP, KEY_ABROWSER_STOP, }, /* 17 169 VK_BROWSER_STOP        */
503    {KEY_SEARCH,       KEY_SSEARCH,       KEY_CSEARCH,       KEY_ASEARCH,       }, /* 18 170 VK_BROWSER_SEARCH      */
504    {KEY_FAVORITES,    KEY_SFAVORITES,    KEY_CFAVORITES,    KEY_AFAVORITES,    }, /* 19 171 VK_BROWSER_FAVORITES   */
505    {KEY_BROWSER_HOME, KEY_SBROWSER_HOME, KEY_CBROWSER_HOME, KEY_ABROWSER_HOME, }, /* 20 172 VK_BROWSER_HOME        */
506    {KEY_VOLUME_MUTE,  KEY_SVOLUME_MUTE,  KEY_CVOLUME_MUTE,  KEY_AVOLUME_MUTE,  }, /* 21 173 VK_VOLUME_MUTE         */
507    {KEY_VOLUME_DOWN,  KEY_SVOLUME_DOWN,  KEY_CVOLUME_DOWN,  KEY_AVOLUME_DOWN,  }, /* 22 174 VK_VOLUME_DOWN         */
508    {KEY_VOLUME_UP,    KEY_SVOLUME_UP,    KEY_CVOLUME_UP,    KEY_AVOLUME_UP,    }, /* 23 175 VK_VOLUME_UP           */
509    {KEY_NEXT_TRACK,   KEY_SNEXT_TRACK,   KEY_CNEXT_TRACK,   KEY_ANEXT_TRACK,   }, /* 24 176 VK_MEDIA_NEXT_TRACK    */
510    {KEY_PREV_TRACK,   KEY_SPREV_TRACK,   KEY_CPREV_TRACK,   KEY_APREV_TRACK,   }, /* 25 177 VK_MEDIA_PREV_TRACK    */
511    {KEY_MEDIA_STOP,   KEY_SMEDIA_STOP,   KEY_CMEDIA_STOP,   KEY_AMEDIA_STOP,   }, /* 26 178 VK_MEDIA_STOP          */
512    {KEY_PLAY_PAUSE,   KEY_SPLAY_PAUSE,   KEY_CPLAY_PAUSE,   KEY_APLAY_PAUSE,   }, /* 27 179 VK_MEDIA_PLAY_PAUSE    */
513    {KEY_LAUNCH_MAIL,  KEY_SLAUNCH_MAIL,  KEY_CLAUNCH_MAIL,  KEY_ALAUNCH_MAIL,  }, /* 28 180 VK_LAUNCH_MAIL         */
514    {KEY_MEDIA_SELECT, KEY_SMEDIA_SELECT, KEY_CMEDIA_SELECT, KEY_AMEDIA_SELECT, }, /* 29 181 VK_LAUNCH_MEDIA_SELECT */
515    {KEY_LAUNCH_APP1,  KEY_SLAUNCH_APP1,  KEY_CLAUNCH_APP1,  KEY_ALAUNCH_APP1,  }, /* 30 182 VK_LAUNCH_APP1         */
516    {KEY_LAUNCH_APP2,  KEY_SLAUNCH_APP2,  KEY_CLAUNCH_APP2,  KEY_ALAUNCH_APP2,  }, /* 31 183 VK_LAUNCH_APP2         */
517 };
518 
519 
520 HFONT PDC_get_font_handle( const int is_bold);            /* pdcdisp.c */
521 
522 /* Mouse handling is done as follows:
523 
524    What we want is a setup wherein,  if the user presses and releases a
525 mouse button within SP->mouse_wait milliseconds,  there will be a
526 KEY_MOUSE issued through getch( ) and the "button state" for that button
527 will be set to BUTTON_CLICKED.
528 
529    If the user presses and releases the button,  and it takes _longer_
530 than SP->mouse_wait milliseconds,  then there should be a KEY_MOUSE
531 issued with the "button state" set to BUTTON_PRESSED.  Then,  later,
532 another KEY_MOUSE with a BUTTON_RELEASED.
533 
534    To accomplish this:  when a message such as WM_LBUTTONDOWN,
535 WM_RBUTTONDOWN,  or WM_MBUTTONDOWN is issued (and more recently WM_XBUTTONDOWN
536 for five-button mice),  we set up a timer with a period of SP->mouse_wait
537 milliseconds.  There are then two possibilities. The user will release the
538 button quickly (so it's a "click") or they won't (and it's a "press/release").
539 
540    In the first case,  we'll get the WM_xBUTTONUP message before the
541 WM_TIMER one.  We'll kill the timer and set up the BUTTON_CLICKED state. (*)
542 
543    In the second case,  we'll get the WM_TIMER message first,  so we'll
544 set the BUTTON_PRESSED state and kill the timer.  Eventually,  the user
545 will get around to letting go of the mouse button,  and we'll get that
546 WM_xBUTTONUP message.  At that time,  we'll set the BUTTON_RELEASED state
547 and add the second KEY_MOUSE to the key queue.
548 
549    Also,  note that if there is already a KEY_MOUSE to the queue,  there's
550 no point in adding another one.  At least at present,  the actual mouse
551 events aren't queued anyway.  So if there was,  say,  a click and then a
552 release without getch( ) being called in between,  you'd then have two
553 KEY_MOUSEs on the queue,  but would have lost all information about what
554 the first one actually was.  Hence the code near the end of this function
555 to ensure there isn't already a KEY_MOUSE in the queue.
556 
557    Also,  a note about wheel handling.  Pre-Vista,  you could just say
558 "the wheel went up" or "the wheel went down".  Vista introduced the possibility
559 that the mouse motion could be a smoothly varying quantity.  So on each
560 mouse move,  we add in the amount moved,  then check to see if that's
561 enough to trigger a wheel up/down event (or possibly several).  The idea
562 is that whereas before,  each movement would be 120 units (the default),
563 you might now get a series of 40-unit moves and should emit a wheel up/down
564 event on every third move.
565 
566    (*) Things are actually slightly more complicated than this.  In general,
567 it'll just be a plain old BUTTON_CLICKED state.  But if there was another
568 BUTTON_CLICKED within the last 2 * SP->mouse_wait milliseconds,  then this
569 must be a _double_ click,  so we set the BUTTON_DOUBLE_CLICKED state.  And
570 if,  within that time frame,  there was a double or triple click,  then we
571 set the BUTTON_TRIPLE_CLICKED state.  There isn't a "quad" or higher state,
572 so if you quadruple-click the mouse,  with each click separated by less
573 than 2 * SP->mouse_wait milliseconds,  then the messages sent will be
574 BUTTON_CLICKED,  BUTTON_DOUBLE_CLICKED,  BUTTON_TRIPLE_CLICKED,  and
575 then another BUTTON_TRIPLE_CLICKED.                                     */
576 
mouse_key_already_in_queue(void)577 static bool mouse_key_already_in_queue( void)
578 {
579     int i = PDC_key_queue_low;
580 
581     while( i != PDC_key_queue_high)
582     {
583         if( PDC_key_queue[i] == KEY_MOUSE)
584         {
585             debug_printf( "Mouse key already in queue\n");
586             return( TRUE);
587         }
588         i = (i + 1) % KEY_QUEUE_SIZE;
589     }
590     return( FALSE);
591 }
592 
set_mouse(const int button_index,const int button_state,const int x,const int y)593 static int set_mouse( const int button_index, const int button_state,
594                            const int x, const int y)
595 {
596     int n_key_mouse_to_add = 1;
597     POINT pt;
598 
599                   /* If there is already a KEY_MOUSE in the queue,  we   */
600                   /* don't really want to add another one.  See above.   */
601     if( mouse_key_already_in_queue( ))
602         return( -1);
603     pt.x = x;
604     pt.y = y;
605     memset(&SP->mouse_status, 0, sizeof(MOUSE_STATUS));
606     if( button_state == BUTTON_MOVED)
607     {
608         if( button_index < 0)
609             SP->mouse_status.changes = PDC_MOUSE_POSITION;
610          else
611             SP->mouse_status.changes = PDC_MOUSE_MOVED | (1 << button_index);
612     }
613     else
614     {
615         if( button_index < PDC_MAX_MOUSE_BUTTONS)
616         {
617             SP->mouse_status.button[button_index] = (short)button_state;
618             if( button_index < 3)
619                SP->mouse_status.changes = (1 << button_index);
620             else
621                SP->mouse_status.changes = (0x40 << button_index);
622         }
623         else                      /* actually a wheel mouse movement */
624         {                         /* button_state = number of units moved */
625             static int mouse_wheel_vertical_loc = 0;
626             static int mouse_wheel_horizontal_loc = 0;
627             const int mouse_wheel_sensitivity = 120;
628 
629             n_key_mouse_to_add = 0;
630             if( button_index == VERTICAL_WHEEL_EVENT)
631             {
632                 mouse_wheel_vertical_loc += button_state;
633                 while( mouse_wheel_vertical_loc > mouse_wheel_sensitivity / 2)
634                 {
635                     n_key_mouse_to_add++;
636                     mouse_wheel_vertical_loc -= mouse_wheel_sensitivity;
637                     SP->mouse_status.changes |= PDC_MOUSE_WHEEL_UP;
638                 }
639                 while( mouse_wheel_vertical_loc < -mouse_wheel_sensitivity / 2)
640                 {
641                     n_key_mouse_to_add++;
642                     mouse_wheel_vertical_loc += mouse_wheel_sensitivity;
643                     SP->mouse_status.changes |= PDC_MOUSE_WHEEL_DOWN;
644                 }
645              }
646              else       /* must be a horizontal event: */
647             {
648                 mouse_wheel_horizontal_loc += button_state;
649                 while( mouse_wheel_horizontal_loc > mouse_wheel_sensitivity / 2)
650                 {
651                     n_key_mouse_to_add++;
652                     mouse_wheel_horizontal_loc -= mouse_wheel_sensitivity;
653                     SP->mouse_status.changes |= PDC_MOUSE_WHEEL_RIGHT;
654                 }
655                 while( mouse_wheel_horizontal_loc < -mouse_wheel_sensitivity / 2)
656                 {
657                     n_key_mouse_to_add++;
658                     mouse_wheel_horizontal_loc += mouse_wheel_sensitivity;
659                     SP->mouse_status.changes |= PDC_MOUSE_WHEEL_LEFT;
660                 }
661              }
662         }
663     }
664     SP->mouse_status.x = pt.x;
665     SP->mouse_status.y = pt.y;
666     {
667         int i, button_flags = 0;
668 
669         if( GetKeyState( VK_MENU) & 0x8000)
670             button_flags |= PDC_BUTTON_ALT;
671 
672         if( GetKeyState( VK_SHIFT) & 0x8000)
673             button_flags |= PDC_BUTTON_SHIFT;
674 
675         if( GetKeyState( VK_CONTROL) & 0x8000)
676             button_flags |= PDC_BUTTON_CONTROL;
677 
678         for (i = 0; i < PDC_MAX_MOUSE_BUTTONS; i++)
679             SP->mouse_status.button[i] |= button_flags;
680     }
681                   /* If the window is maximized,  the click may occur just */
682                   /* outside the "real" screen area.  If so,  we again     */
683                   /* don't want to add a key to the queue:                 */
684     if( SP->mouse_status.x >= PDC_n_cols || SP->mouse_status.y >= PDC_n_rows)
685         n_key_mouse_to_add = 0;
686                   /* OK,  there isn't a KEY_MOUSE already in the queue.   */
687                   /* So we'll add one (or zero or more,  for wheel mice): */
688     while( n_key_mouse_to_add--)
689         add_key_to_queue( KEY_MOUSE);
690     return( 0);
691 }
692 
693       /* The following should be #defined in 'winuser.h',  but such is */
694       /* not always the case.  The following fixes the exceptions:     */
695 #ifndef WM_MOUSEWHEEL
696     #define WM_MOUSEWHEEL                   0x020A
697 #endif
698 #ifndef WM_MOUSEHWHEEL
699     #define WM_MOUSEHWHEEL                  0x020E
700 #endif
701 #ifndef WM_XBUTTONDOWN
702     #define WM_XBUTTONDOWN                  0x020B
703     #define WM_XBUTTONUP                    0x020C
704 #endif
705 #ifndef MK_XBUTTON1
706     #define MK_XBUTTON1                     0x0020
707     #define MK_XBUTTON2                     0x0040
708 #endif
709 
710 #ifdef USE_FALLBACK_FONT
711 extern GLYPHSET *PDC_unicode_range_data;
712 #endif         /* #ifdef USE_FALLBACK_FONT */
713 
714 #define TIMER_ID_FOR_BLINKING 0x2000
715 
716 /* When first loading a font,  we use 'get_character_sizes' to briefly
717 load the (non-bold,  non-italic flavor of the) font,  get its height and
718 width,  and call GetFontUnicodeRanges to determine which characters are
719 actually available from that font.  That set of ranges is used so that,
720 when we come across characters not in the font,  we can switch to a
721 "fallback" font (Unifont,  most likely).   */
722 
get_character_sizes(const HWND hwnd,int * xchar_size,int * ychar_size)723 static void get_character_sizes( const HWND hwnd,
724                            int *xchar_size, int *ychar_size)
725 {
726     HFONT hFont = PDC_get_font_handle( 0);
727     HFONT prev_font;
728     HDC hdc = GetDC (hwnd) ;
729     TEXTMETRIC tm ;
730 #ifdef USE_FALLBACK_FONT
731     DWORD size;
732 #endif
733 
734     prev_font = SelectObject (hdc, hFont);
735     GetTextMetrics (hdc, &tm) ;
736 #ifdef USE_FALLBACK_FONT
737     assert( !PDC_unicode_range_data);
738     size = GetFontUnicodeRanges( hdc, NULL);
739     PDC_unicode_range_data = (GLYPHSET *)calloc( 1, size);
740     PDC_unicode_range_data->cbThis = size;
741     size = GetFontUnicodeRanges( hdc, PDC_unicode_range_data);
742 #endif         /* #ifdef USE_FALLBACK_FONT */
743     SelectObject( hdc, prev_font);
744     ReleaseDC (hwnd, hdc) ;
745     DeleteObject( hFont);
746     *xchar_size = tm.tmAveCharWidth ;
747     *ychar_size = tm.tmHeight;
748 }
749 
750 /* Cygwin lacks _splitpath, _wsplitpath.  THE FOLLOWING ARE NOT FULLY
751 TESTED IMPLEMENTATIONS OF THOSE TWO FUNCTIONS,  because the only use we
752 make of them is to get fname.  (Though I did write a little test program,
753 and they seem to work.) */
754 
755 #ifdef __CYGWIN__
756 #ifdef PDC_WIDE
my_wsplitpath(const wchar_t * path,wchar_t * drive,wchar_t * dir,wchar_t * fname,wchar_t * ext)757 static void my_wsplitpath( const wchar_t *path, wchar_t *drive,
758            wchar_t *dir, wchar_t *fname, wchar_t *ext)
759 {
760     size_t i, loc = 0;
761 
762     assert( path);
763     assert( fname);
764     if( path[0] && path[1] == ':')
765     {
766         if( drive)
767         {
768             drive[0] = path[0];
769             drive[1] = ':';
770             drive[2] = '\0';
771         }
772         path += 2;
773     }
774     else if( drive)
775         *drive = '\0';
776     for( i = 0; path[i]; i++)
777         if( path[i] == '/' || path[i] == '\\')
778             loc = i + 1;
779     if( dir)
780     {
781         memcpy( dir, path, loc * sizeof( wchar_t));
782         dir[loc] = '\0';
783     }
784     if( loc)
785         path += loc;
786     loc = 0;
787     while( path[loc] && path[loc] != '.')
788         loc++;
789     if( fname)
790     {
791         memcpy( fname, path, loc * sizeof( wchar_t));
792         fname[loc] = '\0';
793     }
794     if( ext)
795         wcscpy( ext, path + loc);
796 }
797 #endif            /* #ifdef PDC_WIDE */
798 
my_splitpath(const char * path,char * drive,char * dir,char * fname,char * ext)799 static void my_splitpath( const char *path, char *drive,
800            char *dir, char *fname, char *ext)
801 {
802     size_t i, loc = 0;
803 
804     assert( path);
805     assert( fname);
806     if( path[0] && path[1] == ':')
807     {
808         if( drive)
809         {
810             drive[0] = path[0];
811             drive[1] = ':';
812             drive[2] = '\0';
813         }
814         path += 2;
815     }
816     else if( drive)
817         *drive = '\0';
818     for( i = 0; path[i]; i++)
819         if( path[i] == '/' || path[i] == '\\')
820             loc = i + 1;
821     if( dir)
822     {
823         memcpy( dir, path, loc * sizeof( char));
824         dir[loc] = '\0';
825     }
826     if( loc)
827         path += loc;
828     loc = 0;
829     while( path[loc] && path[loc] != '.')
830         loc++;
831     if( fname)
832     {
833         memcpy( fname, path, loc * sizeof( char));
834         fname[loc] = '\0';
835     }
836     if( ext)
837         strcpy( ext, path + loc);
838 }
839 #else          /* non-Cygwin case : */
840    #define my_splitpath  _splitpath
841    #define my_wsplitpath _wsplitpath
842    #define GOT_ARGV_ARGC
843 #endif            /* #ifdef __CYGWIN__ */
844 
845 /* This function looks at the full command line,  which includes a fully
846 specified path to the executable and arguments;  and strips out just the
847 name of the app,  with the arguments optionally appended.  Hence,
848 
849 C:\PDCURSES\WINGUI\TESTCURS.EXE arg1 arg2
850 
851     would be reduced to 'Testcurs' (if include_args == 0) or
852 'Testcurs arg1 arg2' (if include_args == 1).  The former case is used to
853 create a (hopefully unique) registry key for the app,  so that the app's
854 specific settings (screen and font size) will be stored for the next run.
855 The latter case is used to generate a default window title.
856 
857    Unfortunately,  this code has to do some pretty strange things.  In the
858 Unicode (PDC_WIDE) case,  we really should use __wargv;  but that pointer
859 may or may not be NULL.  If it's NULL, we fall back on __argv.  In at
860 least one case,  where this code is compiled into a DLL using MinGW and
861 then used in an app compiled with MS Visual C, __argv isn't set either,
862 and we drop back to looking at GetCommandLine( ).  Which leads to a real
863 oddity:  GetCommandLine( ) may return something such as,  say,
864 
865 "C:\PDCurses\WinGUI\testcurs.exe" -lRussian
866 
867    ...which,  after being run through _splitpath or _wsplitpath,  becomes
868 
869 testcurs.exe" -lRussian
870 
871    The .exe" is removed,  and the command-line arguments shifted or removed,
872 depending on the value of include_args.  Pretty strange stuff.
873 
874    However,  if one calls Xinitscr( ) and passed command-line arguments when
875 starting this library,  those arguments will be stored in PDC_argc and
876 PDC_argv,  and will be used instead of GetCommandLine.
877 */
878 
879 #ifdef UNICODE
880    #define my_stprintf wsprintf
881    #define my_tcslen   wcslen
882 #ifdef __CYGWIN__
883                      /* Can't lowercase Unicode text in Cygwin */
884    #define my_tcslwr
885 #elif defined( _MSC_VER ) || defined( __WATCOMC__ )
886    #define my_tcslwr   _wcslwr
887 #else
888    #define my_tcslwr   wcslwr
889 #endif      /* __CYGWIN__ */
890    #define my_tcscat   wcscat
891    #define my_tcscpy   wcscpy
892    #define my_stscanf  swscanf
893 
894 #else /* UNICODE */
895 
896    #define my_stprintf sprintf
897    #define my_tcslen   strlen
898    #define my_tcslwr   strlwr
899 #ifdef _MSC_VER
900    #define strlwr     _strlwr
901 #endif
902    #define my_tcscat   strcat
903    #define my_tcscpy   strcpy
904    #define my_stscanf  sscanf
905 #endif /* UNICODE */
906 
907 
get_app_name(TCHAR * buff,const size_t buff_size,const bool include_args)908 static void get_app_name( TCHAR *buff, const size_t buff_size, const bool include_args)
909 {
910     int i;
911     size_t buff_space;
912 #ifdef GOT_ARGV_ARGC
913     int argc = (PDC_argc ? PDC_argc : __argc);
914     char **argv = (PDC_argc ? PDC_argv : __argv);
915 #else
916     int argc = PDC_argc;
917     char **argv = PDC_argv;
918 #endif
919 
920 #ifdef PDC_WIDE
921     wchar_t **wargv = __wargv;
922 #ifdef GOT_ARGV_ARGC
923     /* in case we can not access the array directly try to get it otherwise */
924     if( !wargv) {
925         wchar_t *cmd_linew = GetCommandLine( );
926         if (cmd_linew) {
927             wargv = CommandLineToArgvW (cmd_linew, &argc);
928         }
929     }
930     if( wargv)
931     {
932         my_wsplitpath( wargv[0], NULL, NULL, buff, NULL);
933         if ( include_args)
934         {
935             buff_space = buff_size - my_tcslen( buff) - 1;
936             for ( i = 1; i < argc; i++)
937             {
938                 size_t arg_len = my_tcslen( wargv[i]) + 1;
939                 if ( buff_space < arg_len) {
940                     break;
941                 }
942                 buff_space -= arg_len;
943                 wcscat( buff, L" ");
944                 wcscat( buff, wargv[i]);
945             }
946         }
947     }
948     else
949 #endif      /* #ifdef GOT_ARGV_ARGC */
950        if( argv)
951     {
952         char tbuff[MAX_PATH];
953         my_splitpath( argv[0], NULL, NULL, tbuff, NULL);
954         if ( include_args)
955         {
956             buff_space = buff_size - strlen( tbuff) - 1;
957             for ( i = 1; i < argc; i++)
958             {
959                 size_t arg_len = strlen( argv[i]) + 1;
960                 if ( buff_space < arg_len) {
961                     break;
962                 }
963                 buff_space -= arg_len;
964                 strcat( tbuff, " ");
965                 strcat( tbuff, argv[i]);
966             }
967         }
968         mbstowcs( buff, tbuff, strlen( tbuff) + 1);
969     }
970     else         /* no __argv or PDC_argv pointer available */
971     {
972         wchar_t *tptr;
973 
974         my_wsplitpath( GetCommandLine( ), NULL, NULL, buff, NULL);
975         my_tcslwr( buff + 1);
976         tptr = wcsstr( buff, L".exe\"");
977         if( tptr)
978         {
979             if( include_args)
980                 memmove( tptr, tptr + 5, wcslen( tptr + 4) * sizeof( wchar_t));
981             else
982                 *tptr = '\0';
983         }
984     }
985 #else       /* non-Unicode case */
986     if( argv)
987     {
988         my_splitpath( argv[0], NULL, NULL, buff, NULL);
989         debug_printf( "Path: %s;  exe: %s\n", argv[0], buff);
990         if ( include_args)
991         {
992             buff_space = buff_size - my_tcslen( buff) - 1;
993             for ( i = 1; i < argc; i++)
994             {
995                 size_t arg_len = my_tcslen( argv[i]) + 1;
996                 if ( buff_space < arg_len) {
997                     break;
998                 }
999                 buff_space -= arg_len;
1000                 strcat( buff, " ");
1001                 strcat( buff, argv[i]);
1002             }
1003         }
1004     }
1005     else         /* no __argv pointer available */
1006     {
1007         char *tptr;
1008 
1009         my_splitpath( GetCommandLine( ), NULL, NULL, buff, NULL);
1010         strlwr( buff + 1);
1011         tptr = strstr( buff, ".exe\"");
1012         if( tptr)
1013         {
1014             if( include_args)
1015                 memmove( tptr, tptr + 5, strlen( tptr + 4));
1016             else
1017                 *tptr = '\0';
1018         }
1019     }
1020 #endif
1021     my_tcslwr( buff + 1);
1022 }
1023 
1024 /* Ensure compatibility with old compilers that don't support 64-bit targets. */
1025 #if !defined(_BASETSD_H_) && !defined(_BASETSD_H)
1026 #define LONG_PTR LONG
1027 #endif
1028 
get_app_icon_callback(HMODULE hModule,LPCTSTR lpszType,LPTSTR lpszName,LONG_PTR lParam)1029 static BOOL CALLBACK get_app_icon_callback(HMODULE hModule, LPCTSTR lpszType,
1030                                            LPTSTR lpszName, LONG_PTR lParam)
1031 {
1032     *((HICON *) lParam) = LoadIcon(hModule, lpszName);
1033     return FALSE; /* stop enumeration after first icon */
1034 }
1035 
1036 /* This function extracts the first icon from the executable that is
1037 executing this DLL */
1038 
get_app_icon(HANDLE hModule)1039 INLINE HICON get_app_icon( HANDLE hModule)
1040 {
1041     HICON icon = NULL;
1042     EnumResourceNames(hModule, RT_GROUP_ICON,
1043                       get_app_icon_callback, (LONG_PTR) &icon);
1044     return icon;
1045 }
1046 
1047 extern TCHAR PDC_font_name[];
1048 
1049 /* This flavor of Curses tries to store the window and font sizes on
1050 an app-by-app basis.  To do this,  it uses the above get_app_name( )
1051 function,  then sets or gets a corresponding value from the Windoze
1052 registry.  The benefit should be that one can have one screen size/font
1053 for,  say,  Testcurs,  while having different settings for,  say,  Firework
1054 or Rain or one's own programs.  */
1055 
set_default_sizes_from_registry(const int n_cols,const int n_rows,const int xloc,const int yloc,const int menu_shown)1056 INLINE int set_default_sizes_from_registry( const int n_cols, const int n_rows,
1057                const int xloc, const int yloc, const int menu_shown)
1058 {
1059     DWORD is_new_key;
1060     HKEY hNewKey;
1061     long rval = RegCreateKeyEx( HKEY_CURRENT_USER, _T( "SOFTWARE\\PDCurses"),
1062                             0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
1063                             0, &hNewKey, &is_new_key);
1064 
1065     if( rval == ERROR_SUCCESS)
1066     {
1067         TCHAR buff[180];
1068         TCHAR key_name[MAX_PATH];
1069         extern int PDC_font_size;
1070 
1071         if( IsZoomed( PDC_hWnd))    /* -1x-1 indicates a maximized window */
1072             my_stprintf( buff,
1073                       _T( "-1x-1,%d,0,0,%d"), PDC_font_size, menu_shown);
1074         else
1075             my_stprintf( buff,
1076                       _T( "%dx%d,%d,%d,%d,%d"), n_cols, n_rows, PDC_font_size,
1077                        xloc, yloc, menu_shown);
1078         my_stprintf( buff + my_tcslen( buff),
1079                   _T(";%d,%d,%d,%d:"),
1080                   min_lines, max_lines,
1081                   min_cols, max_cols);
1082         my_tcscat( buff, PDC_font_name);
1083 
1084         get_app_name( key_name, MAX_PATH, FALSE);
1085         rval = RegSetValueEx( hNewKey, key_name, 0, REG_SZ,
1086                        (BYTE *)buff, (DWORD)( my_tcslen( buff) * sizeof( TCHAR)));
1087         RegCloseKey( hNewKey);
1088     }
1089     debug_printf( "Size: %d %d; %d\n", n_cols, n_rows, rval);
1090     return( rval != ERROR_SUCCESS);
1091 }
1092 
1093 /* If the window is maximized,  there will usually be a fractional
1094 character width at the right and bottom edges.  The following code fills
1095 that in with a black brush.  It takes the "paint rectangle",  the area
1096 passed with a WM_PAINT message that specifies what chunk of the client
1097 area needs to be redrawn.
1098 
1099     If the window is _not_ maximized,  this shouldn't happen;  the window
1100 width/height should always be an integral multiple of the character
1101 width/height,  with no slivers at the right and bottom edges. */
1102 
fix_up_edges(const HDC hdc,const RECT * rect)1103 static void fix_up_edges( const HDC hdc, const RECT *rect)
1104 {
1105     const int x = PDC_n_cols * PDC_cxChar;
1106     const int y = PDC_n_rows * PDC_cyChar;
1107 
1108     if( rect->right >= x || rect->bottom >= y)
1109     {
1110         const HBRUSH hOldBrush =
1111                       SelectObject( hdc, GetStockObject( BLACK_BRUSH));
1112 
1113         SelectObject( hdc, GetStockObject( NULL_PEN));
1114         if( rect->right >= x)
1115            Rectangle( hdc, x, rect->top, rect->right + 1, rect->bottom + 1);
1116         if( rect->bottom >= y)
1117            Rectangle( hdc, rect->left, y, rect->right + 1, rect->bottom + 1);
1118         SelectObject( hdc, hOldBrush);
1119     }
1120 }
1121 
adjust_font_size(const int font_size_change)1122 static void adjust_font_size( const int font_size_change)
1123 {
1124     extern int PDC_font_size;
1125 
1126     PDC_font_size += font_size_change;
1127     if( PDC_font_size < 2)
1128         PDC_font_size = 2;
1129     PDC_transform_line( 0, 0, 0, NULL);    /* free any fonts  */
1130     get_character_sizes( PDC_hWnd, &PDC_cxChar, &PDC_cyChar);
1131           /* When the font size changes,  do we want to keep  */
1132           /* the window the same size (except to remove any   */
1133           /* fractional character)?  Or do we keep the number */
1134           /* of rows/columns the same?  For the nonce,  I'm   */
1135           /* keeping the window size fixed if the window is   */
1136           /* maximized,  but keeping the number of rows/lines */
1137           /* fixed if it's windowed.  That's my opinion.  If  */
1138           /* you disagree,  I have others.                    */
1139     if( IsZoomed( PDC_hWnd))
1140     {
1141         RECT client_rect;
1142         HDC hdc;
1143 
1144         GetClientRect( PDC_hWnd, &client_rect);
1145         PDC_n_rows = client_rect.bottom / PDC_cyChar;
1146         PDC_n_cols = client_rect.right / PDC_cxChar;
1147         keep_size_within_bounds( &PDC_n_rows, &PDC_n_cols);
1148         PDC_resize_screen( PDC_n_rows, PDC_n_cols);
1149         add_key_to_queue( KEY_RESIZE);
1150         SP->resized = TRUE;
1151         hdc = GetDC (PDC_hWnd) ;
1152         GetClientRect( PDC_hWnd, &client_rect);
1153         fix_up_edges( hdc, &client_rect);
1154         ReleaseDC( PDC_hWnd, hdc) ;
1155     }
1156     else
1157     {
1158         PDC_resize_screen( PDC_n_rows, PDC_n_cols);
1159         InvalidateRect( PDC_hWnd, NULL, FALSE);
1160     }
1161 }
1162 
1163 #define WM_ENLARGE_FONT       (WM_USER + 1)
1164 #define WM_SHRINK_FONT        (WM_USER + 2)
1165 #define WM_MARK_AND_COPY      (WM_USER + 3)
1166 #define WM_TOGGLE_MENU        (WM_USER + 4)
1167 #define WM_EXIT_GRACELESSLY   (WM_USER + 5)
1168 #define WM_CHOOSE_FONT        (WM_USER + 6)
1169 
1170 static int add_resize_key = 1;
1171 static int resize_limits_set = 0;
1172 
1173 /*man-start**************************************************************
1174 
1175 Resize limits
1176 -------------
1177 
1178 ### Synopsis
1179 
1180     void PDC_set_resize_limits( const int new_min_lines,
1181                                 const int new_max_lines,
1182                                 const int new_min_cols,
1183                                 const int new_max_cols);
1184 
1185 ### Description
1186 
1187    For platforms supporting resizable windows (SDLx, WinGUI, X11).  Some
1188    programs may be unprepared for a resize event;  for these,  calling
1189    this function with the max and min limits equal ensures that no
1190    user resizing can be done.  Other programs may require at least a
1191    certain number,  and/or no more than a certain number,  of columns
1192    and/or lines.
1193 
1194 ### Portability
1195 
1196    PDCurses-only function.
1197 
1198 **man-end****************************************************************/
1199 
PDC_set_resize_limits(const int new_min_lines,const int new_max_lines,const int new_min_cols,const int new_max_cols)1200 void PDC_set_resize_limits( const int new_min_lines, const int new_max_lines,
1201                   const int new_min_cols, const int new_max_cols)
1202 {
1203     min_lines = max( new_min_lines, 2);
1204     max_lines = max( new_max_lines, min_lines);
1205     min_cols = max( new_min_cols, 2);
1206     max_cols = max( new_max_cols, min_cols);
1207     resize_limits_set = 1;
1208 }
1209 
1210       /* The screen should hold the characters (PDC_cxChar * n_default_columns */
1211       /* pixels wide,  similarly high).  In width,  we need two frame widths,  */
1212       /* one on each side.  Vertically,  we need two frame heights,  plus room */
1213       /* for the application title and the menu.  */
1214 
adjust_window_size(int * xpixels,int * ypixels,int window_style,const int menu_shown)1215 static void adjust_window_size( int *xpixels, int *ypixels, int window_style,
1216                const int menu_shown)
1217 {
1218     RECT rect;
1219 
1220     rect.left = rect.top = 0;
1221     rect.right = *xpixels;
1222     rect.bottom = *ypixels;
1223 /*  printf( "Adjusting to %d, %d\n", *xpixels, *ypixels); */
1224     AdjustWindowRect( &rect, window_style, menu_shown);
1225     *xpixels = rect.right - rect.left;
1226     *ypixels = rect.bottom - rect.top;
1227 }
1228 
keep_size_within_bounds(int * lines,int * cols)1229 static int keep_size_within_bounds( int *lines, int *cols)
1230 {
1231     int rval = 0;
1232 
1233     if( *lines < min_lines)
1234     {
1235         *lines = min_lines;
1236         rval = 1;
1237     }
1238     else if( *lines > max_lines)
1239     {
1240         *lines = max_lines;
1241         rval = 2;
1242     }
1243     if( *cols < min_cols)
1244     {
1245         *cols = min_cols;
1246         rval |= 4;
1247     }
1248     else if( *cols > max_cols)
1249     {
1250         *cols = max_cols;
1251         rval |= 8;
1252     }
1253     return( rval);
1254 }
1255 
get_default_sizes_from_registry(int * n_cols,int * n_rows,int * xloc,int * yloc,int * menu_shown)1256 INLINE int get_default_sizes_from_registry( int *n_cols, int *n_rows,
1257                                      int *xloc, int *yloc, int *menu_shown)
1258 {
1259     TCHAR data[100];
1260     DWORD size_out = sizeof( data);
1261     HKEY hKey = 0;
1262     long rval = RegOpenKeyEx( HKEY_CURRENT_USER, _T( "SOFTWARE\\PDCurses"),
1263                         0, KEY_READ, &hKey);
1264 
1265     if( !hKey)
1266         return( 1);
1267     if( rval == ERROR_SUCCESS)
1268     {
1269         TCHAR key_name[MAX_PATH];
1270 
1271         get_app_name( key_name, MAX_PATH, FALSE);
1272         rval = RegQueryValueEx( hKey, key_name,
1273                         NULL, NULL, (BYTE *)data, &size_out);
1274         if( rval == ERROR_SUCCESS)
1275         {
1276             extern int PDC_font_size;
1277             int x = -1, y = -1, bytes_read = 0;
1278             int minl = 0, maxl = 0, minc = 0, maxc = 0;
1279 
1280             my_stscanf( data, _T( "%dx%d,%d,%d,%d,%d;%d,%d,%d,%d:%n"),
1281                              &x, &y, &PDC_font_size,
1282                              xloc, yloc, menu_shown,
1283                              &minl, &maxl,
1284                              &minc, &maxc,
1285                              &bytes_read);
1286             if( bytes_read > 0 && data[bytes_read - 1] == ':')
1287                my_tcscpy( PDC_font_name, data + bytes_read);
1288             if ( !resize_limits_set)
1289                 PDC_set_resize_limits(minl, maxl, minc, maxc);
1290             if( n_cols)
1291                 *n_cols = x;
1292             if( n_rows)
1293                 *n_rows = y;
1294             if( *n_cols > 0 && *n_rows > 0)   /* i.e.,  not maximized */
1295                 keep_size_within_bounds( n_rows, n_cols);
1296         }
1297         RegCloseKey( hKey);
1298     }
1299     if( rval != ERROR_SUCCESS)
1300         debug_printf( "get_default_sizes_from_registry error: %d\n", rval);
1301     return( rval != ERROR_SUCCESS);
1302 }
1303 
1304 /* Ensure that the dragged rectangle    */
1305 /* is an even multiple of the char size */
HandleSizing(WPARAM wParam,LPARAM lParam)1306 INLINE void HandleSizing( WPARAM wParam, LPARAM lParam )
1307 {
1308     RECT *rect = (RECT *)lParam;
1309     RECT window_rect, client_rect;
1310     int hadd, vadd, width, height;
1311     int n_rows, n_cols;
1312     int rounded_width, rounded_height;
1313 
1314     GetWindowRect( PDC_hWnd, &window_rect);
1315     GetClientRect( PDC_hWnd, &client_rect);
1316     hadd = (window_rect.right - window_rect.left) - client_rect.right;
1317     vadd = (window_rect.bottom - window_rect.top) - client_rect.bottom;
1318     width = rect->right - rect->left - hadd;
1319     height = rect->bottom - rect->top - vadd;
1320 
1321     n_cols = (width + PDC_cxChar / 2) / PDC_cxChar;
1322     n_rows = (height + PDC_cyChar / 2) / PDC_cyChar;
1323     keep_size_within_bounds( &n_rows, &n_cols);
1324 
1325     rounded_width = hadd + n_cols * PDC_cxChar;
1326     rounded_height = vadd + n_rows * PDC_cyChar;
1327 
1328     if( wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT
1329                               || wParam == WMSZ_BOTTOMRIGHT)
1330         rect->bottom = rect->top + rounded_height;
1331     if( wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT
1332                            || wParam == WMSZ_TOPRIGHT)
1333         rect->top = rect->bottom - rounded_height;
1334     if( wParam == WMSZ_RIGHT || wParam == WMSZ_BOTTOMRIGHT
1335                              || wParam == WMSZ_TOPRIGHT)
1336         rect->right = rect->left + rounded_width;
1337     if( wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT
1338                             || wParam == WMSZ_TOPLEFT)
1339         rect->left = rect->right - rounded_width;
1340 }
1341 
1342 /* Under Wine,  it appears that the code to force the window size to be
1343 an integral number of columns and rows doesn't work.  This is because
1344 WM_SIZING messages aren't sent (this is apparently fixed as of Wine 1.7.18,
1345 though I've not tried it yet;  I'm still on Wine 1.6,  the stable branch.)
1346 You can therefore end up in a loop where the code keeps trying to resize a
1347 window that isn't actually resizing.  So,  _when running in Wine only_,
1348 we want that code not to be executed... which means having to figure out:
1349 are we running under Wine?  Which means that when PDCurses/WinGUI is
1350 initialized,  we set the following 'wine_version' pointer.  One could
1351 actually call wine_version(),  if not NULL,  to get the current Wine
1352 version.      */
1353 
1354 typedef const char *(CDECL *wine_version_func)(void);
1355 
1356 static wine_version_func wine_version;
1357 
HandleSize(const WPARAM wParam,const LPARAM lParam)1358 static void HandleSize( const WPARAM wParam, const LPARAM lParam)
1359 {
1360     static WPARAM prev_wParam = (WPARAM)-99;
1361     const unsigned n_xpixels = LOWORD (lParam);
1362     const unsigned n_ypixels = HIWORD (lParam);
1363     unsigned new_n_rows, new_n_cols;
1364 
1365     debug_printf( "WM_SIZE: wParam %x %d %d %d\n", (unsigned)wParam,
1366                   n_xpixels, n_ypixels, SP->resized);
1367 /*  if( wine_version)
1368         printf( "Wine version: %s\n", wine_version( ));  */
1369 
1370 
1371     if( wParam == SIZE_MINIMIZED )
1372     {
1373         prev_wParam = SIZE_MINIMIZED;
1374         return;
1375     }
1376     new_n_rows = n_ypixels / PDC_cyChar;
1377     new_n_cols = n_xpixels / PDC_cxChar;
1378     debug_printf( "Size was %d x %d; will be %d x %d\n",
1379                 PDC_n_rows, PDC_n_cols, new_n_rows, new_n_cols);
1380     SP->resized = FALSE;
1381 
1382     /* If the window will have a different number of rows */
1383     /* or columns,  we put KEY_RESIZE in the key queue.   */
1384     /* We don't do this if */
1385     /* the resizing is the result of the window being    */
1386     /* initialized,  or as a result of PDC_resize_screen */
1387     /* being called.  In the latter case,  the user      */
1388     /* presumably already knows the screen's been resized. */
1389     if( PDC_n_rows != (int)new_n_rows || PDC_n_cols != (int)new_n_cols)
1390     {
1391         PDC_n_cols = new_n_cols;
1392         PDC_n_rows = new_n_rows;
1393         debug_printf( "prev_wParam = %d; add_resize_key = %d\n",
1394                       (int)prev_wParam, add_resize_key);
1395         if( prev_wParam != (WPARAM)-99 && add_resize_key)
1396         {
1397             /* don't add a key when the window is initialized */
1398             add_key_to_queue( KEY_RESIZE);
1399             SP->resized = TRUE;
1400         }
1401     }
1402     else if( wine_version)
1403         return;
1404 
1405     add_resize_key = 1;
1406     if( wParam == SIZE_RESTORED &&
1407         ( n_xpixels % PDC_cxChar || n_ypixels % PDC_cyChar))
1408     {
1409         int new_xpixels = PDC_cxChar * PDC_n_cols;
1410         int new_ypixels = PDC_cyChar * PDC_n_rows;
1411 
1412         adjust_window_size( &new_xpixels, &new_ypixels,
1413                             GetWindowLong( PDC_hWnd, GWL_STYLE), menu_shown);
1414         debug_printf( "Irregular size\n");
1415         SetWindowPos( PDC_hWnd, 0, 0, 0,
1416                       new_xpixels, new_ypixels,
1417                       SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
1418 
1419     }
1420 
1421     /* If the window has been restored from minimized form, */
1422     /* we should repaint.  Otherwise,  don't.               */
1423     prev_wParam = wParam;
1424 }
1425 
HandleMouseMove(WPARAM wParam,LPARAM lParam)1426 static int HandleMouseMove( WPARAM wParam, LPARAM lParam)
1427 {
1428     const int mouse_x = LOWORD( lParam) / PDC_cxChar;
1429     const int mouse_y = HIWORD( lParam) / PDC_cyChar;
1430 
1431     return( add_mouse( 0, BUTTON_MOVED, mouse_x, mouse_y) != -1);
1432 }
1433 
HandlePaint(HWND hwnd)1434 static void HandlePaint( HWND hwnd )
1435 {
1436     PAINTSTRUCT ps;
1437     HDC hdc;
1438     RECT rect;
1439 
1440     GetUpdateRect( hwnd, &rect, FALSE);
1441 /*  printf( "In HandlePaint: %ld %ld, %ld %ld\n",
1442                rect.left, rect.top, rect.right, rect.bottom); */
1443 
1444     hdc = BeginPaint( hwnd, &ps);
1445 
1446     fix_up_edges( hdc, &rect);
1447 
1448     if( curscr && curscr->_y)
1449     {
1450         int i, x1, n_chars;
1451 
1452         x1 = rect.left / PDC_cxChar;
1453         n_chars = rect.right / PDC_cxChar - x1 + 1;
1454         if( n_chars > SP->cols - x1)
1455             n_chars = SP->cols - x1;
1456         if( n_chars > 0)
1457         for( i = rect.top / PDC_cyChar; i <= rect.bottom / PDC_cyChar; i++)
1458              if( i < SP->lines && curscr->_y[i])
1459                  PDC_transform_line_given_hdc( hdc, i, x1,
1460                                                n_chars, curscr->_y[i] + x1);
1461     }
1462     EndPaint( hwnd, &ps );
1463 }
1464 
1465 static bool key_already_handled = FALSE;
1466 static int last_key_handled = 0;
1467 
HandleSyskeyDown(const WPARAM wParam,const LPARAM lParam,int * ptr_modified_key_to_return)1468 static void HandleSyskeyDown( const WPARAM wParam, const LPARAM lParam,
1469                int *ptr_modified_key_to_return )
1470 {
1471     const int shift_pressed = (GetKeyState( VK_SHIFT) & 0x8000);
1472     const int ctrl_pressed = (GetKeyState( VK_CONTROL) & 0x8000);
1473     const int alt_pressed = (GetKeyState( VK_MENU) & 0x8000);
1474     const int extended = ((lParam & 0x01000000) != 0);
1475     const int repeated = (int)( lParam >> 30) & 1;
1476     const KPTAB *kptr = kptab + wParam;
1477     int key = 0;
1478     static int repeat_count;
1479 
1480     if( !repeated)
1481         *ptr_modified_key_to_return = 0;
1482 
1483     if( repeated)
1484         repeat_count++;
1485     else
1486         repeat_count = 0;
1487     if( SP->return_key_modifiers && !repeated)
1488     {                     /* See notes above this function */
1489         if( wParam == VK_SHIFT)
1490         {
1491             if( GetKeyState( VK_LSHIFT) & 0x8000)
1492                 *ptr_modified_key_to_return = KEY_SHIFT_L;
1493             else if( GetKeyState( VK_RSHIFT) & 0x8000)
1494                 *ptr_modified_key_to_return = KEY_SHIFT_R;
1495             else if(( HIWORD( lParam) & 0xff) == 0x36)
1496                 *ptr_modified_key_to_return = KEY_SHIFT_R;
1497             else
1498                 *ptr_modified_key_to_return = KEY_SHIFT_L;
1499         }
1500         if( wParam == VK_CONTROL)
1501             *ptr_modified_key_to_return =
1502                                  (extended ? KEY_CONTROL_R : KEY_CONTROL_L);
1503         if( wParam == VK_MENU)
1504             *ptr_modified_key_to_return =
1505                                  (extended ? KEY_ALT_R : KEY_ALT_L);
1506     }
1507 
1508     if( !key)           /* it's not a shift, ctl, alt handled above */
1509     {
1510         if( extended && kptr->extended != 999)
1511             kptr = ext_kptab + kptr->extended;
1512 
1513         if( alt_pressed)
1514             key = kptr->alt;
1515         else if( ctrl_pressed)
1516             key = kptr->control;
1517         else if( shift_pressed)
1518             key = kptr->shift;
1519         else
1520             key = kptr->normal;
1521     }
1522 
1523     /* On non-US keyboards,  people hit Alt-Gr ("Alt-Ctrl" to */
1524     /* those on US keyboards) to get characters not otherwise */
1525     /* available:  accented characters,  local currency symbols, */
1526     /* etc.  So we default to suppressing Alt-Ctrl-letter combos.     */
1527     /* However,  apps can set PDC_show_ctrl_alts if they know they're */
1528     /* running on a US keyboard layout (or other layout that doesn't  */
1529     /* make special use of Ctrl-Alt... for example,  I use the Dvorak */
1530     /* layout;  it's fine with PDC_show_ctrl_alts = 1.)               */
1531     if( key >= KEY_MIN && key <= KEY_MAX)
1532         if( !ctrl_pressed || !alt_pressed || PDC_show_ctrl_alts)
1533             {
1534             add_key_to_queue( key);
1535             if (wParam == VK_MULTIPLY || wParam == VK_DIVIDE
1536                 || wParam == VK_ADD || wParam == VK_SUBTRACT
1537                 || wParam == VK_RETURN) {
1538                     last_key_handled = key;
1539                     key_already_handled = TRUE;
1540                 }
1541             }
1542     SP->key_modifiers = 0;
1543     /* Save the key modifiers if required. Do this first to allow to
1544        detect e.g. a pressed CTRL key after a hit of NUMLOCK. */
1545 
1546     if( alt_pressed)
1547         SP->key_modifiers |= PDC_KEY_MODIFIER_ALT;
1548 
1549     if( shift_pressed)
1550         SP->key_modifiers |= PDC_KEY_MODIFIER_SHIFT;
1551 
1552     if( ctrl_pressed)
1553         SP->key_modifiers |= PDC_KEY_MODIFIER_CONTROL;
1554 
1555     if( GetKeyState( VK_NUMLOCK) & 1)
1556         SP->key_modifiers |= PDC_KEY_MODIFIER_NUMLOCK;
1557 
1558     if( repeat_count)
1559         SP->key_modifiers |= PDC_KEY_MODIFIER_REPEAT;
1560 }
1561 
1562 /* Blinking text is supposed to blink twice a second.  Therefore,
1563 HandleTimer( ) is called every .5 seconds.
1564 
1565    In truth,  it's not so much 'blinking' as 'changing certain types of
1566 text' that happens.  If text is really blinking (i.e.,  PDC_set_blink(TRUE)
1567 has been called),  we need to flip that text.  Or if text _was_ blinking
1568 and we've just called PDC_set_blink(FALSE),  all that text has to be
1569 redrawn in 'standout' mode.  Also,  if PDC_set_line_color() has been
1570 called,  all text with left/right/under/over/strikeout lines needs to
1571 be redrawn.
1572 
1573    Also,  if we've switched from rendering bold text using a bold
1574 font to showing it in intensified color,  or vice versa,  then all
1575 bold text needs to be redrawn.
1576 
1577    So.  After determining which attributes require redrawing (if any),
1578 we run through all of 'curscr' and look for text with those attributes
1579 set.  If we find such text,  we run it through PDC_transform_line.
1580 (To speed matters up slightly,  we skip over text at the start and end
1581 of each line that lacks the desired attributes. We could conceivably
1582 get more clever;  as it stands,  if the very first and very last
1583 characters are blinking,  we redraw the entire line,  even though
1584 everything in between may not require it.  But it would probably be a
1585 lot of code for little benefit.)
1586 
1587    Note that by default,  we'll usually find that the line color hasn't
1588 changed and the 'blink mode' is still FALSE.  In that case,  attr_to_seek
1589 will be zero and the only thing we'll do here is to blink the cursor. */
1590 
HandleTimer(const WPARAM wParam)1591 static void HandleTimer( const WPARAM wParam )
1592 {
1593     int i;           /* see WndProc() notes */
1594     static attr_t prev_termattrs;
1595     static int prev_line_color = -1;
1596     chtype attr_to_seek = 0;
1597 
1598     if( prev_line_color != SP->line_color)
1599         attr_to_seek = A_ALL_LINES;
1600     if( (SP->termattrs | prev_termattrs) & A_BLINK)
1601         attr_to_seek |= A_BLINK;
1602     if( (SP->termattrs ^ prev_termattrs) & A_BOLD)
1603         attr_to_seek |= A_BOLD;
1604     prev_line_color = SP->line_color;
1605     prev_termattrs = SP->termattrs;
1606     PDC_blink_state ^= 1;
1607     if( attr_to_seek)
1608     {
1609         for( i = 0; i < SP->lines; i++)
1610         {
1611             if( curscr->_y[i])
1612             {
1613                 int j = 0, n_chars;
1614                 chtype *line = curscr->_y[i];
1615 
1616                 /* skip over starting text that isn't blinking: */
1617                 while( j < SP->cols && !(*line & attr_to_seek))
1618                 {
1619                     j++;
1620                     line++;
1621                 }
1622                 n_chars = SP->cols - j;
1623                 /* then skip over text at the end that's not blinking: */
1624                 while( n_chars && !(line[n_chars - 1] & attr_to_seek))
1625                 {
1626                     n_chars--;
1627                 }
1628                 if( n_chars)
1629                     PDC_transform_line( i, j, n_chars, line);
1630             }
1631 /*          else
1632                 MessageBox( 0, "NULL _y[] found\n", "PDCurses", MB_OK);  */
1633         }
1634     }
1635     if( SP->cursrow >=SP->lines || SP->curscol >= SP->cols
1636         || SP->cursrow < 0 || SP->curscol < 0
1637         || !curscr->_y || !curscr->_y[SP->cursrow])
1638             debug_printf( "Cursor off-screen: %d %d, %d %d\n",
1639                           SP->cursrow, SP->curscol, SP->lines, SP->cols);
1640     else if( PDC_CURSOR_IS_BLINKING)
1641              PDC_transform_line( SP->cursrow, SP->curscol, 1,
1642                                  curscr->_y[SP->cursrow] + SP->curscol);
1643 }
1644 
1645       /* Options to enlarge/shrink the font are currently commented out. */
1646 
set_menu(void)1647 static HMENU set_menu( void)
1648 {
1649     const HMENU hMenu = CreateMenu( );
1650 #ifdef PDC_WIDE
1651     AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, L"Font");
1652 #else
1653     AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, "Font");
1654 #endif
1655     return( hMenu);
1656 }
1657 
HandleMenuToggle(bool * ptr_ignore_resize)1658 INLINE void HandleMenuToggle( bool *ptr_ignore_resize)
1659 {
1660     const bool is_zoomed = IsZoomed( PDC_hWnd);
1661     HMENU hMenu;
1662 
1663     menu_shown ^= 1;
1664     hMenu = GetSystemMenu( PDC_hWnd, FALSE);
1665     CheckMenuItem( hMenu, WM_TOGGLE_MENU, MF_BYCOMMAND
1666                    | (menu_shown ? MF_CHECKED : MF_UNCHECKED));
1667 
1668     if( !is_zoomed)
1669         *ptr_ignore_resize = TRUE;
1670     if( !menu_shown)
1671     {
1672         hMenu = GetMenu( PDC_hWnd);   /* destroy existing menu */
1673         DestroyMenu( hMenu);
1674         hMenu = CreateMenu( );        /* then set an empty menu */
1675         SetMenu( PDC_hWnd, hMenu);
1676     }
1677     else
1678     {
1679         SetMenu( PDC_hWnd, set_menu( ));
1680     }
1681     *ptr_ignore_resize = FALSE;
1682 
1683     if( !is_zoomed)
1684     {
1685         PDC_resize_screen( PDC_n_rows, PDC_n_cols );
1686     }
1687 
1688     InvalidateRect( PDC_hWnd, NULL, FALSE);
1689 }
1690 
milliseconds_since_1970(void)1691 INLINE uint64_t milliseconds_since_1970( void)
1692 {
1693    FILETIME ft;
1694    const uint64_t jd_1601 = 2305813;  /* actually 2305813.5 */
1695    const uint64_t jd_1970 = 2440587;  /* actually 2440587.5 */
1696    const uint64_t ten_million = 10000000;
1697    const uint64_t diff = (jd_1970 - jd_1601) * ten_million * 86400;
1698    uint64_t decimicroseconds_since_1970;   /* i.e.,  time in units of 1e-7 seconds */
1699 
1700    GetSystemTimeAsFileTime( &ft);
1701    decimicroseconds_since_1970 = ((uint64_t)ft.dwLowDateTime |
1702                                 ((uint64_t)ft.dwHighDateTime << 32)) - diff;
1703    return( decimicroseconds_since_1970 / 10000);
1704 }
1705 
1706 typedef struct
1707 {
1708    int x, y;
1709    int button, action;
1710 } PDC_mouse_event;
1711 
add_mouse(int button,const int action,const int x,const int y)1712 static int add_mouse( int button, const int action, const int x, const int y)
1713 {
1714    static int n = 0;
1715    static PDC_mouse_event e[10];
1716    static uint64_t prev_t = 0;
1717    const uint64_t curr_t = milliseconds_since_1970( );
1718    const int timing_slop = 20;
1719    bool within_timeout = (curr_t < prev_t + SP->mouse_wait + timing_slop);
1720    static int mouse_state = 0;
1721    static int prev_x, prev_y = -1;
1722    const bool actually_moved = (x != prev_x || y != prev_y);
1723 
1724    if( action == BUTTON_MOVED && mouse_key_already_in_queue( ))
1725        return( 0);
1726    if( action == BUTTON_PRESSED)
1727        mouse_state |= (1 << button);
1728    else if( action == BUTTON_RELEASED)
1729        mouse_state &= ~(1 << button);
1730    if( button >= 0)
1731    {
1732       prev_x = x;
1733       prev_y = y;
1734    }
1735    if( action == BUTTON_MOVED)
1736    {
1737        int i;
1738        bool report_this_move = FALSE;
1739 
1740        if( !actually_moved)     /* have to move to a new character cell, */
1741            return( -1);         /* not just a new pixel */
1742        button = -1;        /* assume no buttons down */
1743        for( i = 0; i < 9; i++)
1744            if( (mouse_state >> i) & 1)
1745                button = i;
1746        if( button == -1 && !(SP->_trap_mbe & REPORT_MOUSE_POSITION))
1747            return( -1);
1748        if(        (button == 1 && (SP->_trap_mbe & BUTTON1_MOVED))
1749                || (button == 2 && (SP->_trap_mbe & BUTTON2_MOVED))
1750                || (button == 3 && (SP->_trap_mbe & BUTTON3_MOVED)))
1751            report_this_move = TRUE;
1752        else if( SP->_trap_mbe & REPORT_MOUSE_POSITION)
1753            {
1754            report_this_move = TRUE;
1755            button = 0;
1756            }
1757        debug_printf( "Move button %d, (%d %d) : %d\n", button, x, y, report_this_move);
1758        if( !report_this_move)
1759            return( -1);
1760    }
1761 
1762    if( !within_timeout || action == BUTTON_MOVED)
1763        while( n && !set_mouse( e->button - 1, e->action, e->x, e->y))
1764        {
1765            n--;
1766            memmove( e, e + 1, n * sizeof( PDC_mouse_event));
1767        }
1768    if( action == BUTTON_MOVED)
1769        if( !set_mouse( button - 1, action, x, y))
1770            return( n);
1771    if( button < 0 && action != BUTTON_MOVED)
1772        return( n);         /* we're just checking for timed-out events */
1773    debug_printf( "Button %d, act %d, dt %ld : n %d\n", button, action,
1774                   (long)( curr_t - prev_t), n);
1775    e[n].button = button;
1776    e[n].action = action;
1777    e[n].x = x;
1778    e[n].y = y;
1779    if( n)
1780    {
1781        int merged_act = 0;
1782 
1783        do
1784        {
1785            if( e[n - 1].button == e[n].button)
1786            {
1787                if( e[n - 1].action == BUTTON_PRESSED && e[n].action == BUTTON_RELEASED)
1788                    merged_act = BUTTON_CLICKED;
1789                else if( e[n - 1].action == BUTTON_CLICKED && e[n].action == BUTTON_CLICKED)
1790                    merged_act = BUTTON_DOUBLE_CLICKED;
1791                else if( e[n - 1].action == BUTTON_DOUBLE_CLICKED && e[n].action == BUTTON_CLICKED)
1792                    merged_act = BUTTON_TRIPLE_CLICKED;
1793                if( merged_act)
1794                {
1795                    n--;
1796                    e[n].action = merged_act;
1797                }
1798            }
1799        }  while( n && merged_act);
1800    }
1801    prev_t = curr_t;
1802    n++;
1803    return( n);
1804 }
1805 
1806 /* Note that there are two types of WM_TIMER timer messages.  One type
1807 indicates that SP->mouse_wait milliseconds have elapsed since a mouse
1808 button was pressed;  that's handled as described in the above notes.
1809 The other type,  issued every half second,  indicates that blinking
1810 should take place.  For these,  HandleTimer() is called (see above).
1811 
1812    On WM_PAINT,  we determine what parts of 'curscr' would be covered by
1813 the update rectangle,  and run those through PDC_transform_line.
1814 
1815    For determining left/right shift, alt,  and control,  I borrowed code
1816 from SDL.  Note that the Win32 version of PDCurses doesn't work correctly
1817 here for Win9x;  it just does GetKeyState( VK_LSHIFT),  etc.,  which is
1818 apparently not supported in Win9x.  So no matter which shift (or alt or
1819 Ctrl key) is hit,  the right-hand variant is returned in that library.
1820 The SDL handling,  and hence the handling below,  _does_ work on Win9x.
1821 Note,  though,  that in Win9x,  detection of the Shift keys is hardware
1822 dependent;  if you've an unusual keyboard,  both Shift keys may be
1823 detected as right, or both as left. */
1824 
1825 #if defined(_WIN32) && defined(__GNUC__)
1826 #define ALIGN_STACK __attribute__((force_align_arg_pointer))
1827 #else
1828 #define ALIGN_STACK
1829 #endif
1830 
WndProc(const HWND hwnd,const UINT message,const WPARAM wParam,const LPARAM lParam)1831 static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd,
1832                           const UINT message,
1833                           const WPARAM wParam,
1834                           const LPARAM lParam)
1835 {
1836     static int xbutton_pressed = 0;
1837     static int modified_key_to_return = 0;
1838     static bool ignore_resize = FALSE;
1839     int button = -1, action = -1;
1840 
1841     PDC_hWnd = hwnd;
1842     if( !hwnd)
1843         debug_printf( "Null hWnd: msg %u, wParam %x, lParam %lx\n",
1844                      message, wParam, lParam);
1845 
1846     switch (message)
1847     {
1848     case WM_SIZING:
1849         HandleSizing( wParam, lParam );
1850         return( TRUE );
1851 
1852     case WM_SIZE:
1853                 /* If ignore_resize = 1,  don't bother resizing; */
1854                 /* the final window size has yet to be set       */
1855         if( ignore_resize == FALSE)
1856            HandleSize( wParam, lParam);
1857         return 0 ;
1858 
1859     case WM_MOUSEWHEEL:
1860     case WM_MOUSEHWHEEL:
1861         {
1862             POINT pt;
1863 
1864             pt.x = LOWORD( lParam);
1865             pt.y = HIWORD( lParam);
1866             ScreenToClient( hwnd, &pt);
1867             debug_printf( "Mouse wheel: %u %x %lx\n", message, wParam, lParam);
1868             modified_key_to_return = 0;
1869             set_mouse( (message == WM_MOUSEWHEEL)
1870                       ? VERTICAL_WHEEL_EVENT : HORIZONTAL_WHEEL_EVENT,
1871                       (short)( HIWORD(wParam)), pt.x / PDC_cxChar, pt.y / PDC_cyChar);
1872         }
1873         break;
1874 
1875     case WM_MOUSEMOVE:
1876         if( HandleMouseMove( wParam, lParam))
1877             modified_key_to_return = 0;
1878         break;
1879 
1880     case WM_LBUTTONDOWN:
1881         button = 1;
1882         action = BUTTON_PRESSED;
1883         break;
1884 
1885     case WM_LBUTTONUP:
1886         button = 1;
1887         action = BUTTON_RELEASED;
1888         break;
1889 
1890     case WM_RBUTTONDOWN:
1891         button = 3;
1892         action = BUTTON_PRESSED;
1893         break;
1894 
1895     case WM_RBUTTONUP:
1896         button = 3;
1897         action = BUTTON_RELEASED;
1898         break;
1899 
1900     case WM_MBUTTONDOWN:
1901         button = 2;
1902         action = BUTTON_PRESSED;
1903         break;
1904 
1905     case WM_MBUTTONUP:
1906         button = 2;
1907         action = BUTTON_RELEASED;
1908         break;
1909 
1910     case WM_XBUTTONDOWN:
1911         button = ((wParam & MK_XBUTTON1) ? 3 : 4);
1912         action = BUTTON_PRESSED;
1913         xbutton_pressed = button;
1914         break;
1915 
1916     case WM_XBUTTONUP:
1917 #ifdef WRONG_WAY
1918           /* You'd think we'd use the following line,  wouldn't you?      */
1919           /* But we can't,  because an XBUTTONUP message doesn't actually */
1920           /* tell you which button was released!  So we'll assume that    */
1921           /* the released xbutton matches a pressed one;  and we've kept  */
1922           /* track of which buttons are currently pressed.                */
1923         button = ((wParam & MK_XBUTTON1) ? 3 : 4);
1924 #endif
1925         button = xbutton_pressed;
1926         action = BUTTON_RELEASED;
1927         break;
1928 
1929     case WM_MOVE:
1930         return 0 ;
1931 
1932     case WM_ERASEBKGND:      /* no need to erase background;  it'll */
1933         return( 0);         /* all get painted over anyway */
1934 
1935       /* The WM_PAINT routine is sort of "borrowed" from doupdate( ) from */
1936       /* refresh.c.  I'm not entirely sure that this is what ought to be  */
1937       /* done,  though it does appear to work correctly.                  */
1938     case WM_PAINT:
1939         HandlePaint( hwnd );
1940         break;
1941 
1942     case WM_KEYUP:
1943     case WM_SYSKEYUP:
1944         if( wParam == VK_MENU && numpad_unicode_value)
1945         {
1946             modified_key_to_return = numpad_unicode_value;
1947             numpad_unicode_value = 0;
1948             SP->key_modifiers = 0;
1949         }
1950         if( modified_key_to_return )
1951         {
1952             add_key_to_queue( modified_key_to_return );
1953             modified_key_to_return = 0;
1954         }
1955         break;
1956 
1957     case WM_CHAR:       /* _Don't_ add Shift-Tab;  it's handled elsewhere */
1958         if( wParam == 3 && !SP->raw_inp)     /* Ctrl-C hit */
1959             exit( 0);
1960         if( wParam != 9 || !(GetKeyState( VK_SHIFT) & 0x8000))
1961             if( !key_already_handled || last_key_handled != (int)wParam )
1962                add_key_to_queue( (int)wParam );
1963         key_already_handled = FALSE;
1964         last_key_handled = 0;
1965         break;
1966 
1967     case WM_KEYDOWN:
1968     case WM_SYSKEYDOWN:
1969         if( wParam < 225 && wParam > 0 )
1970         {
1971             HandleSyskeyDown( wParam, lParam, &modified_key_to_return );
1972         }
1973         return 0 ;
1974 
1975     case WM_SYSCHAR:
1976         return 0 ;
1977 
1978     case WM_TIMER:
1979         if( wParam != TIMER_ID_FOR_BLINKING)
1980         {
1981             KillTimer( PDC_hWnd, (int)wParam);
1982 #if 0 /* checkme */
1983             within_timeout = FALSE;
1984 #endif
1985         }
1986         else if( SP && curscr && curscr->_y)
1987         {
1988             /* blink the blinking text */
1989             HandleTimer( wParam );
1990         }
1991         break;
1992 
1993     case WM_CLOSE:
1994         {
1995             if( !PDC_shutdown_key[FUNCTION_KEY_SHUT_DOWN])
1996             {
1997                 final_cleanup( );
1998                 PDC_bDone = TRUE;
1999                 exit( 0);
2000             }
2001             else
2002                 add_key_to_queue( PDC_shutdown_key[FUNCTION_KEY_SHUT_DOWN]);
2003         }
2004         return( 0);
2005 
2006     case WM_COMMAND:
2007     case WM_SYSCOMMAND:
2008         if( wParam == WM_EXIT_GRACELESSLY)
2009         {
2010             final_cleanup( );
2011             PDC_bDone = TRUE;
2012             exit( 0);
2013         }
2014         else if( wParam == WM_ENLARGE_FONT || wParam == WM_SHRINK_FONT)
2015         {
2016             adjust_font_size( (wParam == WM_ENLARGE_FONT) ? 1 : -1);
2017             return( 0);
2018         }
2019         else if( wParam == WM_CHOOSE_FONT)
2020         {
2021             if( PDC_choose_a_new_font( ))
2022                 adjust_font_size( 0);
2023             return( 0);
2024         }
2025         else if( wParam == WM_TOGGLE_MENU)
2026         {
2027             HandleMenuToggle( &ignore_resize);
2028         }
2029         break;
2030 
2031     case WM_DESTROY:
2032         PDC_LOG(("WM_DESTROY\n"));
2033         PostQuitMessage (0) ;
2034         PDC_bDone = TRUE;
2035         return 0 ;
2036     }
2037     if( button != -1)
2038     {
2039         add_mouse( button, action, LOWORD( lParam) / PDC_cxChar, HIWORD( lParam) / PDC_cyChar);
2040         if( action == BUTTON_PRESSED)
2041            SetCapture( hwnd);
2042         else
2043            ReleaseCapture( );
2044 #if 0 /* checkme */
2045         SetTimer( hwnd, 0, SP->mouse_wait, NULL);
2046 #endif
2047     }
2048     else
2049        add_mouse( -1, -1, -1, -1);
2050     return DefWindowProc( hwnd, message, wParam, lParam) ;
2051 }
2052 
2053       /* Default behaviour is that,  when one clicks on the 'close' button, */
2054       /* exit( 0) is called,  just as in the SDL and X11 versions.  But if  */
2055       /* one wishes,  one can call PDC_set_shutdown_key to cause those      */
2056       /* buttons to put a specified character into the input queue.  It's   */
2057       /* then the application's problem to exit gracefully,  perhaps with   */
2058       /* messages such as 'are you sure' and so forth.                      */
2059       /*   If you've set a shutdown key,  there's always a risk that the    */
2060       /* program will get stuck in a loop and never process said key.  So   */
2061       /* when the key is set,  a 'Kill' item is appended to the system menu */
2062       /* so that the user still has some way to terminate the app,  albeit  */
2063       /* with extreme prejudice (i.e.,  click on 'Kill' and exit is called  */
2064       /* and the app exits gracelessly.)                                    */
2065 
PDC_set_function_key(const unsigned function,const int new_key)2066 int PDC_set_function_key( const unsigned function, const int new_key)
2067 {
2068     int old_key = -1;
2069 
2070     if( function < PDC_MAX_FUNCTION_KEYS)
2071     {
2072          old_key = PDC_shutdown_key[function];
2073          PDC_shutdown_key[function] = new_key;
2074     }
2075 
2076     if( function == FUNCTION_KEY_SHUT_DOWN)
2077         if( (new_key && !old_key) || (old_key && !new_key))
2078         {
2079             HMENU hMenu = GetSystemMenu( PDC_hWnd, FALSE);
2080 
2081             if( new_key)
2082                 AppendMenu( hMenu, MF_STRING, WM_EXIT_GRACELESSLY, _T( "Kill"));
2083             else
2084                 RemoveMenu( hMenu, WM_EXIT_GRACELESSLY, MF_BYCOMMAND);
2085         }
2086     return( old_key);
2087 }
2088 
2089 /* Used to define whether or not the Font/Paste menu is visible by default.
2090 If this method is not call, it will be true. Must be called before the main
2091 window has been created. */
PDC_set_default_menu_visibility(int visible)2092 void    PDC_set_default_menu_visibility( int visible)
2093 {
2094     extern HWND PDC_hWnd;
2095     if ( !PDC_hWnd)
2096     {
2097         menu_shown = (visible) ? 1 : 0;
2098     }
2099 }
2100 
2101 
2102 /* https://msdn.microsoft.com/en-us/library/windows/desktop/dd162826(v=vs.85).aspx
2103 The code at the above link provides general methods for positioning a window
2104 on a multiple-display setup.  The only instance we're using is the
2105 MONITOR_WORKAREA one,  which ensures that even if monitor geometry changes,
2106 the window will still be entirely on-screen.
2107 
2108 These functions entered the Win32 API with Windows 2000.  If
2109 MONITOR_DEFAULTTONEAREST isn't defined,  we shouldn't try to do this.  */
2110 
2111 #if defined( MONITOR_DEFAULTTONEAREST) && WINVER >= 0x0410
2112 
clip_or_center_rect_to_monitor(LPRECT prc)2113 static void clip_or_center_rect_to_monitor( LPRECT prc)
2114 {
2115     HMONITOR hMonitor;
2116     MONITORINFO mi;
2117     RECT        rc;
2118     const int   w = prc->right  - prc->left;
2119     const int   h = prc->bottom - prc->top;
2120 
2121     hMonitor = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST);
2122 
2123     mi.cbSize = sizeof(mi);
2124     GetMonitorInfo(hMonitor, &mi);
2125 
2126     rc = mi.rcMonitor;
2127 
2128     prc->left   = max(rc.left, min(rc.right-w,  prc->left));
2129     prc->top    = max(rc.top,  min(rc.bottom-h, prc->top));
2130     prc->right  = prc->left + w;
2131     prc->bottom = prc->top  + h;
2132 }
2133 
clip_or_center_window_to_monitor(HWND hwnd)2134 static void clip_or_center_window_to_monitor( HWND hwnd)
2135 {
2136     RECT rc;
2137 
2138     GetWindowRect(hwnd, &rc);
2139     clip_or_center_rect_to_monitor(&rc);
2140     SetWindowPos(hwnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
2141 }
2142 #endif
2143 
2144 /* By default,  the user cannot resize the window.  This is because
2145 many apps don't handle KEY_RESIZE,  and one can get odd behavior
2146 in such cases.  There are two ways around this.  If you call
2147 PDC_set_resize_limits( ) before initwin( ),  telling WinGUI exactly how
2148 large/small the window can be,  the window will be user-resizable.  Or
2149 you can set ttytype[0...3] to contain the resize limits.   A call such as
2150 
2151 PDC_set_resize_limits( 42, 42, 135, 135);
2152 
2153    will result in the window being fixed at 42 lines by 135 columns.
2154 
2155 PDC_set_resize_limits( 20, 50, 70, 200);
2156 
2157    will mean the window can have 20 to 50 lines and 70 to 200 columns.
2158 The user will be able to resize the window freely within those limits.
2159 See 'newtest.c' (in the 'demos' folder) for an example.
2160 
2161    This function is used in only one place (PDC_scr_open( )),  so
2162 it's inlined. */
2163 
set_up_window(void)2164 INLINE int set_up_window( void)
2165 {
2166     /* create the dialog window  */
2167     WNDCLASS   wndclass ;
2168     HMENU hMenu;
2169     HANDLE hInstance = GetModuleHandle( NULL);
2170     int n_default_columns = 80;
2171     int n_default_rows = 25;
2172     int xsize, ysize, window_style;
2173     int xloc = CW_USEDEFAULT;
2174     int yloc = CW_USEDEFAULT;
2175     TCHAR WindowTitle[MAX_PATH];
2176     const TCHAR *AppName = _T( "Curses_App");
2177     HICON icon;
2178     static bool wndclass_has_been_registered = FALSE;
2179 
2180     if( !hInstance)
2181         debug_printf( "No instance: %d\n", GetLastError( ));
2182     originally_focussed_window = GetForegroundWindow( );
2183     debug_printf( "hInstance %x\nOriginal window %x\n", hInstance, originally_focussed_window);
2184     /* set the window icon from the icon in the process */
2185     icon = get_app_icon(hInstance);
2186     if( !icon )
2187        icon = LoadIcon( NULL, IDI_APPLICATION);
2188     if( !wndclass_has_been_registered)
2189     {
2190         ATOM rval;
2191 
2192         wndclass.style         = CS_VREDRAW ;
2193         wndclass.lpfnWndProc   = WndProc ;
2194         wndclass.cbClsExtra    = 0 ;
2195         wndclass.cbWndExtra    = 0 ;
2196         wndclass.hInstance     = hInstance ;
2197         wndclass.hIcon         = icon;
2198         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
2199         wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
2200         wndclass.lpszMenuName  = NULL ;
2201         wndclass.lpszClassName = AppName ;
2202 
2203         rval = RegisterClass( &wndclass) ;
2204         if( !rval)
2205         {
2206             const DWORD last_error = GetLastError( );
2207 
2208             debug_printf( "RegisterClass failed: GetLastError = %lx\n", last_error);
2209             return( -1);
2210         }
2211         wndclass_has_been_registered = TRUE;
2212     }
2213 
2214     get_app_name( WindowTitle, MAX_PATH, TRUE);
2215 #ifdef PDC_WIDE
2216     debug_printf( "WindowTitle = '%ls'\n", WindowTitle);
2217 #endif
2218 
2219     if( PDC_n_rows > 2 && PDC_n_cols > 2)
2220     {
2221         n_default_columns = PDC_n_cols;
2222         n_default_rows    = PDC_n_rows;
2223     }
2224 
2225     get_default_sizes_from_registry( &n_default_columns, &n_default_rows, &xloc, &yloc,
2226                      &menu_shown);
2227 
2228     if( ttytype[1])
2229         PDC_set_resize_limits( (unsigned char)ttytype[0],
2230                                (unsigned char)ttytype[1],
2231                                (unsigned char)ttytype[2],
2232                                (unsigned char)ttytype[3]);
2233     debug_printf( "Size %d x %d,  loc %d x %d;  menu %d\n",
2234                n_default_columns, n_default_rows, xloc, yloc, menu_shown);
2235     get_character_sizes( NULL, &PDC_cxChar, &PDC_cyChar);
2236 
2237     if( min_lines != max_lines || min_cols != max_cols)
2238         window_style = ((n_default_columns == -1) ?
2239                     WS_MAXIMIZE | WS_OVERLAPPEDWINDOW : WS_OVERLAPPEDWINDOW);
2240     else  /* fixed-size window:  looks "normal",  but w/o a maximize box */
2241         window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
2242 
2243     if( n_default_columns == -1)
2244         xsize = ysize = CW_USEDEFAULT;
2245     else
2246     {
2247         keep_size_within_bounds( &n_default_rows, &n_default_columns);
2248         xsize = PDC_cxChar * n_default_columns;
2249         ysize = PDC_cyChar * n_default_rows;
2250         adjust_window_size( &xsize, &ysize, window_style, menu_shown);
2251     }
2252 
2253     PDC_hWnd = CreateWindow( AppName, WindowTitle, window_style,
2254                     xloc, yloc,
2255                     xsize, ysize,
2256                     NULL, (menu_shown ? set_menu( ) : NULL),
2257                     hInstance, NULL) ;
2258 
2259     if( !PDC_hWnd)
2260     {
2261         const DWORD last_error = GetLastError( );
2262 
2263         debug_printf( "CreateWindow failed; GetLastError = %ld", last_error);
2264         return( -2);
2265     }
2266 
2267     hMenu = GetSystemMenu( PDC_hWnd, FALSE);
2268     AppendMenu( hMenu, MF_STRING | (menu_shown ? MF_CHECKED : MF_UNCHECKED), WM_TOGGLE_MENU, _T( "Menu"));
2269     AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, _T( "Choose Font"));
2270 
2271     debug_printf( "menu set\n");
2272 
2273     ShowWindow (PDC_hWnd,
2274                     (n_default_columns == -1) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
2275     debug_printf( "window shown\n");
2276     UpdateWindow (PDC_hWnd) ;
2277     debug_printf( "window updated\n");
2278     SetTimer( PDC_hWnd, TIMER_ID_FOR_BLINKING, 500, NULL);
2279     debug_printf( "timer set\n");
2280 
2281 #if defined( MONITOR_DEFAULTTONEAREST) && WINVER >= 0x0410
2282     /* if the window is off-screen, move it on screen. */
2283     clip_or_center_window_to_monitor( PDC_hWnd);
2284 #endif
2285 
2286     return( 0);
2287 }
2288 
2289 /* open the physical screen -- allocate SP, miscellaneous intialization,
2290    and may save the existing screen for later restoration.
2291 
2292    Deciding on a for-real maximum screen size has proven difficult.
2293    But there is really no particularly good reason to set such a maximum.
2294    If one does,  you get some tricky issues:  suppose the user drags the
2295    window to create a screen larger than MAX_LINES or MAX_COLUMNS?  My
2296    hope is to evade that problem by just setting those constants to be...
2297    well... unrealistically large.  */
2298 
2299 #define MAX_LINES   50000
2300 #define MAX_COLUMNS 50000
2301 
PDC_scr_open(void)2302 int PDC_scr_open(void)
2303 {
2304     HMODULE hntdll = GetModuleHandle( _T("ntdll.dll"));
2305 
2306     if( hntdll)
2307         wine_version = (wine_version_func)GetProcAddress(hntdll, "wine_get_version");
2308 
2309     PDC_LOG(("PDC_scr_open() - called\n"));
2310     COLORS = N_COLORS;  /* should give this a try and see if it works! */
2311     if (!SP || PDC_init_palette( ))
2312         return ERR;
2313 
2314     debug_printf( "colors alloc\n");
2315     SP->mouse_wait = PDC_CLICK_PERIOD;
2316     SP->visibility = 0;                /* no cursor,  by default */
2317     SP->curscol = SP->cursrow = 0;
2318     SP->audible = TRUE;
2319     SP->mono = FALSE;
2320     SP->termattrs = A_BOLD | A_COLOR | A_LEFTLINE | A_RIGHTLINE
2321                | A_OVERLINE | A_UNDERLINE | A_STRIKEOUT | A_ITALIC
2322                | A_DIM | A_REVERSE;
2323 
2324 #ifdef NO_LONGER_AVAILABLE
2325             /* (Jan 2020 : the wmcbrine flavor lacks Xinitscr) */
2326     /* note: we parse the non-wide argc (see comment in header),
2327        therefore using non-wide char handling here */
2328     if( argc && argv)         /* store a copy of the input arguments */
2329     {
2330         PDC_argc = argc;
2331         PDC_argv = (char **)calloc( argc + 1, sizeof( char *));
2332         for( i = 0; i < argc; i++)
2333         {
2334             PDC_argv[i] = (char *)malloc( strlen( argv[i]) + 1);
2335             strcpy( PDC_argv[i], argv[i]);
2336         }
2337     }
2338 #endif
2339 
2340     if( set_up_window( ))
2341     {
2342         fprintf( stderr, "set_up_window failed\n");
2343         return ERR;
2344     }
2345     debug_printf( "Back from set_up_window\n");
2346     while( !PDC_get_rows( ))     /* wait for screen to be drawn and */
2347       ;                          /* actual size to be determined    */
2348 
2349     debug_printf( "Back from PDC_get_rows\n");
2350     SP->lines = PDC_get_rows();
2351     SP->cols = PDC_get_columns();
2352 
2353     if (SP->lines < 2 || SP->lines > MAX_LINES
2354        || SP->cols < 2 || SP->cols > MAX_COLUMNS)
2355     {
2356         fprintf(stderr, "LINES value must be >= 2 and <= %d: got %d\n",
2357                 MAX_LINES, SP->lines);
2358         fprintf(stderr, "COLS value must be >= 2 and <= %d: got %d\n",
2359                 MAX_COLUMNS, SP->cols);
2360 
2361         return ERR;
2362     }
2363 
2364 /*  PDC_reset_prog_mode();   doesn't do anything anyway */
2365     debug_printf( "...we're done\n");
2366     return OK;
2367 }
2368 
2369 /* the core of resize_term() */
2370 
PDC_resize_screen(int nlines,int ncols)2371 int PDC_resize_screen( int nlines, int ncols)
2372 {
2373     if( !stdscr)        /* window hasn't been created yet;  we're */
2374     {                   /* specifying its size before doing so    */
2375         PDC_n_rows = nlines;
2376         PDC_n_cols = ncols;
2377         return OK;
2378     }
2379     SP->resized = FALSE;
2380     debug_printf( "Incoming: %d %d\n", nlines, ncols);
2381     if( nlines >= 2 && ncols >= 2 && PDC_cxChar && PDC_cyChar && PDC_hWnd &&
2382                   !IsZoomed( PDC_hWnd) /* && WaitResult == WAIT_OBJECT_0 */)
2383     {
2384         RECT rect, client_rect;
2385         int new_width;
2386         int new_height;
2387 
2388         GetWindowRect( PDC_hWnd, &rect);
2389         GetClientRect( PDC_hWnd, &client_rect);
2390         debug_printf( "Outgoing: %d %d\n", nlines, ncols);
2391         new_width = ncols * PDC_cxChar;
2392         new_height = nlines * PDC_cyChar;
2393 
2394         if( new_width != client_rect.right || new_height != client_rect.bottom)
2395         {                    /* check to make sure size actually changed */
2396             add_resize_key = 0;
2397             SetWindowPos( PDC_hWnd, 0, 0, 0,
2398                    new_width + (rect.right - rect.left) - client_rect.right,
2399                    new_height + (rect.bottom - rect.top) - client_rect.bottom,
2400                   SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
2401         }
2402     }
2403     return OK;
2404 }
2405 
PDC_reset_prog_mode(void)2406 void PDC_reset_prog_mode(void)
2407 {
2408     PDC_LOG(("PDC_reset_prog_mode() - called.\n"));
2409 #ifdef NOT_CURRENTLY_IN_USE
2410     if( PDC_bDone == FALSE && PDC_hWnd)
2411     {
2412         PDC_bDone = TRUE;
2413         SetForegroundWindow( PDC_hWnd);
2414     }
2415 #endif
2416 }
2417 
PDC_reset_shell_mode(void)2418 void PDC_reset_shell_mode(void)
2419 {
2420 }
2421 
PDC_restore_screen_mode(int i)2422 void PDC_restore_screen_mode(int i)
2423 {
2424 }
2425 
PDC_save_screen_mode(int i)2426 void PDC_save_screen_mode(int i)
2427 {
2428 }
2429 
2430 /* NOTE:  as with PDC_init_color() (see below),  this function has to
2431 redraw all text with color attribute 'pair' to match the newly-set
2432 foreground and background colors.  The loops to go through every character
2433 in curscr,  looking for those that need to be redrawn and ignoring
2434 those at the front and start of each line,  are very similar. */
2435 
get_pair(const chtype ch)2436 static int get_pair( const chtype ch)
2437 {
2438    return( (int)( (ch & A_COLOR) >> PDC_COLOR_SHIFT));
2439 }
2440 
PDC_can_change_color(void)2441 bool PDC_can_change_color(void)
2442 {
2443     return TRUE;
2444 }
2445 
PDC_color_content(int color,int * red,int * green,int * blue)2446 int PDC_color_content( int color, int *red, int *green, int *blue)
2447 {
2448     COLORREF col = PDC_get_palette_entry( color);
2449 
2450     *red = DIVROUND(GetRValue(col) * 1000, 255);
2451     *green = DIVROUND(GetGValue(col) * 1000, 255);
2452     *blue = DIVROUND(GetBValue(col) * 1000, 255);
2453 
2454     return OK;
2455 }
2456 
2457 /* We have an odd problem when changing colors with PDC_init_color().  On
2458 palette-based systems,  you just change the palette and the hardware takes
2459 care of the rest.  Here,  though,  we actually need to redraw any text that's
2460 drawn in the specified color.  So we gotta look at each character and see if
2461 either the foreground or background matches the index that we're changing.
2462 Then, that text gets redrawn.  For speed/simplicity,  the code looks for the
2463 first and last character in each line that would be affected, then draws those
2464 in between (frequently,  this will be zero characters, i.e., no text on that
2465 particular line happens to use the color index in question.)  See similar code
2466 above for PDC_init_pair(),  to handle basically the same problem. */
2467 
color_used_for_this_char(const chtype c,const int idx)2468 static int color_used_for_this_char( const chtype c, const int idx)
2469 {
2470     const int color = get_pair( c);
2471     int fg, bg;
2472     int rval;
2473 
2474     extended_pair_content( color, &fg, &bg);
2475     rval = (fg == idx || bg == idx);
2476     return( rval);
2477 }
2478 
PDC_init_color(int color,int red,int green,int blue)2479 int PDC_init_color( int color, int red, int green, int blue)
2480 {
2481     const COLORREF new_rgb = RGB(DIVROUND(red * 255, 1000),
2482                                  DIVROUND(green * 255, 1000),
2483                                  DIVROUND(blue * 255, 1000));
2484 
2485     if( !PDC_set_palette_entry( color, new_rgb))
2486     {
2487         /* Possibly go through curscr and redraw everything with that color! */
2488         if( curscr && curscr->_y)
2489         {
2490             int i;
2491 
2492             for( i = 0; i < SP->lines; i++)
2493                 if( curscr->_y[i])
2494                 {
2495                     int j = 0, n_chars;
2496                     chtype *line = curscr->_y[i];
2497 
2498              /* skip over starting text that isn't of the desired color: */
2499                     while( j < SP->cols
2500                                  && !color_used_for_this_char( *line, color))
2501                     {
2502                         j++;
2503                         line++;
2504                     }
2505                     n_chars = SP->cols - j;
2506             /* then skip over text at the end that's not the right color: */
2507                     while( n_chars &&
2508                          !color_used_for_this_char( line[n_chars - 1], color))
2509                         n_chars--;
2510                     if( n_chars)
2511                         PDC_transform_line( i, j, n_chars, line);
2512                 }
2513         }
2514     }
2515     return OK;
2516 }
2517