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