Glenn Maynard's IMM cursor position fix
[sgt/putty] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <richedit.h>
5 #include <mmsystem.h>
6 #ifndef AUTO_WINSOCK
7 #ifdef WINSOCK_TWO
8 #include <winsock2.h>
9 #else
10 #include <winsock.h>
11 #endif
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <ctype.h>
17 #include <time.h>
18 #include <assert.h>
19
20 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
21 #include "putty.h"
22 #include "winstuff.h"
23 #include "storage.h"
24 #include "win_res.h"
25
26 #define IDM_SHOWLOG 0x0010
27 #define IDM_NEWSESS 0x0020
28 #define IDM_DUPSESS 0x0030
29 #define IDM_RECONF 0x0040
30 #define IDM_CLRSB 0x0050
31 #define IDM_RESET 0x0060
32 #define IDM_TEL_AYT 0x0070
33 #define IDM_TEL_BRK 0x0080
34 #define IDM_TEL_SYNCH 0x0090
35 #define IDM_TEL_EC 0x00a0
36 #define IDM_TEL_EL 0x00b0
37 #define IDM_TEL_GA 0x00c0
38 #define IDM_TEL_NOP 0x00d0
39 #define IDM_TEL_ABORT 0x00e0
40 #define IDM_TEL_AO 0x00f0
41 #define IDM_TEL_IP 0x0100
42 #define IDM_TEL_SUSP 0x0110
43 #define IDM_TEL_EOR 0x0120
44 #define IDM_TEL_EOF 0x0130
45 #define IDM_ABOUT 0x0140
46 #define IDM_SAVEDSESS 0x0150
47 #define IDM_COPYALL 0x0160
48 #define IDM_FULLSCREEN 0x0170
49
50 #define IDM_SESSLGP 0x0250 /* log type printable */
51 #define IDM_SESSLGA 0x0260 /* log type all chars */
52 #define IDM_SESSLGE 0x0270 /* log end */
53 #define IDM_SAVED_MIN 0x1000
54 #define IDM_SAVED_MAX 0x2000
55
56 #define WM_IGNORE_CLIP (WM_XUSER + 2)
57
58 /* Needed for Chinese support and apparently not always defined. */
59 #ifndef VK_PROCESSKEY
60 #define VK_PROCESSKEY 0xE5
61 #endif
62
63 /* Needed for mouse wheel support and not defined in earlier SDKs. */
64 #ifndef WM_MOUSEWHEEL
65 #define WM_MOUSEWHEEL 0x020A
66 #endif
67
68 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
69 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
70 unsigned char *output);
71 static void cfgtopalette(void);
72 static void init_palette(void);
73 static void init_fonts(int, int);
74 static void another_font(int);
75 static void deinit_fonts(void);
76
77 /* Window layout information */
78 static void reset_window(int);
79 static int full_screen = 0;
80 static int extra_width, extra_height;
81 static int font_width, font_height, font_dualwidth;
82 static int offset_width, offset_height;
83 static int was_zoomed = 0;
84 static int prev_rows, prev_cols;
85
86 static int pending_netevent = 0;
87 static WPARAM pend_netevent_wParam = 0;
88 static LPARAM pend_netevent_lParam = 0;
89 static void enact_pending_netevent(void);
90 static void flash_window(int mode);
91 static void flip_full_screen(void);
92
93 static time_t last_movement = 0;
94
95 #define FONT_NORMAL 0
96 #define FONT_BOLD 1
97 #define FONT_UNDERLINE 2
98 #define FONT_BOLDUND 3
99 #define FONT_WIDE 0x04
100 #define FONT_HIGH 0x08
101 #define FONT_NARROW 0x10
102
103 #define FONT_OEM 0x20
104 #define FONT_OEMBOLD 0x21
105 #define FONT_OEMUND 0x22
106 #define FONT_OEMBOLDUND 0x23
107
108 #define FONT_MAXNO 0x2F
109 #define FONT_SHIFT 5
110 static HFONT fonts[FONT_MAXNO];
111 static int fontflag[FONT_MAXNO];
112 static enum {
113 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
114 } bold_mode;
115 static enum {
116 UND_LINE, UND_FONT
117 } und_mode;
118 static int descent;
119
120 #define NCOLOURS 24
121 static COLORREF colours[NCOLOURS];
122 static HPALETTE pal;
123 static LPLOGPALETTE logpal;
124 static RGBTRIPLE defpal[NCOLOURS];
125
126 static HWND hwnd;
127
128 static HBITMAP caretbm;
129
130 static int dbltime, lasttime, lastact;
131 static Mouse_Button lastbtn;
132
133 /* this allows xterm-style mouse handling. */
134 static int send_raw_mouse = 0;
135 static int wheel_accumulator = 0;
136
137 static char *window_name, *icon_name;
138
139 static int compose_state = 0;
140
141 static OSVERSIONINFO osVersion;
142
143 /* Dummy routine, only required in plink. */
144 void ldisc_update(int echo, int edit)
145 {
146 }
147
148 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
149 {
150 static char appname[] = "PuTTY";
151 WORD winsock_ver;
152 WSADATA wsadata;
153 WNDCLASS wndclass;
154 MSG msg;
155 int guess_width, guess_height;
156
157 hinst = inst;
158 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
159
160 winsock_ver = MAKEWORD(1, 1);
161 if (WSAStartup(winsock_ver, &wsadata)) {
162 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
163 MB_OK | MB_ICONEXCLAMATION);
164 return 1;
165 }
166 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
167 MessageBox(NULL, "WinSock version is incompatible with 1.1",
168 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
169 WSACleanup();
170 return 1;
171 }
172 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
173 sk_init();
174
175 InitCommonControls();
176
177 /* Ensure a Maximize setting in Explorer doesn't maximise the
178 * config box. */
179 defuse_showwindow();
180
181 {
182 ZeroMemory(&osVersion, sizeof(osVersion));
183 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
184 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
185 MessageBox(NULL, "Windows refuses to report a version",
186 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
187 return 1;
188 }
189 }
190
191 /*
192 * Process the command line.
193 */
194 {
195 char *p;
196
197 default_protocol = DEFAULT_PROTOCOL;
198 default_port = DEFAULT_PORT;
199 cfg.logtype = LGTYP_NONE;
200
201 do_defaults(NULL, &cfg);
202
203 p = cmdline;
204 while (*p && isspace(*p))
205 p++;
206
207 /*
208 * Process command line options first. Yes, this can be
209 * done better, and it will be as soon as I have the
210 * energy...
211 */
212 while (*p == '-') {
213 char *q = p + strcspn(p, " \t");
214 p++;
215 if (q == p + 3 &&
216 tolower(p[0]) == 's' &&
217 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
218 default_protocol = cfg.protocol = PROT_SSH;
219 default_port = cfg.port = 22;
220 } else if (q == p + 7 &&
221 tolower(p[0]) == 'c' &&
222 tolower(p[1]) == 'l' &&
223 tolower(p[2]) == 'e' &&
224 tolower(p[3]) == 'a' &&
225 tolower(p[4]) == 'n' &&
226 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
227 /*
228 * `putty -cleanup'. Remove all registry entries
229 * associated with PuTTY, and also find and delete
230 * the random seed file.
231 */
232 if (MessageBox(NULL,
233 "This procedure will remove ALL Registry\n"
234 "entries associated with PuTTY, and will\n"
235 "also remove the PuTTY random seed file.\n"
236 "\n"
237 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
238 "SESSIONS. Are you really sure you want\n"
239 "to continue?",
240 "PuTTY Warning",
241 MB_YESNO | MB_ICONWARNING) == IDYES) {
242 cleanup_all();
243 }
244 exit(0);
245 }
246 p = q + strspn(q, " \t");
247 }
248
249 /*
250 * An initial @ means to activate a saved session.
251 */
252 if (*p == '@') {
253 int i = strlen(p);
254 while (i > 1 && isspace(p[i - 1]))
255 i--;
256 p[i] = '\0';
257 do_defaults(p + 1, &cfg);
258 if (!*cfg.host && !do_config()) {
259 WSACleanup();
260 return 0;
261 }
262 } else if (*p == '&') {
263 /*
264 * An initial & means we've been given a command line
265 * containing the hex value of a HANDLE for a file
266 * mapping object, which we must then extract as a
267 * config.
268 */
269 HANDLE filemap;
270 Config *cp;
271 if (sscanf(p + 1, "%p", &filemap) == 1 &&
272 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
273 0, 0, sizeof(Config))) != NULL) {
274 cfg = *cp;
275 UnmapViewOfFile(cp);
276 CloseHandle(filemap);
277 } else if (!do_config()) {
278 WSACleanup();
279 return 0;
280 }
281 } else if (*p) {
282 char *q = p;
283 /*
284 * If the hostname starts with "telnet:", set the
285 * protocol to Telnet and process the string as a
286 * Telnet URL.
287 */
288 if (!strncmp(q, "telnet:", 7)) {
289 char c;
290
291 q += 7;
292 if (q[0] == '/' && q[1] == '/')
293 q += 2;
294 cfg.protocol = PROT_TELNET;
295 p = q;
296 while (*p && *p != ':' && *p != '/')
297 p++;
298 c = *p;
299 if (*p)
300 *p++ = '\0';
301 if (c == ':')
302 cfg.port = atoi(p);
303 else
304 cfg.port = -1;
305 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
306 cfg.host[sizeof(cfg.host) - 1] = '\0';
307 } else {
308 while (*p && !isspace(*p))
309 p++;
310 if (*p)
311 *p++ = '\0';
312 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
313 cfg.host[sizeof(cfg.host) - 1] = '\0';
314 while (*p && isspace(*p))
315 p++;
316 if (*p)
317 cfg.port = atoi(p);
318 else
319 cfg.port = -1;
320 }
321 } else {
322 if (!do_config()) {
323 WSACleanup();
324 return 0;
325 }
326 }
327
328 /*
329 * Trim leading whitespace off the hostname if it's there.
330 */
331 {
332 int space = strspn(cfg.host, " \t");
333 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
334 }
335
336 /* See if host is of the form user@host */
337 if (cfg.host[0] != '\0') {
338 char *atsign = strchr(cfg.host, '@');
339 /* Make sure we're not overflowing the user field */
340 if (atsign) {
341 if (atsign - cfg.host < sizeof cfg.username) {
342 strncpy(cfg.username, cfg.host, atsign - cfg.host);
343 cfg.username[atsign - cfg.host] = '\0';
344 }
345 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
346 }
347 }
348
349 /*
350 * Trim a colon suffix off the hostname if it's there.
351 */
352 cfg.host[strcspn(cfg.host, ":")] = '\0';
353 }
354
355 /*
356 * Select protocol. This is farmed out into a table in a
357 * separate file to enable an ssh-free variant.
358 */
359 {
360 int i;
361 back = NULL;
362 for (i = 0; backends[i].backend != NULL; i++)
363 if (backends[i].protocol == cfg.protocol) {
364 back = backends[i].backend;
365 break;
366 }
367 if (back == NULL) {
368 MessageBox(NULL, "Unsupported protocol number found",
369 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
370 WSACleanup();
371 return 1;
372 }
373 }
374
375 /* Check for invalid Port number (i.e. zero) */
376 if (cfg.port == 0) {
377 MessageBox(NULL, "Invalid Port Number",
378 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
379 WSACleanup();
380 return 1;
381 }
382
383 if (!prev) {
384 wndclass.style = 0;
385 wndclass.lpfnWndProc = WndProc;
386 wndclass.cbClsExtra = 0;
387 wndclass.cbWndExtra = 0;
388 wndclass.hInstance = inst;
389 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
390 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
391 wndclass.hbrBackground = NULL;
392 wndclass.lpszMenuName = NULL;
393 wndclass.lpszClassName = appname;
394
395 RegisterClass(&wndclass);
396 }
397
398 hwnd = NULL;
399
400 savelines = cfg.savelines;
401 term_init();
402
403 cfgtopalette();
404
405 /*
406 * Guess some defaults for the window size. This all gets
407 * updated later, so we don't really care too much. However, we
408 * do want the font width/height guesses to correspond to a
409 * large font rather than a small one...
410 */
411
412 font_width = 10;
413 font_height = 20;
414 extra_width = 25;
415 extra_height = 28;
416 term_size(cfg.height, cfg.width, cfg.savelines);
417 guess_width = extra_width + font_width * cols;
418 guess_height = extra_height + font_height * rows;
419 {
420 RECT r;
421 HWND w = GetDesktopWindow();
422 GetWindowRect(w, &r);
423 if (guess_width > r.right - r.left)
424 guess_width = r.right - r.left;
425 if (guess_height > r.bottom - r.top)
426 guess_height = r.bottom - r.top;
427 }
428
429 {
430 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
431 int exwinmode = 0;
432 if (!cfg.scrollbar)
433 winmode &= ~(WS_VSCROLL);
434 if (cfg.resize_action == RESIZE_DISABLED)
435 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
436 if (cfg.alwaysontop)
437 exwinmode |= WS_EX_TOPMOST;
438 if (cfg.sunken_edge)
439 exwinmode |= WS_EX_CLIENTEDGE;
440 hwnd = CreateWindowEx(exwinmode, appname, appname,
441 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
442 guess_width, guess_height,
443 NULL, NULL, inst, NULL);
444 }
445
446 /*
447 * Initialise the fonts, simultaneously correcting the guesses
448 * for font_{width,height}.
449 */
450 init_fonts(0,0);
451
452 /*
453 * Correct the guesses for extra_{width,height}.
454 */
455 {
456 RECT cr, wr;
457 GetWindowRect(hwnd, &wr);
458 GetClientRect(hwnd, &cr);
459 offset_width = offset_height = cfg.window_border;
460 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
461 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
462 }
463
464 /*
465 * Resize the window, now we know what size we _really_ want it
466 * to be.
467 */
468 guess_width = extra_width + font_width * cols;
469 guess_height = extra_height + font_height * rows;
470 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
471 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
472
473 /*
474 * Set up a caret bitmap, with no content.
475 */
476 {
477 char *bits;
478 int size = (font_width + 15) / 16 * 2 * font_height;
479 bits = smalloc(size);
480 memset(bits, 0, size);
481 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
482 sfree(bits);
483 }
484 CreateCaret(hwnd, caretbm, font_width, font_height);
485
486 /*
487 * Initialise the scroll bar.
488 */
489 {
490 SCROLLINFO si;
491
492 si.cbSize = sizeof(si);
493 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
494 si.nMin = 0;
495 si.nMax = rows - 1;
496 si.nPage = rows;
497 si.nPos = 0;
498 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
499 }
500
501 /*
502 * Start up the telnet connection.
503 */
504 {
505 char *error;
506 char msg[1024], *title;
507 char *realhost;
508
509 error = back->init(cfg.host, cfg.port, &realhost);
510 if (error) {
511 sprintf(msg, "Unable to open connection to\n"
512 "%.800s\n" "%s", cfg.host, error);
513 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
514 return 0;
515 }
516 window_name = icon_name = NULL;
517 if (*cfg.wintitle) {
518 title = cfg.wintitle;
519 } else {
520 sprintf(msg, "%s - PuTTY", realhost);
521 title = msg;
522 }
523 sfree(realhost);
524 set_title(title);
525 set_icon(title);
526 }
527
528 session_closed = FALSE;
529
530 /*
531 * Prepare the mouse handler.
532 */
533 lastact = MA_NOTHING;
534 lastbtn = MBT_NOTHING;
535 dbltime = GetDoubleClickTime();
536
537 /*
538 * Set up the session-control options on the system menu.
539 */
540 {
541 HMENU m = GetSystemMenu(hwnd, FALSE);
542 HMENU p, s;
543 int i;
544
545 AppendMenu(m, MF_SEPARATOR, 0, 0);
546 if (cfg.protocol == PROT_TELNET) {
547 p = CreateMenu();
548 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
549 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
550 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
551 AppendMenu(p, MF_SEPARATOR, 0, 0);
552 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
553 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
554 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
555 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
556 AppendMenu(p, MF_SEPARATOR, 0, 0);
557 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
558 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
559 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
560 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
561 AppendMenu(p, MF_SEPARATOR, 0, 0);
562 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
563 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
564 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
565 "Telnet Command");
566 AppendMenu(m, MF_SEPARATOR, 0, 0);
567 }
568 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
569 AppendMenu(m, MF_SEPARATOR, 0, 0);
570 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
571 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
572 s = CreateMenu();
573 get_sesslist(TRUE);
574 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
575 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
576 sessions[i]);
577 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
578 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
579 AppendMenu(m, MF_SEPARATOR, 0, 0);
580 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
581 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
582 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
583 AppendMenu(m, MF_SEPARATOR, 0, 0);
584 AppendMenu(m, MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
585 AppendMenu(m, MF_SEPARATOR, 0, 0);
586 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
587 }
588
589 /*
590 * Finally show the window!
591 */
592 ShowWindow(hwnd, show);
593 SetForegroundWindow(hwnd);
594
595 /*
596 * Open the initial log file if there is one.
597 */
598 logfopen();
599
600 /*
601 * Set the palette up.
602 */
603 pal = NULL;
604 logpal = NULL;
605 init_palette();
606
607 has_focus = (GetForegroundWindow() == hwnd);
608 UpdateWindow(hwnd);
609
610 if (GetMessage(&msg, NULL, 0, 0) == 1) {
611 int timer_id = 0, long_timer = 0;
612
613 while (msg.message != WM_QUIT) {
614 /* Sometimes DispatchMessage calls routines that use their own
615 * GetMessage loop, setup this timer so we get some control back.
616 *
617 * Also call term_update() from the timer so that if the host
618 * is sending data flat out we still do redraws.
619 */
620 if (timer_id && long_timer) {
621 KillTimer(hwnd, timer_id);
622 long_timer = timer_id = 0;
623 }
624 if (!timer_id)
625 timer_id = SetTimer(hwnd, 1, 20, NULL);
626 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
627 DispatchMessage(&msg);
628
629 /* Make sure we blink everything that needs it. */
630 term_blink(0);
631
632 /* Send the paste buffer if there's anything to send */
633 term_paste();
634
635 /* If there's nothing new in the queue then we can do everything
636 * we've delayed, reading the socket, writing, and repainting
637 * the window.
638 */
639 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
640 continue;
641
642 if (pending_netevent) {
643 enact_pending_netevent();
644
645 /* Force the cursor blink on */
646 term_blink(1);
647
648 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
649 continue;
650 }
651
652 /* Okay there is now nothing to do so we make sure the screen is
653 * completely up to date then tell windows to call us in a little
654 * while.
655 */
656 if (timer_id) {
657 KillTimer(hwnd, timer_id);
658 timer_id = 0;
659 }
660 HideCaret(hwnd);
661 term_out();
662 term_update();
663 ShowCaret(hwnd);
664
665 flash_window(1); /* maintain */
666
667 if (in_vbell)
668 /* Hmm, term_update didn't want to do an update too soon ... */
669 timer_id = SetTimer(hwnd, 1, 50, NULL);
670 else if (!has_focus)
671 timer_id = SetTimer(hwnd, 1, 500, NULL);
672 else
673 timer_id = SetTimer(hwnd, 1, 100, NULL);
674 long_timer = 1;
675
676 /* There's no point rescanning everything in the message queue
677 * so we do an apparently unnecessary wait here
678 */
679 WaitMessage();
680 if (GetMessage(&msg, NULL, 0, 0) != 1)
681 break;
682 }
683 }
684
685 /*
686 * Clean up.
687 */
688 deinit_fonts();
689 sfree(logpal);
690 if (pal)
691 DeleteObject(pal);
692 WSACleanup();
693
694 if (cfg.protocol == PROT_SSH) {
695 random_save_seed();
696 #ifdef MSCRYPTOAPI
697 crypto_wrapup();
698 #endif
699 }
700
701 return msg.wParam;
702 }
703
704 /*
705 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
706 */
707 char *do_select(SOCKET skt, int startup)
708 {
709 int msg, events;
710 if (startup) {
711 msg = WM_NETEVENT;
712 events = (FD_CONNECT | FD_READ | FD_WRITE |
713 FD_OOB | FD_CLOSE | FD_ACCEPT);
714 } else {
715 msg = events = 0;
716 }
717 if (!hwnd)
718 return "do_select(): internal error (hwnd==NULL)";
719 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
720 switch (WSAGetLastError()) {
721 case WSAENETDOWN:
722 return "Network is down";
723 default:
724 return "WSAAsyncSelect(): unknown error";
725 }
726 }
727 return NULL;
728 }
729
730 /*
731 * set or clear the "raw mouse message" mode
732 */
733 void set_raw_mouse_mode(int activate)
734 {
735 send_raw_mouse = activate;
736 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
737 }
738
739 /*
740 * Print a message box and close the connection.
741 */
742 void connection_fatal(char *fmt, ...)
743 {
744 va_list ap;
745 char stuff[200];
746
747 va_start(ap, fmt);
748 vsprintf(stuff, fmt, ap);
749 va_end(ap);
750 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
751 if (cfg.close_on_exit == COE_ALWAYS)
752 PostQuitMessage(1);
753 else {
754 session_closed = TRUE;
755 SetWindowText(hwnd, "PuTTY (inactive)");
756 }
757 }
758
759 /*
760 * Actually do the job requested by a WM_NETEVENT
761 */
762 static void enact_pending_netevent(void)
763 {
764 static int reentering = 0;
765 extern int select_result(WPARAM, LPARAM);
766 int ret;
767
768 if (reentering)
769 return; /* don't unpend the pending */
770
771 pending_netevent = FALSE;
772
773 reentering = 1;
774 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
775 reentering = 0;
776
777 if (ret == 0 && !session_closed) {
778 /* Abnormal exits will already have set session_closed and taken
779 * appropriate action. */
780 if (cfg.close_on_exit == COE_ALWAYS ||
781 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
782 else {
783 session_closed = TRUE;
784 SetWindowText(hwnd, "PuTTY (inactive)");
785 MessageBox(hwnd, "Connection closed by remote host",
786 "PuTTY", MB_OK | MB_ICONINFORMATION);
787 }
788 }
789 }
790
791 /*
792 * Copy the colour palette from the configuration data into defpal.
793 * This is non-trivial because the colour indices are different.
794 */
795 static void cfgtopalette(void)
796 {
797 int i;
798 static const int ww[] = {
799 6, 7, 8, 9, 10, 11, 12, 13,
800 14, 15, 16, 17, 18, 19, 20, 21,
801 0, 1, 2, 3, 4, 4, 5, 5
802 };
803
804 for (i = 0; i < 24; i++) {
805 int w = ww[i];
806 defpal[i].rgbtRed = cfg.colours[w][0];
807 defpal[i].rgbtGreen = cfg.colours[w][1];
808 defpal[i].rgbtBlue = cfg.colours[w][2];
809 }
810 }
811
812 /*
813 * Set up the colour palette.
814 */
815 static void init_palette(void)
816 {
817 int i;
818 HDC hdc = GetDC(hwnd);
819 if (hdc) {
820 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
821 logpal = smalloc(sizeof(*logpal)
822 - sizeof(logpal->palPalEntry)
823 + NCOLOURS * sizeof(PALETTEENTRY));
824 logpal->palVersion = 0x300;
825 logpal->palNumEntries = NCOLOURS;
826 for (i = 0; i < NCOLOURS; i++) {
827 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
828 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
829 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
830 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
831 }
832 pal = CreatePalette(logpal);
833 if (pal) {
834 SelectPalette(hdc, pal, FALSE);
835 RealizePalette(hdc);
836 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
837 }
838 }
839 ReleaseDC(hwnd, hdc);
840 }
841 if (pal)
842 for (i = 0; i < NCOLOURS; i++)
843 colours[i] = PALETTERGB(defpal[i].rgbtRed,
844 defpal[i].rgbtGreen,
845 defpal[i].rgbtBlue);
846 else
847 for (i = 0; i < NCOLOURS; i++)
848 colours[i] = RGB(defpal[i].rgbtRed,
849 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
850 }
851
852 /*
853 * Initialise all the fonts we will need initially. There may be as many as
854 * three or as few as one. The other (poentially) twentyone fonts are done
855 * if/when they are needed.
856 *
857 * We also:
858 *
859 * - check the font width and height, correcting our guesses if
860 * necessary.
861 *
862 * - verify that the bold font is the same width as the ordinary
863 * one, and engage shadow bolding if not.
864 *
865 * - verify that the underlined font is the same width as the
866 * ordinary one (manual underlining by means of line drawing can
867 * be done in a pinch).
868 */
869 static void init_fonts(int pick_width, int pick_height)
870 {
871 TEXTMETRIC tm;
872 CPINFO cpinfo;
873 int fontsize[3];
874 int i;
875 HDC hdc;
876 int fw_dontcare, fw_bold;
877
878 for (i = 0; i < FONT_MAXNO; i++)
879 fonts[i] = NULL;
880
881 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
882 und_mode = UND_FONT;
883
884 if (cfg.fontisbold) {
885 fw_dontcare = FW_BOLD;
886 fw_bold = FW_HEAVY;
887 } else {
888 fw_dontcare = FW_DONTCARE;
889 fw_bold = FW_BOLD;
890 }
891
892 hdc = GetDC(hwnd);
893
894 if (pick_height)
895 font_height = pick_height;
896 else {
897 font_height = cfg.fontheight;
898 if (font_height > 0) {
899 font_height =
900 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
901 }
902 }
903 font_width = pick_width;
904
905 #define f(i,c,w,u) \
906 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
907 c, OUT_DEFAULT_PRECIS, \
908 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
909 FIXED_PITCH | FF_DONTCARE, cfg.font)
910
911 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
912
913 SelectObject(hdc, fonts[FONT_NORMAL]);
914 GetTextMetrics(hdc, &tm);
915
916 if (pick_width == 0 || pick_height == 0) {
917 font_height = tm.tmHeight;
918 font_width = tm.tmAveCharWidth;
919 }
920 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
921
922 #ifdef RDB_DEBUG_PATCH
923 debug(23, "Primary font H=%d, AW=%d, MW=%d",
924 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
925 #endif
926
927 {
928 CHARSETINFO info;
929 DWORD cset = tm.tmCharSet;
930 memset(&info, 0xFF, sizeof(info));
931
932 /* !!! Yes the next line is right */
933 if (cset == OEM_CHARSET)
934 font_codepage = GetOEMCP();
935 else
936 if (TranslateCharsetInfo
937 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
938 info.ciACP;
939 else
940 font_codepage = -1;
941
942 GetCPInfo(font_codepage, &cpinfo);
943 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
944 }
945
946 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
947
948 /*
949 * Some fonts, e.g. 9-pt Courier, draw their underlines
950 * outside their character cell. We successfully prevent
951 * screen corruption by clipping the text output, but then
952 * we lose the underline completely. Here we try to work
953 * out whether this is such a font, and if it is, we set a
954 * flag that causes underlines to be drawn by hand.
955 *
956 * Having tried other more sophisticated approaches (such
957 * as examining the TEXTMETRIC structure or requesting the
958 * height of a string), I think we'll do this the brute
959 * force way: we create a small bitmap, draw an underlined
960 * space on it, and test to see whether any pixels are
961 * foreground-coloured. (Since we expect the underline to
962 * go all the way across the character cell, we only search
963 * down a single column of the bitmap, half way across.)
964 */
965 {
966 HDC und_dc;
967 HBITMAP und_bm, und_oldbm;
968 int i, gotit;
969 COLORREF c;
970
971 und_dc = CreateCompatibleDC(hdc);
972 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
973 und_oldbm = SelectObject(und_dc, und_bm);
974 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
975 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
976 SetTextColor(und_dc, RGB(255, 255, 255));
977 SetBkColor(und_dc, RGB(0, 0, 0));
978 SetBkMode(und_dc, OPAQUE);
979 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
980 gotit = FALSE;
981 for (i = 0; i < font_height; i++) {
982 c = GetPixel(und_dc, font_width / 2, i);
983 if (c != RGB(0, 0, 0))
984 gotit = TRUE;
985 }
986 SelectObject(und_dc, und_oldbm);
987 DeleteObject(und_bm);
988 DeleteDC(und_dc);
989 if (!gotit) {
990 und_mode = UND_LINE;
991 DeleteObject(fonts[FONT_UNDERLINE]);
992 fonts[FONT_UNDERLINE] = 0;
993 }
994 }
995
996 if (bold_mode == BOLD_FONT) {
997 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
998 }
999 #undef f
1000
1001 descent = tm.tmAscent + 1;
1002 if (descent >= font_height)
1003 descent = font_height - 1;
1004
1005 for (i = 0; i < 3; i++) {
1006 if (fonts[i]) {
1007 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1008 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1009 else
1010 fontsize[i] = -i;
1011 } else
1012 fontsize[i] = -i;
1013 }
1014
1015 ReleaseDC(hwnd, hdc);
1016
1017 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1018 und_mode = UND_LINE;
1019 DeleteObject(fonts[FONT_UNDERLINE]);
1020 fonts[FONT_UNDERLINE] = 0;
1021 }
1022
1023 if (bold_mode == BOLD_FONT &&
1024 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1025 bold_mode = BOLD_SHADOW;
1026 DeleteObject(fonts[FONT_BOLD]);
1027 fonts[FONT_BOLD] = 0;
1028 }
1029 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1030
1031 init_ucs_tables();
1032 }
1033
1034 static void another_font(int fontno)
1035 {
1036 int basefont;
1037 int fw_dontcare, fw_bold;
1038 int c, u, w, x;
1039 char *s;
1040
1041 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1042 return;
1043
1044 basefont = (fontno & ~(FONT_BOLDUND));
1045 if (basefont != fontno && !fontflag[basefont])
1046 another_font(basefont);
1047
1048 if (cfg.fontisbold) {
1049 fw_dontcare = FW_BOLD;
1050 fw_bold = FW_HEAVY;
1051 } else {
1052 fw_dontcare = FW_DONTCARE;
1053 fw_bold = FW_BOLD;
1054 }
1055
1056 c = cfg.fontcharset;
1057 w = fw_dontcare;
1058 u = FALSE;
1059 s = cfg.font;
1060 x = font_width;
1061
1062 if (fontno & FONT_WIDE)
1063 x *= 2;
1064 if (fontno & FONT_NARROW)
1065 x = (x+1)/2;
1066 if (fontno & FONT_OEM)
1067 c = OEM_CHARSET;
1068 if (fontno & FONT_BOLD)
1069 w = fw_bold;
1070 if (fontno & FONT_UNDERLINE)
1071 u = TRUE;
1072
1073 fonts[fontno] =
1074 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1075 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1076 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1077 FIXED_PITCH | FF_DONTCARE, s);
1078
1079 fontflag[fontno] = 1;
1080 }
1081
1082 static void deinit_fonts(void)
1083 {
1084 int i;
1085 for (i = 0; i < FONT_MAXNO; i++) {
1086 if (fonts[i])
1087 DeleteObject(fonts[i]);
1088 fonts[i] = 0;
1089 fontflag[i] = 0;
1090 }
1091 }
1092
1093 void request_resize(int w, int h)
1094 {
1095 int width, height;
1096
1097 /* If the window is maximized supress resizing attempts */
1098 if (IsZoomed(hwnd)) {
1099 if (cfg.resize_action != RESIZE_FONT)
1100 return;
1101 }
1102
1103 if (cfg.resize_action == RESIZE_DISABLED) return;
1104 if (h == rows && w == cols) return;
1105
1106 /* Sanity checks ... */
1107 {
1108 static int first_time = 1;
1109 static RECT ss;
1110
1111 switch (first_time) {
1112 case 1:
1113 /* Get the size of the screen */
1114 if (GetClientRect(GetDesktopWindow(), &ss))
1115 /* first_time = 0 */ ;
1116 else {
1117 first_time = 2;
1118 break;
1119 }
1120 case 0:
1121 /* Make sure the values are sane */
1122 width = (ss.right - ss.left - extra_width) / 4;
1123 height = (ss.bottom - ss.top - extra_height) / 6;
1124
1125 if (w > width || h > height)
1126 return;
1127 if (w < 15)
1128 w = 15;
1129 if (h < 1)
1130 h = 1;
1131 }
1132 }
1133
1134 term_size(h, w, cfg.savelines);
1135
1136 if (cfg.resize_action != RESIZE_FONT) {
1137 width = extra_width + font_width * w;
1138 height = extra_height + font_height * h;
1139
1140 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1141 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1142 SWP_NOMOVE | SWP_NOZORDER);
1143 } else
1144 reset_window(0);
1145
1146 InvalidateRect(hwnd, NULL, TRUE);
1147 }
1148
1149 static void reset_window(int reinit) {
1150 /*
1151 * This function decides how to resize or redraw when the
1152 * user changes something.
1153 *
1154 * This function doesn't like to change the terminal size but if the
1155 * font size is locked that may be it's only soluion.
1156 */
1157 int win_width, win_height;
1158 RECT cr, wr;
1159
1160 #ifdef RDB_DEBUG_PATCH
1161 debug((27, "reset_window()"));
1162 #endif
1163
1164 /* Current window sizes ... */
1165 GetWindowRect(hwnd, &wr);
1166 GetClientRect(hwnd, &cr);
1167
1168 win_width = cr.right - cr.left;
1169 win_height = cr.bottom - cr.top;
1170
1171 /* Are we being forced to reload the fonts ? */
1172 if (reinit>1) {
1173 #ifdef RDB_DEBUG_PATCH
1174 debug((27, "reset_window() -- Forced deinit"));
1175 #endif
1176 deinit_fonts();
1177 init_fonts(0,0);
1178 }
1179
1180 /* Oh, looks like we're minimised */
1181 if (win_width == 0 || win_height == 0)
1182 return;
1183
1184 /* Is the window out of position ? */
1185 if ( !reinit &&
1186 (offset_width != (win_width-font_width*cols)/2 ||
1187 offset_height != (win_height-font_height*rows)/2) ){
1188 offset_width = (win_width-font_width*cols)/2;
1189 offset_height = (win_height-font_height*rows)/2;
1190 InvalidateRect(hwnd, NULL, TRUE);
1191 #ifdef RDB_DEBUG_PATCH
1192 debug((27, "reset_window() -> Reposition terminal"));
1193 #endif
1194 }
1195
1196 if (IsZoomed(hwnd)) {
1197 /* We're fullscreen, this means we must not change the size of
1198 * the window so it's the font size or the terminal itself.
1199 */
1200
1201 extra_width = wr.right - wr.left - cr.right + cr.left;
1202 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1203
1204 if (cfg.resize_action == RESIZE_FONT) {
1205 if ( font_width != win_width/cols ||
1206 font_height != win_height/rows) {
1207 deinit_fonts();
1208 init_fonts(win_width/cols, win_height/rows);
1209 offset_width = (win_width-font_width*cols)/2;
1210 offset_height = (win_height-font_height*rows)/2;
1211 InvalidateRect(hwnd, NULL, TRUE);
1212 #ifdef RDB_DEBUG_PATCH
1213 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1214 font_width, font_height));
1215 #endif
1216 }
1217 } else {
1218 if ( font_width != win_width/cols ||
1219 font_height != win_height/rows) {
1220 /* Our only choice at this point is to change the
1221 * size of the terminal; Oh well.
1222 */
1223 term_size( win_height/font_height, win_width/font_width,
1224 cfg.savelines);
1225 offset_width = (win_width-font_width*cols)/2;
1226 offset_height = (win_height-font_height*rows)/2;
1227 InvalidateRect(hwnd, NULL, TRUE);
1228 #ifdef RDB_DEBUG_PATCH
1229 debug((27, "reset_window() -> Zoomed term_size"));
1230 #endif
1231 }
1232 }
1233 return;
1234 }
1235
1236 /* Hmm, a force re-init means we should ignore the current window
1237 * so we resize to the default font size.
1238 */
1239 if (reinit>0) {
1240 #ifdef RDB_DEBUG_PATCH
1241 debug((27, "reset_window() -> Forced re-init"));
1242 #endif
1243
1244 offset_width = offset_height = cfg.window_border;
1245 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1246 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1247
1248 if (win_width != font_width*cols + offset_width*2 ||
1249 win_height != font_height*rows + offset_height*2) {
1250
1251 /* If this is too large windows will resize it to the maximum
1252 * allowed window size, we will then be back in here and resize
1253 * the font or terminal to fit.
1254 */
1255 SetWindowPos(hwnd, NULL, 0, 0,
1256 font_width*cols + extra_width,
1257 font_height*rows + extra_height,
1258 SWP_NOMOVE | SWP_NOZORDER);
1259 }
1260 return;
1261 }
1262
1263 /* Okay the user doesn't want us to change the font so we try the
1264 * window. But that may be too big for the screen which forces us
1265 * to change the terminal.
1266 */
1267 if ((cfg.resize_action != RESIZE_FONT && reinit==0) || reinit>0) {
1268 offset_width = offset_height = cfg.window_border;
1269 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1270 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1271
1272 if (win_width != font_width*cols + offset_width*2 ||
1273 win_height != font_height*rows + offset_height*2) {
1274
1275 static RECT ss;
1276 int width, height;
1277
1278 GetClientRect(GetDesktopWindow(), &ss);
1279 width = (ss.right - ss.left - extra_width) / font_width;
1280 height = (ss.bottom - ss.top - extra_height) / font_height;
1281
1282 /* Grrr too big */
1283 if ( rows > height || cols > width ) {
1284 if ( height > rows ) height = rows;
1285 if ( width > cols ) width = cols;
1286 term_size(height, width, cfg.savelines);
1287 #ifdef RDB_DEBUG_PATCH
1288 debug((27, "reset_window() -> term resize to (%d,%d)",
1289 height, width));
1290 #endif
1291 }
1292
1293 SetWindowPos(hwnd, NULL, 0, 0,
1294 font_width*cols + extra_width,
1295 font_height*rows + extra_height,
1296 SWP_NOMOVE | SWP_NOZORDER);
1297
1298 InvalidateRect(hwnd, NULL, TRUE);
1299 #ifdef RDB_DEBUG_PATCH
1300 debug((27, "reset_window() -> window resize to (%d,%d)",
1301 font_width*cols + extra_width,
1302 font_height*rows + extra_height));
1303 #endif
1304 }
1305 return;
1306 }
1307
1308 /* We're allowed to or must change the font but do we want to ? */
1309
1310 if (font_width != (win_width-cfg.window_border*2)/cols ||
1311 font_height != (win_height-cfg.window_border*2)/rows) {
1312
1313 deinit_fonts();
1314 init_fonts((win_width-cfg.window_border*2)/cols,
1315 (win_height-cfg.window_border*2)/rows);
1316 offset_width = (win_width-font_width*cols)/2;
1317 offset_height = (win_height-font_height*rows)/2;
1318
1319 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1320 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1321
1322 InvalidateRect(hwnd, NULL, TRUE);
1323 #ifdef RDB_DEBUG_PATCH
1324 debug((25, "reset_window() -> font resize to (%d,%d)",
1325 font_width, font_height));
1326 #endif
1327 }
1328 }
1329
1330 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1331 {
1332 int thistime = GetMessageTime();
1333
1334 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1335 lastbtn = MBT_NOTHING;
1336 term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1337 return;
1338 }
1339
1340 if (lastbtn == b && thistime - lasttime < dbltime) {
1341 lastact = (lastact == MA_CLICK ? MA_2CLK :
1342 lastact == MA_2CLK ? MA_3CLK :
1343 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1344 } else {
1345 lastbtn = b;
1346 lastact = MA_CLICK;
1347 }
1348 if (lastact != MA_NOTHING)
1349 term_mouse(b, lastact, x, y, shift, ctrl, alt);
1350 lasttime = thistime;
1351 }
1352
1353 /*
1354 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1355 * into a cooked one (SELECT, EXTEND, PASTE).
1356 */
1357 Mouse_Button translate_button(Mouse_Button button)
1358 {
1359 if (button == MBT_LEFT)
1360 return MBT_SELECT;
1361 if (button == MBT_MIDDLE)
1362 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1363 if (button == MBT_RIGHT)
1364 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1365 return 0; /* shouldn't happen */
1366 }
1367
1368 static void show_mouseptr(int show)
1369 {
1370 static int cursor_visible = 1;
1371 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1372 show = 1;
1373 if (cursor_visible && !show)
1374 ShowCursor(FALSE);
1375 else if (!cursor_visible && show)
1376 ShowCursor(TRUE);
1377 cursor_visible = show;
1378 }
1379
1380 static int is_alt_pressed(void)
1381 {
1382 BYTE keystate[256];
1383 int r = GetKeyboardState(keystate);
1384 if (!r)
1385 return FALSE;
1386 if (keystate[VK_MENU] & 0x80)
1387 return TRUE;
1388 if (keystate[VK_RMENU] & 0x80)
1389 return TRUE;
1390 return FALSE;
1391 }
1392
1393 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1394 WPARAM wParam, LPARAM lParam)
1395 {
1396 HDC hdc;
1397 static int ignore_clip = FALSE;
1398 static int resizing = FALSE;
1399 static int need_backend_resize = FALSE;
1400
1401 switch (message) {
1402 case WM_TIMER:
1403 if (pending_netevent)
1404 enact_pending_netevent();
1405 term_out();
1406 noise_regular();
1407 HideCaret(hwnd);
1408 term_update();
1409 ShowCaret(hwnd);
1410 if (cfg.ping_interval > 0) {
1411 time_t now;
1412 time(&now);
1413 if (now - last_movement > cfg.ping_interval) {
1414 back->special(TS_PING);
1415 last_movement = now;
1416 }
1417 }
1418 net_pending_errors();
1419 return 0;
1420 case WM_CREATE:
1421 break;
1422 case WM_CLOSE:
1423 show_mouseptr(1);
1424 if (!cfg.warn_on_close || session_closed ||
1425 MessageBox(hwnd,
1426 "Are you sure you want to close this session?",
1427 "PuTTY Exit Confirmation",
1428 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1429 DestroyWindow(hwnd);
1430 return 0;
1431 case WM_DESTROY:
1432 show_mouseptr(1);
1433 PostQuitMessage(0);
1434 return 0;
1435 case WM_SYSCOMMAND:
1436 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1437 case IDM_SHOWLOG:
1438 showeventlog(hwnd);
1439 break;
1440 case IDM_NEWSESS:
1441 case IDM_DUPSESS:
1442 case IDM_SAVEDSESS:
1443 {
1444 char b[2048];
1445 char c[30], *cl;
1446 int freecl = FALSE;
1447 STARTUPINFO si;
1448 PROCESS_INFORMATION pi;
1449 HANDLE filemap = NULL;
1450
1451 if (wParam == IDM_DUPSESS) {
1452 /*
1453 * Allocate a file-mapping memory chunk for the
1454 * config structure.
1455 */
1456 SECURITY_ATTRIBUTES sa;
1457 Config *p;
1458
1459 sa.nLength = sizeof(sa);
1460 sa.lpSecurityDescriptor = NULL;
1461 sa.bInheritHandle = TRUE;
1462 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1463 &sa,
1464 PAGE_READWRITE,
1465 0, sizeof(Config), NULL);
1466 if (filemap) {
1467 p = (Config *) MapViewOfFile(filemap,
1468 FILE_MAP_WRITE,
1469 0, 0, sizeof(Config));
1470 if (p) {
1471 *p = cfg; /* structure copy */
1472 UnmapViewOfFile(p);
1473 }
1474 }
1475 sprintf(c, "putty &%p", filemap);
1476 cl = c;
1477 } else if (wParam == IDM_SAVEDSESS) {
1478 char *session =
1479 sessions[(lParam - IDM_SAVED_MIN) / 16];
1480 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1481 if (!cl)
1482 cl = NULL; /* not a very important failure mode */
1483 else {
1484 sprintf(cl, "putty @%s", session);
1485 freecl = TRUE;
1486 }
1487 } else
1488 cl = NULL;
1489
1490 GetModuleFileName(NULL, b, sizeof(b) - 1);
1491 si.cb = sizeof(si);
1492 si.lpReserved = NULL;
1493 si.lpDesktop = NULL;
1494 si.lpTitle = NULL;
1495 si.dwFlags = 0;
1496 si.cbReserved2 = 0;
1497 si.lpReserved2 = NULL;
1498 CreateProcess(b, cl, NULL, NULL, TRUE,
1499 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1500
1501 if (filemap)
1502 CloseHandle(filemap);
1503 if (freecl)
1504 sfree(cl);
1505 }
1506 break;
1507 case IDM_RECONF:
1508 {
1509 Config prev_cfg;
1510 int init_lvl = 1;
1511
1512 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1513 prev_cfg = cfg;
1514
1515 if (!do_reconfig(hwnd))
1516 break;
1517
1518 /* If user forcibly disables full-screen, gracefully unzoom */
1519 if (full_screen && !cfg.fullscreenonaltenter) {
1520 flip_full_screen();
1521 }
1522
1523 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1524 prev_cfg.logtype != cfg.logtype) {
1525 logfclose(); /* reset logging */
1526 logfopen();
1527 }
1528
1529 sfree(logpal);
1530 /*
1531 * Flush the line discipline's edit buffer in the
1532 * case where local editing has just been disabled.
1533 */
1534 ldisc_send(NULL, 0, 0);
1535 if (pal)
1536 DeleteObject(pal);
1537 logpal = NULL;
1538 pal = NULL;
1539 cfgtopalette();
1540 init_palette();
1541
1542 /* Screen size changed ? */
1543 if (cfg.height != prev_cfg.height ||
1544 cfg.width != prev_cfg.width ||
1545 cfg.savelines != prev_cfg.savelines ||
1546 cfg.resize_action != RESIZE_TERM)
1547 term_size(cfg.height, cfg.width, cfg.savelines);
1548
1549 /* Enable or disable the scroll bar, etc */
1550 {
1551 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1552 LONG nexflag, exflag =
1553 GetWindowLong(hwnd, GWL_EXSTYLE);
1554
1555 nexflag = exflag;
1556 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1557 if (cfg.alwaysontop) {
1558 nexflag |= WS_EX_TOPMOST;
1559 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1560 SWP_NOMOVE | SWP_NOSIZE);
1561 } else {
1562 nexflag &= ~(WS_EX_TOPMOST);
1563 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1564 SWP_NOMOVE | SWP_NOSIZE);
1565 }
1566 }
1567 if (cfg.sunken_edge)
1568 nexflag |= WS_EX_CLIENTEDGE;
1569 else
1570 nexflag &= ~(WS_EX_CLIENTEDGE);
1571
1572 nflg = flag;
1573 if (cfg.scrollbar)
1574 nflg |= WS_VSCROLL;
1575 else
1576 nflg &= ~WS_VSCROLL;
1577 if (cfg.resize_action == RESIZE_DISABLED)
1578 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1579 else
1580 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1581
1582 if (nflg != flag || nexflag != exflag) {
1583 if (nflg != flag)
1584 SetWindowLong(hwnd, GWL_STYLE, nflg);
1585 if (nexflag != exflag)
1586 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1587
1588 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1589 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1590 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1591 SWP_FRAMECHANGED);
1592
1593 init_lvl = 2;
1594 }
1595 }
1596
1597 /* Oops */
1598 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1599 force_normal(hwnd);
1600 init_lvl = 2;
1601 }
1602
1603 set_title(cfg.wintitle);
1604 if (IsIconic(hwnd)) {
1605 SetWindowText(hwnd,
1606 cfg.win_name_always ? window_name :
1607 icon_name);
1608 }
1609
1610 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1611 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1612 cfg.fontisbold != prev_cfg.fontisbold ||
1613 cfg.fontheight != prev_cfg.fontheight ||
1614 cfg.fontcharset != prev_cfg.fontcharset ||
1615 cfg.vtmode != prev_cfg.vtmode ||
1616 cfg.bold_colour != prev_cfg.bold_colour ||
1617 (cfg.resize_action != RESIZE_FONT &&
1618 prev_cfg.resize_action == RESIZE_FONT))
1619 init_lvl = 2;
1620
1621 InvalidateRect(hwnd, NULL, TRUE);
1622 reset_window(init_lvl);
1623 net_pending_errors();
1624 }
1625 break;
1626 case IDM_COPYALL:
1627 term_copyall();
1628 break;
1629 case IDM_CLRSB:
1630 term_clrsb();
1631 break;
1632 case IDM_RESET:
1633 term_pwron();
1634 break;
1635 case IDM_TEL_AYT:
1636 back->special(TS_AYT);
1637 net_pending_errors();
1638 break;
1639 case IDM_TEL_BRK:
1640 back->special(TS_BRK);
1641 net_pending_errors();
1642 break;
1643 case IDM_TEL_SYNCH:
1644 back->special(TS_SYNCH);
1645 net_pending_errors();
1646 break;
1647 case IDM_TEL_EC:
1648 back->special(TS_EC);
1649 net_pending_errors();
1650 break;
1651 case IDM_TEL_EL:
1652 back->special(TS_EL);
1653 net_pending_errors();
1654 break;
1655 case IDM_TEL_GA:
1656 back->special(TS_GA);
1657 net_pending_errors();
1658 break;
1659 case IDM_TEL_NOP:
1660 back->special(TS_NOP);
1661 net_pending_errors();
1662 break;
1663 case IDM_TEL_ABORT:
1664 back->special(TS_ABORT);
1665 net_pending_errors();
1666 break;
1667 case IDM_TEL_AO:
1668 back->special(TS_AO);
1669 net_pending_errors();
1670 break;
1671 case IDM_TEL_IP:
1672 back->special(TS_IP);
1673 net_pending_errors();
1674 break;
1675 case IDM_TEL_SUSP:
1676 back->special(TS_SUSP);
1677 net_pending_errors();
1678 break;
1679 case IDM_TEL_EOR:
1680 back->special(TS_EOR);
1681 net_pending_errors();
1682 break;
1683 case IDM_TEL_EOF:
1684 back->special(TS_EOF);
1685 net_pending_errors();
1686 break;
1687 case IDM_ABOUT:
1688 showabout(hwnd);
1689 break;
1690 case SC_MOUSEMENU:
1691 /*
1692 * We get this if the System menu has been activated
1693 * using the mouse.
1694 */
1695 show_mouseptr(1);
1696 break;
1697 case SC_KEYMENU:
1698 /*
1699 * We get this if the System menu has been activated
1700 * using the keyboard. This might happen from within
1701 * TranslateKey, in which case it really wants to be
1702 * followed by a `space' character to actually _bring
1703 * the menu up_ rather than just sitting there in
1704 * `ready to appear' state.
1705 */
1706 show_mouseptr(1); /* make sure pointer is visible */
1707 if( lParam == 0 )
1708 PostMessage(hwnd, WM_CHAR, ' ', 0);
1709 break;
1710 case IDM_FULLSCREEN:
1711 flip_full_screen();
1712 break;
1713 default:
1714 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1715 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1716 }
1717 }
1718 break;
1719
1720 #define X_POS(l) ((int)(short)LOWORD(l))
1721 #define Y_POS(l) ((int)(short)HIWORD(l))
1722
1723 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1724 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1725 #define WHEEL_DELTA 120
1726 case WM_MOUSEWHEEL:
1727 {
1728 wheel_accumulator += (short) HIWORD(wParam);
1729 wParam = LOWORD(wParam);
1730
1731 /* process events when the threshold is reached */
1732 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1733 int b;
1734
1735 /* reduce amount for next time */
1736 if (wheel_accumulator > 0) {
1737 b = MBT_WHEEL_UP;
1738 wheel_accumulator -= WHEEL_DELTA;
1739 } else if (wheel_accumulator < 0) {
1740 b = MBT_WHEEL_DOWN;
1741 wheel_accumulator += WHEEL_DELTA;
1742 } else
1743 break;
1744
1745 if (send_raw_mouse) {
1746 /* send a mouse-down followed by a mouse up */
1747
1748 term_mouse(b,
1749 MA_CLICK,
1750 TO_CHR_X(X_POS(lParam)),
1751 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1752 wParam & MK_CONTROL, is_alt_pressed());
1753 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1754 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1755 wParam & MK_CONTROL, is_alt_pressed());
1756 } else {
1757 /* trigger a scroll */
1758 term_scroll(0,
1759 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1760 }
1761 }
1762 return 0;
1763 }
1764 case WM_LBUTTONDOWN:
1765 case WM_MBUTTONDOWN:
1766 case WM_RBUTTONDOWN:
1767 case WM_LBUTTONUP:
1768 case WM_MBUTTONUP:
1769 case WM_RBUTTONUP:
1770 {
1771 int button, press;
1772
1773 switch (message) {
1774 case WM_LBUTTONDOWN:
1775 button = MBT_LEFT;
1776 press = 1;
1777 break;
1778 case WM_MBUTTONDOWN:
1779 button = MBT_MIDDLE;
1780 press = 1;
1781 break;
1782 case WM_RBUTTONDOWN:
1783 button = MBT_RIGHT;
1784 press = 1;
1785 break;
1786 case WM_LBUTTONUP:
1787 button = MBT_LEFT;
1788 press = 0;
1789 break;
1790 case WM_MBUTTONUP:
1791 button = MBT_MIDDLE;
1792 press = 0;
1793 break;
1794 case WM_RBUTTONUP:
1795 button = MBT_RIGHT;
1796 press = 0;
1797 break;
1798 default:
1799 button = press = 0; /* shouldn't happen */
1800 }
1801 show_mouseptr(1);
1802 if (press) {
1803 click(button,
1804 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1805 wParam & MK_SHIFT, wParam & MK_CONTROL,
1806 is_alt_pressed());
1807 SetCapture(hwnd);
1808 } else {
1809 term_mouse(button, MA_RELEASE,
1810 TO_CHR_X(X_POS(lParam)),
1811 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1812 wParam & MK_CONTROL, is_alt_pressed());
1813 ReleaseCapture();
1814 }
1815 }
1816 return 0;
1817 case WM_MOUSEMOVE:
1818 show_mouseptr(1);
1819 /*
1820 * Add the mouse position and message time to the random
1821 * number noise.
1822 */
1823 noise_ultralight(lParam);
1824
1825 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1826 Mouse_Button b;
1827 if (wParam & MK_LBUTTON)
1828 b = MBT_LEFT;
1829 else if (wParam & MK_MBUTTON)
1830 b = MBT_MIDDLE;
1831 else
1832 b = MBT_RIGHT;
1833 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1834 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1835 wParam & MK_CONTROL, is_alt_pressed());
1836 }
1837 return 0;
1838 case WM_NCMOUSEMOVE:
1839 show_mouseptr(1);
1840 noise_ultralight(lParam);
1841 return 0;
1842 case WM_IGNORE_CLIP:
1843 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1844 break;
1845 case WM_DESTROYCLIPBOARD:
1846 if (!ignore_clip)
1847 term_deselect();
1848 ignore_clip = FALSE;
1849 return 0;
1850 case WM_PAINT:
1851 {
1852 PAINTSTRUCT p;
1853 HideCaret(hwnd);
1854 hdc = BeginPaint(hwnd, &p);
1855 if (pal) {
1856 SelectPalette(hdc, pal, TRUE);
1857 RealizePalette(hdc);
1858 }
1859 term_paint(hdc,
1860 (p.rcPaint.left-offset_width)/font_width,
1861 (p.rcPaint.top-offset_height)/font_height,
1862 (p.rcPaint.right-offset_width-1)/font_width,
1863 (p.rcPaint.bottom-offset_height-1)/font_height);
1864
1865 if (p.fErase ||
1866 p.rcPaint.left < offset_width ||
1867 p.rcPaint.top < offset_height ||
1868 p.rcPaint.right >= offset_width + font_width*cols ||
1869 p.rcPaint.bottom>= offset_height + font_height*rows)
1870 {
1871 HBRUSH fillcolour, oldbrush;
1872 HPEN edge, oldpen;
1873 fillcolour = CreateSolidBrush (
1874 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1875 oldbrush = SelectObject(hdc, fillcolour);
1876 edge = CreatePen(PS_SOLID, 0,
1877 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1878 oldpen = SelectObject(hdc, edge);
1879
1880 ExcludeClipRect(hdc,
1881 offset_width, offset_height,
1882 offset_width+font_width*cols,
1883 offset_height+font_height*rows);
1884
1885 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1886 p.rcPaint.right, p.rcPaint.bottom);
1887
1888 // SelectClipRgn(hdc, NULL);
1889
1890 SelectObject(hdc, oldbrush);
1891 DeleteObject(fillcolour);
1892 SelectObject(hdc, oldpen);
1893 DeleteObject(edge);
1894 }
1895 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1896 SelectObject(hdc, GetStockObject(WHITE_PEN));
1897 EndPaint(hwnd, &p);
1898 ShowCaret(hwnd);
1899 }
1900 return 0;
1901 case WM_NETEVENT:
1902 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1903 * but the only one that's likely to try to overload us is FD_READ.
1904 * This means buffering just one is fine.
1905 */
1906 if (pending_netevent)
1907 enact_pending_netevent();
1908
1909 pending_netevent = TRUE;
1910 pend_netevent_wParam = wParam;
1911 pend_netevent_lParam = lParam;
1912 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1913 enact_pending_netevent();
1914
1915 time(&last_movement);
1916 return 0;
1917 case WM_SETFOCUS:
1918 has_focus = TRUE;
1919 CreateCaret(hwnd, caretbm, font_width, font_height);
1920 ShowCaret(hwnd);
1921 flash_window(0); /* stop */
1922 compose_state = 0;
1923 term_out();
1924 term_update();
1925 break;
1926 case WM_KILLFOCUS:
1927 show_mouseptr(1);
1928 has_focus = FALSE;
1929 DestroyCaret();
1930 term_out();
1931 term_update();
1932 break;
1933 case WM_ENTERSIZEMOVE:
1934 #ifdef RDB_DEBUG_PATCH
1935 debug((27, "WM_ENTERSIZEMOVE"));
1936 #endif
1937 EnableSizeTip(1);
1938 resizing = TRUE;
1939 need_backend_resize = FALSE;
1940 break;
1941 case WM_EXITSIZEMOVE:
1942 EnableSizeTip(0);
1943 resizing = FALSE;
1944 #ifdef RDB_DEBUG_PATCH
1945 debug((27, "WM_EXITSIZEMOVE"));
1946 #endif
1947 if (need_backend_resize) {
1948 term_size(cfg.height, cfg.width, cfg.savelines);
1949 InvalidateRect(hwnd, NULL, TRUE);
1950 }
1951 break;
1952 case WM_SIZING:
1953 /*
1954 * This does two jobs:
1955 * 1) Keep the sizetip uptodate
1956 * 2) Make sure the window size is _stepped_ in units of the font size.
1957 */
1958 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1959 int width, height, w, h, ew, eh;
1960 LPRECT r = (LPRECT) lParam;
1961
1962 if ( !need_backend_resize &&
1963 (cfg.height != rows || cfg.width != cols )) {
1964 /*
1965 * Great! It seems the host has been changing the terminal
1966 * size, well the user is now grabbing so this is probably
1967 * the least confusing solution in the long run even though
1968 * it a is suprise. Unfortunatly the only way to prevent
1969 * this seems to be to let the host change the window size
1970 * and as that's a user option we're still right back here.
1971 */
1972 term_size(cfg.height, cfg.width, cfg.savelines);
1973 reset_window(2);
1974 InvalidateRect(hwnd, NULL, TRUE);
1975 need_backend_resize = TRUE;
1976 }
1977
1978 width = r->right - r->left - extra_width;
1979 height = r->bottom - r->top - extra_height;
1980 w = (width + font_width / 2) / font_width;
1981 if (w < 1)
1982 w = 1;
1983 h = (height + font_height / 2) / font_height;
1984 if (h < 1)
1985 h = 1;
1986 UpdateSizeTip(hwnd, w, h);
1987 ew = width - w * font_width;
1988 eh = height - h * font_height;
1989 if (ew != 0) {
1990 if (wParam == WMSZ_LEFT ||
1991 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1992 r->left += ew;
1993 else
1994 r->right -= ew;
1995 }
1996 if (eh != 0) {
1997 if (wParam == WMSZ_TOP ||
1998 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1999 r->top += eh;
2000 else
2001 r->bottom -= eh;
2002 }
2003 if (ew || eh)
2004 return 1;
2005 else
2006 return 0;
2007 } else {
2008 int width, height, w, h, rv = 0;
2009 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2010 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2011 LPRECT r = (LPRECT) lParam;
2012
2013 width = r->right - r->left - ex_width;
2014 height = r->bottom - r->top - ex_height;
2015
2016 w = (width + cols/2)/cols;
2017 h = (height + rows/2)/rows;
2018 if ( r->right != r->left + w*cols + ex_width)
2019 rv = 1;
2020
2021 if (wParam == WMSZ_LEFT ||
2022 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2023 r->left = r->right - w*cols - ex_width;
2024 else
2025 r->right = r->left + w*cols + ex_width;
2026
2027 if (r->bottom != r->top + h*rows + ex_height)
2028 rv = 1;
2029
2030 if (wParam == WMSZ_TOP ||
2031 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2032 r->top = r->bottom - h*rows - ex_height;
2033 else
2034 r->bottom = r->top + h*rows + ex_height;
2035
2036 return rv;
2037 }
2038 /* break; (never reached) */
2039 case WM_SIZE:
2040 #ifdef RDB_DEBUG_PATCH
2041 debug((27, "WM_SIZE %s (%d,%d)",
2042 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2043 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2044 (wParam == SIZE_RESTORED && resizing) ? "to":
2045 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2046 "...",
2047 LOWORD(lParam), HIWORD(lParam)));
2048 #endif
2049 if (wParam == SIZE_MINIMIZED) {
2050 SetWindowText(hwnd,
2051 cfg.win_name_always ? window_name : icon_name);
2052 break;
2053 }
2054 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2055 SetWindowText(hwnd, window_name);
2056
2057 if (cfg.resize_action == RESIZE_DISABLED) {
2058 /* A resize, well it better be a minimize. */
2059 reset_window(-1);
2060 } else {
2061
2062 int width, height, w, h;
2063
2064 width = LOWORD(lParam);
2065 height = HIWORD(lParam);
2066
2067 if (!resizing) {
2068 if (wParam == SIZE_MAXIMIZED) {
2069 was_zoomed = 1;
2070 prev_rows = rows;
2071 prev_cols = cols;
2072 if (cfg.resize_action != RESIZE_FONT) {
2073 w = width / font_width;
2074 if (w < 1) w = 1;
2075 h = height / font_height;
2076 if (h < 1) h = 1;
2077
2078 term_size(h, w, cfg.savelines);
2079 }
2080 reset_window(0);
2081 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2082 was_zoomed = 0;
2083 if (cfg.resize_action != RESIZE_FONT)
2084 term_size(prev_rows, prev_cols, cfg.savelines);
2085 reset_window(0);
2086 }
2087 /* This is an unexpected resize, these will normally happen
2088 * if the window is too large. Probably either the user
2089 * selected a huge font or the screen size has changed.
2090 *
2091 * This is also called with minimize.
2092 */
2093 else reset_window(-1);
2094 }
2095
2096 /*
2097 * Don't call back->size in mid-resize. (To prevent
2098 * massive numbers of resize events getting sent
2099 * down the connection during an NT opaque drag.)
2100 */
2101 if (resizing) {
2102 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2103 need_backend_resize = TRUE;
2104 w = (width-cfg.window_border*2) / font_width;
2105 if (w < 1) w = 1;
2106 h = (height-cfg.window_border*2) / font_height;
2107 if (h < 1) h = 1;
2108
2109 cfg.height = h;
2110 cfg.width = w;
2111 } else
2112 reset_window(0);
2113 }
2114 }
2115 return 0;
2116 case WM_VSCROLL:
2117 switch (LOWORD(wParam)) {
2118 case SB_BOTTOM:
2119 term_scroll(-1, 0);
2120 break;
2121 case SB_TOP:
2122 term_scroll(+1, 0);
2123 break;
2124 case SB_LINEDOWN:
2125 term_scroll(0, +1);
2126 break;
2127 case SB_LINEUP:
2128 term_scroll(0, -1);
2129 break;
2130 case SB_PAGEDOWN:
2131 term_scroll(0, +rows / 2);
2132 break;
2133 case SB_PAGEUP:
2134 term_scroll(0, -rows / 2);
2135 break;
2136 case SB_THUMBPOSITION:
2137 case SB_THUMBTRACK:
2138 term_scroll(1, HIWORD(wParam));
2139 break;
2140 }
2141 break;
2142 case WM_PALETTECHANGED:
2143 if ((HWND) wParam != hwnd && pal != NULL) {
2144 HDC hdc = get_ctx();
2145 if (hdc) {
2146 if (RealizePalette(hdc) > 0)
2147 UpdateColors(hdc);
2148 free_ctx(hdc);
2149 }
2150 }
2151 break;
2152 case WM_QUERYNEWPALETTE:
2153 if (pal != NULL) {
2154 HDC hdc = get_ctx();
2155 if (hdc) {
2156 if (RealizePalette(hdc) > 0)
2157 UpdateColors(hdc);
2158 free_ctx(hdc);
2159 return TRUE;
2160 }
2161 }
2162 return FALSE;
2163 case WM_KEYDOWN:
2164 case WM_SYSKEYDOWN:
2165 case WM_KEYUP:
2166 case WM_SYSKEYUP:
2167 /*
2168 * Add the scan code and keypress timing to the random
2169 * number noise.
2170 */
2171 noise_ultralight(lParam);
2172
2173 /*
2174 * We don't do TranslateMessage since it disassociates the
2175 * resulting CHAR message from the KEYDOWN that sparked it,
2176 * which we occasionally don't want. Instead, we process
2177 * KEYDOWN, and call the Win32 translator functions so that
2178 * we get the translations under _our_ control.
2179 */
2180 {
2181 unsigned char buf[20];
2182 int len;
2183
2184 if (wParam == VK_PROCESSKEY) {
2185 MSG m;
2186 m.hwnd = hwnd;
2187 m.message = WM_KEYDOWN;
2188 m.wParam = wParam;
2189 m.lParam = lParam & 0xdfff;
2190 TranslateMessage(&m);
2191 } else {
2192 len = TranslateKey(message, wParam, lParam, buf);
2193 if (len == -1)
2194 return DefWindowProc(hwnd, message, wParam, lParam);
2195
2196 if (len != 0) {
2197 /*
2198 * Interrupt an ongoing paste. I'm not sure
2199 * this is sensible, but for the moment it's
2200 * preferable to having to faff about buffering
2201 * things.
2202 */
2203 term_nopaste();
2204
2205 /*
2206 * We need not bother about stdin backlogs
2207 * here, because in GUI PuTTY we can't do
2208 * anything about it anyway; there's no means
2209 * of asking Windows to hold off on KEYDOWN
2210 * messages. We _have_ to buffer everything
2211 * we're sent.
2212 */
2213 ldisc_send(buf, len, 1);
2214 show_mouseptr(0);
2215 }
2216 }
2217 }
2218 net_pending_errors();
2219 return 0;
2220 case WM_INPUTLANGCHANGE:
2221 {
2222 /* wParam == Font number */
2223 /* lParam == Locale */
2224 char lbuf[20];
2225 HKL NewInputLocale = (HKL) lParam;
2226
2227 // lParam == GetKeyboardLayout(0);
2228
2229 GetLocaleInfo(LOWORD(NewInputLocale),
2230 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2231
2232 kbd_codepage = atoi(lbuf);
2233 }
2234 break;
2235 case WM_IME_COMPOSITION:
2236 {
2237 HIMC hIMC;
2238 int n;
2239 char *buff;
2240
2241 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2242 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2243
2244 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2245 break; /* fall back to DefWindowProc */
2246
2247 hIMC = ImmGetContext(hwnd);
2248 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2249
2250 if (n > 0) {
2251 buff = (char*) smalloc(n);
2252 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2253 luni_send((unsigned short *)buff, n / 2, 1);
2254 free(buff);
2255 }
2256 ImmReleaseContext(hwnd, hIMC);
2257 return 1;
2258 }
2259
2260 case WM_IME_CHAR:
2261 if (wParam & 0xFF00) {
2262 unsigned char buf[2];
2263
2264 buf[1] = wParam;
2265 buf[0] = wParam >> 8;
2266 lpage_send(kbd_codepage, buf, 2, 1);
2267 } else {
2268 char c = (unsigned char) wParam;
2269 lpage_send(kbd_codepage, &c, 1, 1);
2270 }
2271 return (0);
2272 case WM_CHAR:
2273 case WM_SYSCHAR:
2274 /*
2275 * Nevertheless, we are prepared to deal with WM_CHAR
2276 * messages, should they crop up. So if someone wants to
2277 * post the things to us as part of a macro manoeuvre,
2278 * we're ready to cope.
2279 */
2280 {
2281 char c = (unsigned char)wParam;
2282 lpage_send(CP_ACP, &c, 1, 1);
2283 }
2284 return 0;
2285 case WM_SETCURSOR:
2286 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2287 SetCursor(LoadCursor(NULL, IDC_ARROW));
2288 return TRUE;
2289 }
2290 }
2291
2292 return DefWindowProc(hwnd, message, wParam, lParam);
2293 }
2294
2295 /*
2296 * Move the system caret. (We maintain one, even though it's
2297 * invisible, for the benefit of blind people: apparently some
2298 * helper software tracks the system caret, so we should arrange to
2299 * have one.)
2300 */
2301 void sys_cursor(int x, int y)
2302 {
2303 COMPOSITIONFORM cf;
2304 HIMC hIMC;
2305
2306 if (!has_focus) return;
2307
2308 SetCaretPos(x * font_width + offset_width,
2309 y * font_height + offset_height);
2310
2311 /* IMM calls on Win98 and beyond only */
2312 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2313
2314 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2315 osVersion.dwMinorVersion == 0) return; /* 95 */
2316
2317 /* we should have the IMM functions */
2318 hIMC = ImmGetContext(hwnd);
2319 cf.dwStyle = CFS_POINT;
2320 cf.ptCurrentPos.x = x * font_width + offset_width;
2321 cf.ptCurrentPos.y = y * font_height + offset_height;
2322 ImmSetCompositionWindow(hIMC, &cf);
2323
2324 ImmReleaseContext(hwnd, hIMC);
2325 }
2326
2327 /*
2328 * Draw a line of text in the window, at given character
2329 * coordinates, in given attributes.
2330 *
2331 * We are allowed to fiddle with the contents of `text'.
2332 */
2333 void do_text(Context ctx, int x, int y, char *text, int len,
2334 unsigned long attr, int lattr)
2335 {
2336 COLORREF fg, bg, t;
2337 int nfg, nbg, nfont;
2338 HDC hdc = ctx;
2339 RECT line_box;
2340 int force_manual_underline = 0;
2341 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2342 int char_width = fnt_width;
2343 int text_adjust = 0;
2344 static int *IpDx = 0, IpDxLEN = 0;
2345
2346 if (attr & ATTR_WIDE)
2347 char_width *= 2;
2348
2349 if (len > IpDxLEN || IpDx[0] != char_width) {
2350 int i;
2351 if (len > IpDxLEN) {
2352 sfree(IpDx);
2353 IpDx = smalloc((len + 16) * sizeof(int));
2354 IpDxLEN = (len + 16);
2355 }
2356 for (i = 0; i < IpDxLEN; i++)
2357 IpDx[i] = char_width;
2358 }
2359
2360 /* Only want the left half of double width lines */
2361 if (lattr != LATTR_NORM && x*2 >= cols)
2362 return;
2363
2364 x *= fnt_width;
2365 y *= font_height;
2366 x += offset_width;
2367 y += offset_height;
2368
2369 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2370 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2371 attr ^= ATTR_CUR_XOR;
2372 }
2373
2374 nfont = 0;
2375 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2376 /* Assume a poorman font is borken in other ways too. */
2377 lattr = LATTR_WIDE;
2378 } else
2379 switch (lattr) {
2380 case LATTR_NORM:
2381 break;
2382 case LATTR_WIDE:
2383 nfont |= FONT_WIDE;
2384 break;
2385 default:
2386 nfont |= FONT_WIDE + FONT_HIGH;
2387 break;
2388 }
2389 if (attr & ATTR_NARROW)
2390 nfont |= FONT_NARROW;
2391
2392 /* Special hack for the VT100 linedraw glyphs. */
2393 if ((attr & CSET_MASK) == 0x2300) {
2394 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2395 switch ((unsigned char) (text[0])) {
2396 case 0xBA:
2397 text_adjust = -2 * font_height / 5;
2398 break;
2399 case 0xBB:
2400 text_adjust = -1 * font_height / 5;
2401 break;
2402 case 0xBC:
2403 text_adjust = font_height / 5;
2404 break;
2405 case 0xBD:
2406 text_adjust = 2 * font_height / 5;
2407 break;
2408 }
2409 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2410 text_adjust *= 2;
2411 attr &= ~CSET_MASK;
2412 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2413 attr |= (unitab_xterm['q'] & CSET_MASK);
2414 if (attr & ATTR_UNDER) {
2415 attr &= ~ATTR_UNDER;
2416 force_manual_underline = 1;
2417 }
2418 }
2419 }
2420
2421 /* Anything left as an original character set is unprintable. */
2422 if (DIRECT_CHAR(attr)) {
2423 attr &= ~CSET_MASK;
2424 attr |= 0xFF00;
2425 memset(text, 0xFD, len);
2426 }
2427
2428 /* OEM CP */
2429 if ((attr & CSET_MASK) == ATTR_OEMCP)
2430 nfont |= FONT_OEM;
2431
2432 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2433 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2434 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2435 nfont |= FONT_BOLD;
2436 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2437 nfont |= FONT_UNDERLINE;
2438 another_font(nfont);
2439 if (!fonts[nfont]) {
2440 if (nfont & FONT_UNDERLINE)
2441 force_manual_underline = 1;
2442 /* Don't do the same for manual bold, it could be bad news. */
2443
2444 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2445 }
2446 another_font(nfont);
2447 if (!fonts[nfont])
2448 nfont = FONT_NORMAL;
2449 if (attr & ATTR_REVERSE) {
2450 t = nfg;
2451 nfg = nbg;
2452 nbg = t;
2453 }
2454 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2455 nfg++;
2456 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2457 nbg++;
2458 fg = colours[nfg];
2459 bg = colours[nbg];
2460 SelectObject(hdc, fonts[nfont]);
2461 SetTextColor(hdc, fg);
2462 SetBkColor(hdc, bg);
2463 SetBkMode(hdc, OPAQUE);
2464 line_box.left = x;
2465 line_box.top = y;
2466 line_box.right = x + char_width * len;
2467 line_box.bottom = y + font_height;
2468
2469 /* Only want the left half of double width lines */
2470 if (line_box.right > font_width*cols+offset_width)
2471 line_box.right = font_width*cols+offset_width;
2472
2473 /* We're using a private area for direct to font. (512 chars.) */
2474 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2475 /* Ho Hum, dbcs fonts are a PITA! */
2476 /* To display on W9x I have to convert to UCS */
2477 static wchar_t *uni_buf = 0;
2478 static int uni_len = 0;
2479 int nlen, mptr;
2480 if (len > uni_len) {
2481 sfree(uni_buf);
2482 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2483 }
2484
2485 for(nlen = mptr = 0; mptr<len; mptr++) {
2486 uni_buf[nlen] = 0xFFFD;
2487 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2488 IpDx[nlen] += char_width;
2489 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2490 text+mptr, 2, uni_buf+nlen, 1);
2491 mptr++;
2492 }
2493 else
2494 {
2495 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2496 text+mptr, 1, uni_buf+nlen, 1);
2497 }
2498 nlen++;
2499 }
2500 if (nlen <= 0)
2501 return; /* Eeek! */
2502
2503 ExtTextOutW(hdc, x,
2504 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2505 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2506 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2507 SetBkMode(hdc, TRANSPARENT);
2508 ExtTextOutW(hdc, x - 1,
2509 y - font_height * (lattr ==
2510 LATTR_BOT) + text_adjust,
2511 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2512 }
2513
2514 IpDx[0] = -1;
2515 } else if (DIRECT_FONT(attr)) {
2516 ExtTextOut(hdc, x,
2517 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2518 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2519 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2520 SetBkMode(hdc, TRANSPARENT);
2521
2522 /* GRR: This draws the character outside it's box and can leave
2523 * 'droppings' even with the clip box! I suppose I could loop it
2524 * one character at a time ... yuk.
2525 *
2526 * Or ... I could do a test print with "W", and use +1 or -1 for this
2527 * shift depending on if the leftmost column is blank...
2528 */
2529 ExtTextOut(hdc, x - 1,
2530 y - font_height * (lattr ==
2531 LATTR_BOT) + text_adjust,
2532 ETO_CLIPPED, &line_box, text, len, IpDx);
2533 }
2534 } else {
2535 /* And 'normal' unicode characters */
2536 static WCHAR *wbuf = NULL;
2537 static int wlen = 0;
2538 int i;
2539 if (wlen < len) {
2540 sfree(wbuf);
2541 wlen = len;
2542 wbuf = smalloc(wlen * sizeof(WCHAR));
2543 }
2544 for (i = 0; i < len; i++)
2545 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2546
2547 ExtTextOutW(hdc, x,
2548 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2549 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2550
2551 /* And the shadow bold hack. */
2552 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2553 SetBkMode(hdc, TRANSPARENT);
2554 ExtTextOutW(hdc, x - 1,
2555 y - font_height * (lattr ==
2556 LATTR_BOT) + text_adjust,
2557 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2558 }
2559 }
2560 if (lattr != LATTR_TOP && (force_manual_underline ||
2561 (und_mode == UND_LINE
2562 && (attr & ATTR_UNDER)))) {
2563 HPEN oldpen;
2564 int dec = descent;
2565 if (lattr == LATTR_BOT)
2566 dec = dec * 2 - font_height;
2567
2568 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2569 MoveToEx(hdc, x, y + dec, NULL);
2570 LineTo(hdc, x + len * char_width, y + dec);
2571 oldpen = SelectObject(hdc, oldpen);
2572 DeleteObject(oldpen);
2573 }
2574 }
2575
2576 void do_cursor(Context ctx, int x, int y, char *text, int len,
2577 unsigned long attr, int lattr)
2578 {
2579
2580 int fnt_width;
2581 int char_width;
2582 HDC hdc = ctx;
2583 int ctype = cfg.cursor_type;
2584
2585 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2586 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2587 do_text(ctx, x, y, text, len, attr, lattr);
2588 return;
2589 }
2590 ctype = 2;
2591 attr |= TATTR_RIGHTCURS;
2592 }
2593
2594 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2595 if (attr & ATTR_WIDE)
2596 char_width *= 2;
2597 x *= fnt_width;
2598 y *= font_height;
2599 x += offset_width;
2600 y += offset_height;
2601
2602 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2603 POINT pts[5];
2604 HPEN oldpen;
2605 pts[0].x = pts[1].x = pts[4].x = x;
2606 pts[2].x = pts[3].x = x + char_width - 1;
2607 pts[0].y = pts[3].y = pts[4].y = y;
2608 pts[1].y = pts[2].y = y + font_height - 1;
2609 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2610 Polyline(hdc, pts, 5);
2611 oldpen = SelectObject(hdc, oldpen);
2612 DeleteObject(oldpen);
2613 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2614 int startx, starty, dx, dy, length, i;
2615 if (ctype == 1) {
2616 startx = x;
2617 starty = y + descent;
2618 dx = 1;
2619 dy = 0;
2620 length = char_width;
2621 } else {
2622 int xadjust = 0;
2623 if (attr & TATTR_RIGHTCURS)
2624 xadjust = char_width - 1;
2625 startx = x + xadjust;
2626 starty = y;
2627 dx = 0;
2628 dy = 1;
2629 length = font_height;
2630 }
2631 if (attr & TATTR_ACTCURS) {
2632 HPEN oldpen;
2633 oldpen =
2634 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2635 MoveToEx(hdc, startx, starty, NULL);
2636 LineTo(hdc, startx + dx * length, starty + dy * length);
2637 oldpen = SelectObject(hdc, oldpen);
2638 DeleteObject(oldpen);
2639 } else {
2640 for (i = 0; i < length; i++) {
2641 if (i % 2 == 0) {
2642 SetPixel(hdc, startx, starty, colours[23]);
2643 }
2644 startx += dx;
2645 starty += dy;
2646 }
2647 }
2648 }
2649 }
2650
2651 /* This function gets the actual width of a character in the normal font.
2652 */
2653 int CharWidth(Context ctx, int uc) {
2654 HDC hdc = ctx;
2655 int ibuf = 0;
2656
2657 /* If the font max is the same as the font ave width then this
2658 * function is a no-op.
2659 */
2660 if (!font_dualwidth) return 1;
2661
2662 switch (uc & CSET_MASK) {
2663 case ATTR_ASCII:
2664 uc = unitab_line[uc & 0xFF];
2665 break;
2666 case ATTR_LINEDRW:
2667 uc = unitab_xterm[uc & 0xFF];
2668 break;
2669 case ATTR_SCOACS:
2670 uc = unitab_scoacs[uc & 0xFF];
2671 break;
2672 }
2673 if (DIRECT_FONT(uc)) {
2674 if (dbcs_screenfont) return 1;
2675
2676 /* Speedup, I know of no font where ascii is the wrong width */
2677 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2678 return 1;
2679
2680 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2681 SelectObject(hdc, fonts[FONT_NORMAL]);
2682 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2683 another_font(FONT_OEM);
2684 if (!fonts[FONT_OEM]) return 0;
2685
2686 SelectObject(hdc, fonts[FONT_OEM]);
2687 } else
2688 return 0;
2689
2690 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2691 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2692 return 0;
2693 } else {
2694 /* Speedup, I know of no font where ascii is the wrong width */
2695 if (uc >= ' ' && uc <= '~') return 1;
2696
2697 SelectObject(hdc, fonts[FONT_NORMAL]);
2698 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2699 /* Okay that one worked */ ;
2700 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2701 /* This should work on 9x too, but it's "less accurate" */ ;
2702 else
2703 return 0;
2704 }
2705
2706 ibuf += font_width / 2 -1;
2707 ibuf /= font_width;
2708
2709 return ibuf;
2710 }
2711
2712 /*
2713 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2714 * codes. Returns number of bytes used or zero to drop the message
2715 * or -1 to forward the message to windows.
2716 */
2717 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2718 unsigned char *output)
2719 {
2720 BYTE keystate[256];
2721 int scan, left_alt = 0, key_down, shift_state;
2722 int r, i, code;
2723 unsigned char *p = output;
2724 static int alt_sum = 0;
2725
2726 HKL kbd_layout = GetKeyboardLayout(0);
2727
2728 static WORD keys[3];
2729 static int compose_char = 0;
2730 static WPARAM compose_key = 0;
2731
2732 r = GetKeyboardState(keystate);
2733 if (!r)
2734 memset(keystate, 0, sizeof(keystate));
2735 else {
2736 #if 0
2737 #define SHOW_TOASCII_RESULT
2738 { /* Tell us all about key events */
2739 static BYTE oldstate[256];
2740 static int first = 1;
2741 static int scan;
2742 int ch;
2743 if (first)
2744 memcpy(oldstate, keystate, sizeof(oldstate));
2745 first = 0;
2746
2747 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2748 debug(("+"));
2749 } else if ((HIWORD(lParam) & KF_UP)
2750 && scan == (HIWORD(lParam) & 0xFF)) {
2751 debug((". U"));
2752 } else {
2753 debug((".\n"));
2754 if (wParam >= VK_F1 && wParam <= VK_F20)
2755 debug(("K_F%d", wParam + 1 - VK_F1));
2756 else
2757 switch (wParam) {
2758 case VK_SHIFT:
2759 debug(("SHIFT"));
2760 break;
2761 case VK_CONTROL:
2762 debug(("CTRL"));
2763 break;
2764 case VK_MENU:
2765 debug(("ALT"));
2766 break;
2767 default:
2768 debug(("VK_%02x", wParam));
2769 }
2770 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2771 debug(("*"));
2772 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2773
2774 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2775 if (ch >= ' ' && ch <= '~')
2776 debug((", '%c'", ch));
2777 else if (ch)
2778 debug((", $%02x", ch));
2779
2780 if (keys[0])
2781 debug((", KB0=%02x", keys[0]));
2782 if (keys[1])
2783 debug((", KB1=%02x", keys[1]));
2784 if (keys[2])
2785 debug((", KB2=%02x", keys[2]));
2786
2787 if ((keystate[VK_SHIFT] & 0x80) != 0)
2788 debug((", S"));
2789 if ((keystate[VK_CONTROL] & 0x80) != 0)
2790 debug((", C"));
2791 if ((HIWORD(lParam) & KF_EXTENDED))
2792 debug((", E"));
2793 if ((HIWORD(lParam) & KF_UP))
2794 debug((", U"));
2795 }
2796
2797 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2798 else if ((HIWORD(lParam) & KF_UP))
2799 oldstate[wParam & 0xFF] ^= 0x80;
2800 else
2801 oldstate[wParam & 0xFF] ^= 0x81;
2802
2803 for (ch = 0; ch < 256; ch++)
2804 if (oldstate[ch] != keystate[ch])
2805 debug((", M%02x=%02x", ch, keystate[ch]));
2806
2807 memcpy(oldstate, keystate, sizeof(oldstate));
2808 }
2809 #endif
2810
2811 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2812 keystate[VK_RMENU] = keystate[VK_MENU];
2813 }
2814
2815
2816 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2817 if ((cfg.funky_type == 3 ||
2818 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2819 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2820
2821 wParam = VK_EXECUTE;
2822
2823 /* UnToggle NUMLock */
2824 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2825 keystate[VK_NUMLOCK] ^= 1;
2826 }
2827
2828 /* And write back the 'adjusted' state */
2829 SetKeyboardState(keystate);
2830 }
2831
2832 /* Disable Auto repeat if required */
2833 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2834 return 0;
2835
2836 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2837 left_alt = 1;
2838
2839 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2840
2841 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2842 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2843 if (cfg.ctrlaltkeys)
2844 keystate[VK_MENU] = 0;
2845 else {
2846 keystate[VK_RMENU] = 0x80;
2847 left_alt = 0;
2848 }
2849 }
2850
2851 alt_pressed = (left_alt && key_down);
2852
2853 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2854 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2855 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2856
2857 /* Note if AltGr was pressed and if it was used as a compose key */
2858 if (!compose_state) {
2859 compose_key = 0x100;
2860 if (cfg.compose_key) {
2861 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2862 compose_key = wParam;
2863 }
2864 if (wParam == VK_APPS)
2865 compose_key = wParam;
2866 }
2867
2868 if (wParam == compose_key) {
2869 if (compose_state == 0
2870 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2871 1;
2872 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2873 compose_state = 2;
2874 else
2875 compose_state = 0;
2876 } else if (compose_state == 1 && wParam != VK_CONTROL)
2877 compose_state = 0;
2878
2879 /*
2880 * Record that we pressed key so the scroll window can be reset, but
2881 * be careful to avoid Shift-UP/Down
2882 */
2883 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2884 wParam != VK_MENU && wParam != VK_CONTROL) {
2885 seen_key_event = 1;
2886 }
2887
2888 if (compose_state > 1 && left_alt)
2889 compose_state = 0;
2890
2891 /* Sanitize the number pad if not using a PC NumPad */
2892 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2893 && cfg.funky_type != 2)
2894 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2895 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2896 int nParam = 0;
2897 switch (wParam) {
2898 case VK_INSERT:
2899 nParam = VK_NUMPAD0;
2900 break;
2901 case VK_END:
2902 nParam = VK_NUMPAD1;
2903 break;
2904 case VK_DOWN:
2905 nParam = VK_NUMPAD2;
2906 break;
2907 case VK_NEXT:
2908 nParam = VK_NUMPAD3;
2909 break;
2910 case VK_LEFT:
2911 nParam = VK_NUMPAD4;
2912 break;
2913 case VK_CLEAR:
2914 nParam = VK_NUMPAD5;
2915 break;
2916 case VK_RIGHT:
2917 nParam = VK_NUMPAD6;
2918 break;
2919 case VK_HOME:
2920 nParam = VK_NUMPAD7;
2921 break;
2922 case VK_UP:
2923 nParam = VK_NUMPAD8;
2924 break;
2925 case VK_PRIOR:
2926 nParam = VK_NUMPAD9;
2927 break;
2928 case VK_DELETE:
2929 nParam = VK_DECIMAL;
2930 break;
2931 }
2932 if (nParam) {
2933 if (keystate[VK_NUMLOCK] & 1)
2934 shift_state |= 1;
2935 wParam = nParam;
2936 }
2937 }
2938 }
2939
2940 /* If a key is pressed and AltGr is not active */
2941 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2942 /* Okay, prepare for most alts then ... */
2943 if (left_alt)
2944 *p++ = '\033';
2945
2946 /* Lets see if it's a pattern we know all about ... */
2947 if (wParam == VK_PRIOR && shift_state == 1) {
2948 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2949 return 0;
2950 }
2951 if (wParam == VK_NEXT && shift_state == 1) {
2952 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2953 return 0;
2954 }
2955 if (wParam == VK_INSERT && shift_state == 1) {
2956 term_do_paste();
2957 return 0;
2958 }
2959 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2960 return -1;
2961 }
2962 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2963 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2964 return -1;
2965 }
2966 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2967 flip_full_screen();
2968 return -1;
2969 }
2970 /* Control-Numlock for app-keypad mode switch */
2971 if (wParam == VK_PAUSE && shift_state == 2) {
2972 app_keypad_keys ^= 1;
2973 return 0;
2974 }
2975
2976 /* Nethack keypad */
2977 if (cfg.nethack_keypad && !left_alt) {
2978 switch (wParam) {
2979 case VK_NUMPAD1:
2980 *p++ = shift_state ? 'B' : 'b';
2981 return p - output;
2982 case VK_NUMPAD2:
2983 *p++ = shift_state ? 'J' : 'j';
2984 return p - output;
2985 case VK_NUMPAD3:
2986 *p++ = shift_state ? 'N' : 'n';
2987 return p - output;
2988 case VK_NUMPAD4:
2989 *p++ = shift_state ? 'H' : 'h';
2990 return p - output;
2991 case VK_NUMPAD5:
2992 *p++ = shift_state ? '.' : '.';
2993 return p - output;
2994 case VK_NUMPAD6:
2995 *p++ = shift_state ? 'L' : 'l';
2996 return p - output;
2997 case VK_NUMPAD7:
2998 *p++ = shift_state ? 'Y' : 'y';
2999 return p - output;
3000 case VK_NUMPAD8:
3001 *p++ = shift_state ? 'K' : 'k';
3002 return p - output;
3003 case VK_NUMPAD9:
3004 *p++ = shift_state ? 'U' : 'u';
3005 return p - output;
3006 }
3007 }
3008
3009 /* Application Keypad */
3010 if (!left_alt) {
3011 int xkey = 0;
3012
3013 if (cfg.funky_type == 3 ||
3014 (cfg.funky_type <= 1 &&
3015 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3016 case VK_EXECUTE:
3017 xkey = 'P';
3018 break;
3019 case VK_DIVIDE:
3020 xkey = 'Q';
3021 break;
3022 case VK_MULTIPLY:
3023 xkey = 'R';
3024 break;
3025 case VK_SUBTRACT:
3026 xkey = 'S';
3027 break;
3028 }
3029 if (app_keypad_keys && !cfg.no_applic_k)
3030 switch (wParam) {
3031 case VK_NUMPAD0:
3032 xkey = 'p';
3033 break;
3034 case VK_NUMPAD1:
3035 xkey = 'q';
3036 break;
3037 case VK_NUMPAD2:
3038 xkey = 'r';
3039 break;
3040 case VK_NUMPAD3:
3041 xkey = 's';
3042 break;
3043 case VK_NUMPAD4:
3044 xkey = 't';
3045 break;
3046 case VK_NUMPAD5:
3047 xkey = 'u';
3048 break;
3049 case VK_NUMPAD6:
3050 xkey = 'v';
3051 break;
3052 case VK_NUMPAD7:
3053 xkey = 'w';
3054 break;
3055 case VK_NUMPAD8:
3056 xkey = 'x';
3057 break;
3058 case VK_NUMPAD9:
3059 xkey = 'y';
3060 break;
3061
3062 case VK_DECIMAL:
3063 xkey = 'n';
3064 break;
3065 case VK_ADD:
3066 if (cfg.funky_type == 2) {
3067 if (shift_state)
3068 xkey = 'l';
3069 else
3070 xkey = 'k';
3071 } else if (shift_state)
3072 xkey = 'm';
3073 else
3074 xkey = 'l';
3075 break;
3076
3077 case VK_DIVIDE:
3078 if (cfg.funky_type == 2)
3079 xkey = 'o';
3080 break;
3081 case VK_MULTIPLY:
3082 if (cfg.funky_type == 2)
3083 xkey = 'j';
3084 break;
3085 case VK_SUBTRACT:
3086 if (cfg.funky_type == 2)
3087 xkey = 'm';
3088 break;
3089
3090 case VK_RETURN:
3091 if (HIWORD(lParam) & KF_EXTENDED)
3092 xkey = 'M';
3093 break;
3094 }
3095 if (xkey) {
3096 if (vt52_mode) {
3097 if (xkey >= 'P' && xkey <= 'S')
3098 p += sprintf((char *) p, "\x1B%c", xkey);
3099 else
3100 p += sprintf((char *) p, "\x1B?%c", xkey);
3101 } else
3102 p += sprintf((char *) p, "\x1BO%c", xkey);
3103 return p - output;
3104 }
3105 }
3106
3107 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3108 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3109 *p++ = 0;
3110 return -2;
3111 }
3112 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3113 *p++ = 0x1B;
3114 *p++ = '[';
3115 *p++ = 'Z';
3116 return p - output;
3117 }
3118 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3119 *p++ = 0;
3120 return p - output;
3121 }
3122 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3123 *p++ = 160;
3124 return p - output;
3125 }
3126 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3127 *p++ = 3;
3128 *p++ = 0;
3129 return -2;
3130 }
3131 if (wParam == VK_PAUSE) { /* Break/Pause */
3132 *p++ = 26;
3133 *p++ = 0;
3134 return -2;
3135 }
3136 /* Control-2 to Control-8 are special */
3137 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3138 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3139 return p - output;
3140 }
3141 if (shift_state == 2 && wParam == 0xBD) {
3142 *p++ = 0x1F;
3143 return p - output;
3144 }
3145 if (shift_state == 2 && wParam == 0xDF) {
3146 *p++ = 0x1C;
3147 return p - output;
3148 }
3149 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3150 *p++ = '\r';
3151 *p++ = '\n';
3152 return p - output;
3153 }
3154
3155 /*
3156 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3157 * for integer decimal nn.)
3158 *
3159 * We also deal with the weird ones here. Linux VCs replace F1
3160 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3161 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3162 * respectively.
3163 */
3164 code = 0;
3165 switch (wParam) {
3166 case VK_F1:
3167 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3168 break;
3169 case VK_F2:
3170 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3171 break;
3172 case VK_F3:
3173 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3174 break;
3175 case VK_F4:
3176 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3177 break;
3178 case VK_F5:
3179 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3180 break;
3181 case VK_F6:
3182 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3183 break;
3184 case VK_F7:
3185 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3186 break;
3187 case VK_F8:
3188 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3189 break;
3190 case VK_F9:
3191 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3192 break;
3193 case VK_F10:
3194 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3195 break;
3196 case VK_F11:
3197 code = 23;
3198 break;
3199 case VK_F12:
3200 code = 24;
3201 break;
3202 case VK_F13:
3203 code = 25;
3204 break;
3205 case VK_F14:
3206 code = 26;
3207 break;
3208 case VK_F15:
3209 code = 28;
3210 break;
3211 case VK_F16:
3212 code = 29;
3213 break;
3214 case VK_F17:
3215 code = 31;
3216 break;
3217 case VK_F18:
3218 code = 32;
3219 break;
3220 case VK_F19:
3221 code = 33;
3222 break;
3223 case VK_F20:
3224 code = 34;
3225 break;
3226 }
3227 if ((shift_state&2) == 0) switch (wParam) {
3228 case VK_HOME:
3229 code = 1;
3230 break;
3231 case VK_INSERT:
3232 code = 2;
3233 break;
3234 case VK_DELETE:
3235 code = 3;
3236 break;
3237 case VK_END:
3238 code = 4;
3239 break;
3240 case VK_PRIOR:
3241 code = 5;
3242 break;
3243 case VK_NEXT:
3244 code = 6;
3245 break;
3246 }
3247 /* Reorder edit keys to physical order */
3248 if (cfg.funky_type == 3 && code <= 6)
3249 code = "\0\2\1\4\5\3\6"[code];
3250
3251 if (vt52_mode && code > 0 && code <= 6) {
3252 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3253 return p - output;
3254 }
3255
3256 if (cfg.funky_type == 5 && /* SCO function keys */
3257 code >= 11 && code <= 34) {
3258 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3259 int index = 0;
3260 switch (wParam) {
3261 case VK_F1: index = 0; break;
3262 case VK_F2: index = 1; break;
3263 case VK_F3: index = 2; break;
3264 case VK_F4: index = 3; break;
3265 case VK_F5: index = 4; break;
3266 case VK_F6: index = 5; break;
3267 case VK_F7: index = 6; break;
3268 case VK_F8: index = 7; break;
3269 case VK_F9: index = 8; break;
3270 case VK_F10: index = 9; break;
3271 case VK_F11: index = 10; break;
3272 case VK_F12: index = 11; break;
3273 }
3274 if (keystate[VK_SHIFT] & 0x80) index += 12;
3275 if (keystate[VK_CONTROL] & 0x80) index += 24;
3276 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3277 return p - output;
3278 }
3279 if (cfg.funky_type == 5 && /* SCO small keypad */
3280 code >= 1 && code <= 6) {
3281 char codes[] = "HL.FIG";
3282 if (code == 3) {
3283 *p++ = '\x7F';
3284 } else {
3285 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3286 }
3287 return p - output;
3288 }
3289 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3290 int offt = 0;
3291 if (code > 15)
3292 offt++;
3293 if (code > 21)
3294 offt++;
3295 if (vt52_mode)
3296 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3297 else
3298 p +=
3299 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3300 return p - output;
3301 }
3302 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3303 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3304 return p - output;
3305 }
3306 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3307 if (vt52_mode)
3308 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3309 else
3310 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3311 return p - output;
3312 }
3313 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3314 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3315 return p - output;
3316 }
3317 if (code) {
3318 p += sprintf((char *) p, "\x1B[%d~", code);
3319 return p - output;
3320 }
3321
3322 /*
3323 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3324 * some reason seems to send VK_CLEAR to Windows...).
3325 */
3326 {
3327 char xkey = 0;
3328 switch (wParam) {
3329 case VK_UP:
3330 xkey = 'A';
3331 break;
3332 case VK_DOWN:
3333 xkey = 'B';
3334 break;
3335 case VK_RIGHT:
3336 xkey = 'C';
3337 break;
3338 case VK_LEFT:
3339 xkey = 'D';
3340 break;
3341 case VK_CLEAR:
3342 xkey = 'G';
3343 break;
3344 }
3345 if (xkey) {
3346 if (vt52_mode)
3347 p += sprintf((char *) p, "\x1B%c", xkey);
3348 else {
3349 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3350 /* VT100 & VT102 manuals both state the app cursor keys
3351 * only work if the app keypad is on.
3352 */
3353 if (!app_keypad_keys)
3354 app_flg = 0;
3355 /* Useful mapping of Ctrl-arrows */
3356 if (shift_state == 2)
3357 app_flg = !app_flg;
3358
3359 if (app_flg)
3360 p += sprintf((char *) p, "\x1BO%c", xkey);
3361 else
3362 p += sprintf((char *) p, "\x1B[%c", xkey);
3363 }
3364 return p - output;
3365 }
3366 }
3367
3368 /*
3369 * Finally, deal with Return ourselves. (Win95 seems to
3370 * foul it up when Alt is pressed, for some reason.)
3371 */
3372 if (wParam == VK_RETURN) { /* Return */
3373 *p++ = 0x0D;
3374 *p++ = 0;
3375 return -2;
3376 }
3377
3378 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3379 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3380 else
3381 alt_sum = 0;
3382 }
3383
3384 /* Okay we've done everything interesting; let windows deal with
3385 * the boring stuff */
3386 {
3387 BOOL capsOn=0;
3388
3389 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3390 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3391 capsOn= !left_alt;
3392 keystate[VK_CAPITAL] = 0;
3393 }
3394
3395 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3396 #ifdef SHOW_TOASCII_RESULT
3397 if (r == 1 && !key_down) {
3398 if (alt_sum) {
3399 if (in_utf || dbcs_screenfont)
3400 debug((", (U+%04x)", alt_sum));
3401 else
3402 debug((", LCH(%d)", alt_sum));
3403 } else {
3404 debug((", ACH(%d)", keys[0]));
3405 }
3406 } else if (r > 0) {
3407 int r1;
3408 debug((", ASC("));
3409 for (r1 = 0; r1 < r; r1++) {
3410 debug(("%s%d", r1 ? "," : "", keys[r1]));
3411 }
3412 debug((")"));
3413 }
3414 #endif
3415 if (r > 0) {
3416 WCHAR keybuf;
3417
3418 /*
3419 * Interrupt an ongoing paste. I'm not sure this is
3420 * sensible, but for the moment it's preferable to
3421 * having to faff about buffering things.
3422 */
3423 term_nopaste();
3424
3425 p = output;
3426 for (i = 0; i < r; i++) {
3427 unsigned char ch = (unsigned char) keys[i];
3428
3429 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3430 compose_char = ch;
3431 compose_state++;
3432 continue;
3433 }
3434 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3435 int nc;
3436 compose_state = 0;
3437
3438 if ((nc = check_compose(compose_char, ch)) == -1) {
3439 MessageBeep(MB_ICONHAND);
3440 return 0;
3441 }
3442 keybuf = nc;
3443 luni_send(&keybuf, 1, 1);
3444 continue;
3445 }
3446
3447 compose_state = 0;
3448
3449 if (!key_down) {
3450 if (alt_sum) {
3451 if (in_utf || dbcs_screenfont) {
3452 keybuf = alt_sum;
3453 luni_send(&keybuf, 1, 1);
3454 } else {
3455 ch = (char) alt_sum;
3456 /*
3457 * We need not bother about stdin
3458 * backlogs here, because in GUI PuTTY
3459 * we can't do anything about it
3460 * anyway; there's no means of asking
3461 * Windows to hold off on KEYDOWN
3462 * messages. We _have_ to buffer
3463 * everything we're sent.
3464 */
3465 ldisc_send(&ch, 1, 1);
3466 }
3467 alt_sum = 0;
3468 } else
3469 lpage_send(kbd_codepage, &ch, 1, 1);
3470 } else {
3471 if(capsOn && ch < 0x80) {
3472 WCHAR cbuf[2];
3473 cbuf[0] = 27;
3474 cbuf[1] = xlat_uskbd2cyrllic(ch);
3475 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3476 } else {
3477 char cbuf[2];
3478 cbuf[0] = '\033';
3479 cbuf[1] = ch;
3480 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3481 }
3482 }
3483 show_mouseptr(0);
3484 }
3485
3486 /* This is so the ALT-Numpad and dead keys work correctly. */
3487 keys[0] = 0;
3488
3489 return p - output;
3490 }
3491 /* If we're definitly not building up an ALT-54321 then clear it */
3492 if (!left_alt)
3493 keys[0] = 0;
3494 /* If we will be using alt_sum fix the 256s */
3495 else if (keys[0] && (in_utf || dbcs_screenfont))
3496 keys[0] = 10;
3497 }
3498
3499 /*
3500 * ALT alone may or may not want to bring up the System menu.
3501 * If it's not meant to, we return 0 on presses or releases of
3502 * ALT, to show that we've swallowed the keystroke. Otherwise
3503 * we return -1, which means Windows will give the keystroke
3504 * its default handling (i.e. bring up the System menu).
3505 */
3506 if (wParam == VK_MENU && !cfg.alt_only)
3507 return 0;
3508
3509 return -1;
3510 }
3511
3512 void set_title(char *title)
3513 {
3514 sfree(window_name);
3515 window_name = smalloc(1 + strlen(title));
3516 strcpy(window_name, title);
3517 if (cfg.win_name_always || !IsIconic(hwnd))
3518 SetWindowText(hwnd, title);
3519 }
3520
3521 void set_icon(char *title)
3522 {
3523 sfree(icon_name);
3524 icon_name = smalloc(1 + strlen(title));
3525 strcpy(icon_name, title);
3526 if (!cfg.win_name_always && IsIconic(hwnd))
3527 SetWindowText(hwnd, title);
3528 }
3529
3530 void set_sbar(int total, int start, int page)
3531 {
3532 SCROLLINFO si;
3533
3534 if (!cfg.scrollbar)
3535 return;
3536
3537 si.cbSize = sizeof(si);
3538 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3539 si.nMin = 0;
3540 si.nMax = total - 1;
3541 si.nPage = page;
3542 si.nPos = start;
3543 if (hwnd)
3544 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3545 }
3546
3547 Context get_ctx(void)
3548 {
3549 HDC hdc;
3550 if (hwnd) {
3551 hdc = GetDC(hwnd);
3552 if (hdc && pal)
3553 SelectPalette(hdc, pal, FALSE);
3554 return hdc;
3555 } else
3556 return NULL;
3557 }
3558
3559 void free_ctx(Context ctx)
3560 {
3561 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3562 ReleaseDC(hwnd, ctx);
3563 }
3564
3565 static void real_palette_set(int n, int r, int g, int b)
3566 {
3567 if (pal) {
3568 logpal->palPalEntry[n].peRed = r;
3569 logpal->palPalEntry[n].peGreen = g;
3570 logpal->palPalEntry[n].peBlue = b;
3571 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3572 colours[n] = PALETTERGB(r, g, b);
3573 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3574 } else
3575 colours[n] = RGB(r, g, b);
3576 }
3577
3578 void palette_set(int n, int r, int g, int b)
3579 {
3580 static const int first[21] = {
3581 0, 2, 4, 6, 8, 10, 12, 14,
3582 1, 3, 5, 7, 9, 11, 13, 15,
3583 16, 17, 18, 20, 22
3584 };
3585 real_palette_set(first[n], r, g, b);
3586 if (first[n] >= 18)
3587 real_palette_set(first[n] + 1, r, g, b);
3588 if (pal) {
3589 HDC hdc = get_ctx();
3590 UnrealizeObject(pal);
3591 RealizePalette(hdc);
3592 free_ctx(hdc);
3593 }
3594 }
3595
3596 void palette_reset(void)
3597 {
3598 int i;
3599
3600 for (i = 0; i < NCOLOURS; i++) {
3601 if (pal) {
3602 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3603 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3604 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3605 logpal->palPalEntry[i].peFlags = 0;
3606 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3607 defpal[i].rgbtGreen,
3608 defpal[i].rgbtBlue);
3609 } else
3610 colours[i] = RGB(defpal[i].rgbtRed,
3611 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3612 }
3613
3614 if (pal) {
3615 HDC hdc;
3616 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3617 hdc = get_ctx();
3618 RealizePalette(hdc);
3619 free_ctx(hdc);
3620 }
3621 }
3622
3623 void write_aclip(char *data, int len, int must_deselect)
3624 {
3625 HGLOBAL clipdata;
3626 void *lock;
3627
3628 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3629 if (!clipdata)
3630 return;
3631 lock = GlobalLock(clipdata);
3632 if (!lock)
3633 return;
3634 memcpy(lock, data, len);
3635 ((unsigned char *) lock)[len] = 0;
3636 GlobalUnlock(clipdata);
3637
3638 if (!must_deselect)
3639 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3640
3641 if (OpenClipboard(hwnd)) {
3642 EmptyClipboard();
3643 SetClipboardData(CF_TEXT, clipdata);
3644 CloseClipboard();
3645 } else
3646 GlobalFree(clipdata);
3647
3648 if (!must_deselect)
3649 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3650 }
3651
3652 /*
3653 * Note: unlike write_aclip() this will not append a nul.
3654 */
3655 void write_clip(wchar_t * data, int len, int must_deselect)
3656 {
3657 HGLOBAL clipdata, clipdata2, clipdata3;
3658 int len2;
3659 void *lock, *lock2, *lock3;
3660
3661 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3662
3663 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3664 len * sizeof(wchar_t));
3665 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3666
3667 if (!clipdata || !clipdata2 || !clipdata3) {
3668 if (clipdata)
3669 GlobalFree(clipdata);
3670 if (clipdata2)
3671 GlobalFree(clipdata2);
3672 if (clipdata3)
3673 GlobalFree(clipdata3);
3674 return;
3675 }
3676 if (!(lock = GlobalLock(clipdata)))
3677 return;
3678 if (!(lock2 = GlobalLock(clipdata2)))
3679 return;
3680
3681 memcpy(lock, data, len * sizeof(wchar_t));
3682 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3683
3684 if (cfg.rtf_paste) {
3685 wchar_t unitab[256];
3686 char *rtf = NULL;
3687 unsigned char *tdata = (unsigned char *)lock2;
3688 wchar_t *udata = (wchar_t *)lock;
3689 int rtflen = 0, uindex = 0, tindex = 0;
3690 int rtfsize = 0;
3691 int multilen, blen, alen, totallen, i;
3692 char before[16], after[4];
3693
3694 get_unitab(CP_ACP, unitab, 0);
3695
3696 rtfsize = 100 + strlen(cfg.font);
3697 rtf = smalloc(rtfsize);
3698 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3699 GetACP(), cfg.font);
3700 rtflen = strlen(rtf);
3701
3702 /*
3703 * We want to construct a piece of RTF that specifies the
3704 * same Unicode text. To do this we will read back in
3705 * parallel from the Unicode data in `udata' and the
3706 * non-Unicode data in `tdata'. For each character in
3707 * `tdata' which becomes the right thing in `udata' when
3708 * looked up in `unitab', we just copy straight over from
3709 * tdata. For each one that doesn't, we must WCToMB it
3710 * individually and produce a \u escape sequence.
3711 *
3712 * It would probably be more robust to just bite the bullet
3713 * and WCToMB each individual Unicode character one by one,
3714 * then MBToWC each one back to see if it was an accurate
3715 * translation; but that strikes me as a horrifying number
3716 * of Windows API calls so I want to see if this faster way
3717 * will work. If it screws up badly we can always revert to
3718 * the simple and slow way.
3719 */
3720 while (tindex < len2 && uindex < len &&
3721 tdata[tindex] && udata[uindex]) {
3722 if (tindex + 1 < len2 &&
3723 tdata[tindex] == '\r' &&
3724 tdata[tindex+1] == '\n') {
3725 tindex++;
3726 uindex++;
3727 }
3728 if (unitab[tdata[tindex]] == udata[uindex]) {
3729 multilen = 1;
3730 before[0] = '\0';
3731 after[0] = '\0';
3732 blen = alen = 0;
3733 } else {
3734 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3735 NULL, 0, NULL, NULL);
3736 if (multilen != 1) {
3737 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3738 udata[uindex]);
3739 alen = 1; strcpy(after, "}");
3740 } else {
3741 blen = sprintf(before, "\\u%d", udata[uindex]);
3742 alen = 0; after[0] = '\0';
3743 }
3744 }
3745 assert(tindex + multilen <= len2);
3746 totallen = blen + alen;
3747 for (i = 0; i < multilen; i++) {
3748 if (tdata[tindex+i] == '\\' ||
3749 tdata[tindex+i] == '{' ||
3750 tdata[tindex+i] == '}')
3751 totallen += 2;
3752 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3753 totallen += 6; /* \par\r\n */
3754 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3755 totallen += 4;
3756 else
3757 totallen++;
3758 }
3759
3760 if (rtfsize < rtflen + totallen + 3) {
3761 rtfsize = rtflen + totallen + 512;
3762 rtf = srealloc(rtf, rtfsize);
3763 }
3764
3765 strcpy(rtf + rtflen, before); rtflen += blen;
3766 for (i = 0; i < multilen; i++) {
3767 if (tdata[tindex+i] == '\\' ||
3768 tdata[tindex+i] == '{' ||
3769 tdata[tindex+i] == '}') {
3770 rtf[rtflen++] = '\\';
3771 rtf[rtflen++] = tdata[tindex+i];
3772 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3773 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3774 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3775 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3776 } else {
3777 rtf[rtflen++] = tdata[tindex+i];
3778 }
3779 }
3780 strcpy(rtf + rtflen, after); rtflen += alen;
3781
3782 tindex += multilen;
3783 uindex++;
3784 }
3785
3786 strcpy(rtf + rtflen, "}");
3787 rtflen += 2;
3788
3789 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3790 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3791 strcpy(lock3, rtf);
3792 GlobalUnlock(clipdata3);
3793 }
3794 sfree(rtf);
3795 } else
3796 clipdata3 = NULL;
3797
3798 GlobalUnlock(clipdata);
3799 GlobalUnlock(clipdata2);
3800
3801 if (!must_deselect)
3802 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3803
3804 if (OpenClipboard(hwnd)) {
3805 EmptyClipboard();
3806 SetClipboardData(CF_UNICODETEXT, clipdata);
3807 SetClipboardData(CF_TEXT, clipdata2);
3808 if (clipdata3)
3809 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3810 CloseClipboard();
3811 } else {
3812 GlobalFree(clipdata);
3813 GlobalFree(clipdata2);
3814 }
3815
3816 if (!must_deselect)
3817 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3818 }
3819
3820 void get_clip(wchar_t ** p, int *len)
3821 {
3822 static HGLOBAL clipdata = NULL;
3823 static wchar_t *converted = 0;
3824 wchar_t *p2;
3825
3826 if (converted) {
3827 sfree(converted);
3828 converted = 0;
3829 }
3830 if (!p) {
3831 if (clipdata)
3832 GlobalUnlock(clipdata);
3833 clipdata = NULL;
3834 return;
3835 } else if (OpenClipboard(NULL)) {
3836 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3837 CloseClipboard();
3838 *p = GlobalLock(clipdata);
3839 if (*p) {
3840 for (p2 = *p; *p2; p2++);
3841 *len = p2 - *p;
3842 return;
3843 }
3844 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3845 char *s;
3846 int i;
3847 CloseClipboard();
3848 s = GlobalLock(clipdata);
3849 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3850 *p = converted = smalloc(i * sizeof(wchar_t));
3851 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3852 *len = i - 1;
3853 return;
3854 } else
3855 CloseClipboard();
3856 }
3857
3858 *p = NULL;
3859 *len = 0;
3860 }
3861
3862 #if 0
3863 /*
3864 * Move `lines' lines from position `from' to position `to' in the
3865 * window.
3866 */
3867 void optimised_move(int to, int from, int lines)
3868 {
3869 RECT r;
3870 int min, max;
3871
3872 min = (to < from ? to : from);
3873 max = to + from - min;
3874
3875 r.left = offset_width;
3876 r.right = offset_width + cols * font_width;
3877 r.top = offset_height + min * font_height;
3878 r.bottom = offset_height + (max + lines) * font_height;
3879 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3880 }
3881 #endif
3882
3883 /*
3884 * Print a message box and perform a fatal exit.
3885 */
3886 void fatalbox(char *fmt, ...)
3887 {
3888 va_list ap;
3889 char stuff[200];
3890
3891 va_start(ap, fmt);
3892 vsprintf(stuff, fmt, ap);
3893 va_end(ap);
3894 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3895 exit(1);
3896 }
3897
3898 /*
3899 * Manage window caption / taskbar flashing, if enabled.
3900 * 0 = stop, 1 = maintain, 2 = start
3901 */
3902 static void flash_window(int mode)
3903 {
3904 static long last_flash = 0;
3905 static int flashing = 0;
3906 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3907 /* stop */
3908 if (flashing) {
3909 FlashWindow(hwnd, FALSE);
3910 flashing = 0;
3911 }
3912
3913 } else if (mode == 2) {
3914 /* start */
3915 if (!flashing) {
3916 last_flash = GetTickCount();
3917 flashing = 1;
3918 FlashWindow(hwnd, TRUE);
3919 }
3920
3921 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3922 /* maintain */
3923 if (flashing) {
3924 long now = GetTickCount();
3925 long fdiff = now - last_flash;
3926 if (fdiff < 0 || fdiff > 450) {
3927 last_flash = now;
3928 FlashWindow(hwnd, TRUE); /* toggle */
3929 }
3930 }
3931 }
3932 }
3933
3934 /*
3935 * Beep.
3936 */
3937 void beep(int mode)
3938 {
3939 if (mode == BELL_DEFAULT) {
3940 /*
3941 * For MessageBeep style bells, we want to be careful of
3942 * timing, because they don't have the nice property of
3943 * PlaySound bells that each one cancels the previous
3944 * active one. So we limit the rate to one per 50ms or so.
3945 */
3946 static long lastbeep = 0;
3947 long beepdiff;
3948
3949 beepdiff = GetTickCount() - lastbeep;
3950 if (beepdiff >= 0 && beepdiff < 50)
3951 return;
3952 MessageBeep(MB_OK);
3953 /*
3954 * The above MessageBeep call takes time, so we record the
3955 * time _after_ it finishes rather than before it starts.
3956 */
3957 lastbeep = GetTickCount();
3958 } else if (mode == BELL_WAVEFILE) {
3959 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3960 char buf[sizeof(cfg.bell_wavefile) + 80];
3961 sprintf(buf, "Unable to play sound file\n%s\n"
3962 "Using default sound instead", cfg.bell_wavefile);
3963 MessageBox(hwnd, buf, "PuTTY Sound Error",
3964 MB_OK | MB_ICONEXCLAMATION);
3965 cfg.beep = BELL_DEFAULT;
3966 }
3967 }
3968 /* Otherwise, either visual bell or disabled; do nothing here */
3969 if (!has_focus) {
3970 flash_window(2); /* start */
3971 }
3972 }
3973
3974 /*
3975 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3976 * implementation.
3977 * Revised by <wez@thebrainroom.com>
3978 */
3979 static void flip_full_screen(void)
3980 {
3981 WINDOWPLACEMENT wp;
3982 LONG style;
3983
3984 wp.length = sizeof(wp);
3985 GetWindowPlacement(hwnd, &wp);
3986
3987 full_screen = !full_screen;
3988
3989 if (full_screen) {
3990 if (wp.showCmd == SW_SHOWMAXIMIZED) {
3991 /* Ooops it was already 'zoomed' we have to unzoom it before
3992 * everything will work right.
3993 */
3994 wp.showCmd = SW_SHOWNORMAL;
3995 SetWindowPlacement(hwnd, &wp);
3996 }
3997
3998 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3999 style &= ~WS_VSCROLL;
4000 if (cfg.scrollbar_in_fullscreen)
4001 style |= WS_VSCROLL;
4002 SetWindowLong(hwnd, GWL_STYLE, style);
4003
4004 /* This seems to be needed otherwize explorer doesn't notice
4005 * we want to go fullscreen and it's bar is still visible
4006 */
4007 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4008 SWP_NOACTIVATE | SWP_NOCOPYBITS |
4009 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4010 SWP_FRAMECHANGED);
4011
4012 wp.showCmd = SW_SHOWMAXIMIZED;
4013 SetWindowPlacement(hwnd, &wp);
4014 } else {
4015 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4016 style &= ~WS_VSCROLL;
4017 if (cfg.scrollbar)
4018 style |= WS_VSCROLL;
4019 SetWindowLong(hwnd, GWL_STYLE, style);
4020
4021 /* Don't need to do a SetWindowPos as the resize will force a
4022 * full redraw.
4023 */
4024 wp.showCmd = SW_SHOWNORMAL;
4025 SetWindowPlacement(hwnd, &wp);
4026 }
4027
4028 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4029 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4030 }