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