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