Implement writing RTF to the clipboard (off by default), so that if
[u/mdw/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_KEYMENU:
1691 /*
1692 * We get this if the System menu has been activated.
1693 * This might happen from within TranslateKey, in which
1694 * case it really wants to be followed by a `space'
1695 * character to actually _bring the menu up_ rather
1696 * than just sitting there in `ready to appear' state.
1697 */
1698 if( lParam == 0 )
1699 PostMessage(hwnd, WM_CHAR, ' ', 0);
1700 break;
1701 case IDM_FULLSCREEN:
1702 flip_full_screen();
1703 break;
1704 default:
1705 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1706 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1707 }
1708 }
1709 break;
1710
1711 #define X_POS(l) ((int)(short)LOWORD(l))
1712 #define Y_POS(l) ((int)(short)HIWORD(l))
1713
1714 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1715 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1716 #define WHEEL_DELTA 120
1717 case WM_MOUSEWHEEL:
1718 {
1719 wheel_accumulator += (short) HIWORD(wParam);
1720 wParam = LOWORD(wParam);
1721
1722 /* process events when the threshold is reached */
1723 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1724 int b;
1725
1726 /* reduce amount for next time */
1727 if (wheel_accumulator > 0) {
1728 b = MBT_WHEEL_UP;
1729 wheel_accumulator -= WHEEL_DELTA;
1730 } else if (wheel_accumulator < 0) {
1731 b = MBT_WHEEL_DOWN;
1732 wheel_accumulator += WHEEL_DELTA;
1733 } else
1734 break;
1735
1736 if (send_raw_mouse) {
1737 /* send a mouse-down followed by a mouse up */
1738
1739 term_mouse(b,
1740 MA_CLICK,
1741 TO_CHR_X(X_POS(lParam)),
1742 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1743 wParam & MK_CONTROL, is_alt_pressed());
1744 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1745 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1746 wParam & MK_CONTROL, is_alt_pressed());
1747 } else {
1748 /* trigger a scroll */
1749 term_scroll(0,
1750 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1751 }
1752 }
1753 return 0;
1754 }
1755 case WM_LBUTTONDOWN:
1756 case WM_MBUTTONDOWN:
1757 case WM_RBUTTONDOWN:
1758 case WM_LBUTTONUP:
1759 case WM_MBUTTONUP:
1760 case WM_RBUTTONUP:
1761 {
1762 int button, press;
1763
1764 switch (message) {
1765 case WM_LBUTTONDOWN:
1766 button = MBT_LEFT;
1767 press = 1;
1768 break;
1769 case WM_MBUTTONDOWN:
1770 button = MBT_MIDDLE;
1771 press = 1;
1772 break;
1773 case WM_RBUTTONDOWN:
1774 button = MBT_RIGHT;
1775 press = 1;
1776 break;
1777 case WM_LBUTTONUP:
1778 button = MBT_LEFT;
1779 press = 0;
1780 break;
1781 case WM_MBUTTONUP:
1782 button = MBT_MIDDLE;
1783 press = 0;
1784 break;
1785 case WM_RBUTTONUP:
1786 button = MBT_RIGHT;
1787 press = 0;
1788 break;
1789 default:
1790 button = press = 0; /* shouldn't happen */
1791 }
1792 show_mouseptr(1);
1793 if (press) {
1794 click(button,
1795 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1796 wParam & MK_SHIFT, wParam & MK_CONTROL,
1797 is_alt_pressed());
1798 SetCapture(hwnd);
1799 } else {
1800 term_mouse(button, MA_RELEASE,
1801 TO_CHR_X(X_POS(lParam)),
1802 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1803 wParam & MK_CONTROL, is_alt_pressed());
1804 ReleaseCapture();
1805 }
1806 }
1807 return 0;
1808 case WM_MOUSEMOVE:
1809 show_mouseptr(1);
1810 /*
1811 * Add the mouse position and message time to the random
1812 * number noise.
1813 */
1814 noise_ultralight(lParam);
1815
1816 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1817 Mouse_Button b;
1818 if (wParam & MK_LBUTTON)
1819 b = MBT_LEFT;
1820 else if (wParam & MK_MBUTTON)
1821 b = MBT_MIDDLE;
1822 else
1823 b = MBT_RIGHT;
1824 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1825 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1826 wParam & MK_CONTROL, is_alt_pressed());
1827 }
1828 return 0;
1829 case WM_NCMOUSEMOVE:
1830 show_mouseptr(1);
1831 noise_ultralight(lParam);
1832 return 0;
1833 case WM_IGNORE_CLIP:
1834 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1835 break;
1836 case WM_DESTROYCLIPBOARD:
1837 if (!ignore_clip)
1838 term_deselect();
1839 ignore_clip = FALSE;
1840 return 0;
1841 case WM_PAINT:
1842 {
1843 PAINTSTRUCT p;
1844 HideCaret(hwnd);
1845 hdc = BeginPaint(hwnd, &p);
1846 if (pal) {
1847 SelectPalette(hdc, pal, TRUE);
1848 RealizePalette(hdc);
1849 }
1850 term_paint(hdc,
1851 (p.rcPaint.left-offset_width)/font_width,
1852 (p.rcPaint.top-offset_height)/font_height,
1853 (p.rcPaint.right-offset_width-1)/font_width,
1854 (p.rcPaint.bottom-offset_height-1)/font_height);
1855
1856 if (p.fErase ||
1857 p.rcPaint.left < offset_width ||
1858 p.rcPaint.top < offset_height ||
1859 p.rcPaint.right >= offset_width + font_width*cols ||
1860 p.rcPaint.bottom>= offset_height + font_height*rows)
1861 {
1862 HBRUSH fillcolour, oldbrush;
1863 HPEN edge, oldpen;
1864 fillcolour = CreateSolidBrush (
1865 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1866 oldbrush = SelectObject(hdc, fillcolour);
1867 edge = CreatePen(PS_SOLID, 0,
1868 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1869 oldpen = SelectObject(hdc, edge);
1870
1871 ExcludeClipRect(hdc,
1872 offset_width, offset_height,
1873 offset_width+font_width*cols,
1874 offset_height+font_height*rows);
1875
1876 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1877 p.rcPaint.right, p.rcPaint.bottom);
1878
1879 // SelectClipRgn(hdc, NULL);
1880
1881 SelectObject(hdc, oldbrush);
1882 DeleteObject(fillcolour);
1883 SelectObject(hdc, oldpen);
1884 DeleteObject(edge);
1885 }
1886 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1887 SelectObject(hdc, GetStockObject(WHITE_PEN));
1888 EndPaint(hwnd, &p);
1889 ShowCaret(hwnd);
1890 }
1891 return 0;
1892 case WM_NETEVENT:
1893 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1894 * but the only one that's likely to try to overload us is FD_READ.
1895 * This means buffering just one is fine.
1896 */
1897 if (pending_netevent)
1898 enact_pending_netevent();
1899
1900 pending_netevent = TRUE;
1901 pend_netevent_wParam = wParam;
1902 pend_netevent_lParam = lParam;
1903 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1904 enact_pending_netevent();
1905
1906 time(&last_movement);
1907 return 0;
1908 case WM_SETFOCUS:
1909 has_focus = TRUE;
1910 CreateCaret(hwnd, caretbm, font_width, font_height);
1911 ShowCaret(hwnd);
1912 flash_window(0); /* stop */
1913 compose_state = 0;
1914 term_out();
1915 term_update();
1916 break;
1917 case WM_KILLFOCUS:
1918 show_mouseptr(1);
1919 has_focus = FALSE;
1920 DestroyCaret();
1921 term_out();
1922 term_update();
1923 break;
1924 case WM_ENTERSIZEMOVE:
1925 #ifdef RDB_DEBUG_PATCH
1926 debug((27, "WM_ENTERSIZEMOVE"));
1927 #endif
1928 EnableSizeTip(1);
1929 resizing = TRUE;
1930 need_backend_resize = FALSE;
1931 break;
1932 case WM_EXITSIZEMOVE:
1933 EnableSizeTip(0);
1934 resizing = FALSE;
1935 #ifdef RDB_DEBUG_PATCH
1936 debug((27, "WM_EXITSIZEMOVE"));
1937 #endif
1938 if (need_backend_resize) {
1939 term_size(cfg.height, cfg.width, cfg.savelines);
1940 InvalidateRect(hwnd, NULL, TRUE);
1941 }
1942 break;
1943 case WM_SIZING:
1944 /*
1945 * This does two jobs:
1946 * 1) Keep the sizetip uptodate
1947 * 2) Make sure the window size is _stepped_ in units of the font size.
1948 */
1949 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1950 int width, height, w, h, ew, eh;
1951 LPRECT r = (LPRECT) lParam;
1952
1953 if ( !need_backend_resize &&
1954 (cfg.height != rows || cfg.width != cols )) {
1955 /*
1956 * Great! It seems the host has been changing the terminal
1957 * size, well the user is now grabbing so this is probably
1958 * the least confusing solution in the long run even though
1959 * it a is suprise. Unfortunatly the only way to prevent
1960 * this seems to be to let the host change the window size
1961 * and as that's a user option we're still right back here.
1962 */
1963 term_size(cfg.height, cfg.width, cfg.savelines);
1964 reset_window(2);
1965 InvalidateRect(hwnd, NULL, TRUE);
1966 need_backend_resize = TRUE;
1967 }
1968
1969 width = r->right - r->left - extra_width;
1970 height = r->bottom - r->top - extra_height;
1971 w = (width + font_width / 2) / font_width;
1972 if (w < 1)
1973 w = 1;
1974 h = (height + font_height / 2) / font_height;
1975 if (h < 1)
1976 h = 1;
1977 UpdateSizeTip(hwnd, w, h);
1978 ew = width - w * font_width;
1979 eh = height - h * font_height;
1980 if (ew != 0) {
1981 if (wParam == WMSZ_LEFT ||
1982 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1983 r->left += ew;
1984 else
1985 r->right -= ew;
1986 }
1987 if (eh != 0) {
1988 if (wParam == WMSZ_TOP ||
1989 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1990 r->top += eh;
1991 else
1992 r->bottom -= eh;
1993 }
1994 if (ew || eh)
1995 return 1;
1996 else
1997 return 0;
1998 } else {
1999 int width, height, w, h, rv = 0;
2000 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2001 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2002 LPRECT r = (LPRECT) lParam;
2003
2004 width = r->right - r->left - ex_width;
2005 height = r->bottom - r->top - ex_height;
2006
2007 w = (width + cols/2)/cols;
2008 h = (height + rows/2)/rows;
2009 if ( r->right != r->left + w*cols + ex_width)
2010 rv = 1;
2011
2012 if (wParam == WMSZ_LEFT ||
2013 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2014 r->left = r->right - w*cols - ex_width;
2015 else
2016 r->right = r->left + w*cols + ex_width;
2017
2018 if (r->bottom != r->top + h*rows + ex_height)
2019 rv = 1;
2020
2021 if (wParam == WMSZ_TOP ||
2022 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2023 r->top = r->bottom - h*rows - ex_height;
2024 else
2025 r->bottom = r->top + h*rows + ex_height;
2026
2027 return rv;
2028 }
2029 /* break; (never reached) */
2030 case WM_SIZE:
2031 #ifdef RDB_DEBUG_PATCH
2032 debug((27, "WM_SIZE %s (%d,%d)",
2033 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2034 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2035 (wParam == SIZE_RESTORED && resizing) ? "to":
2036 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2037 "...",
2038 LOWORD(lParam), HIWORD(lParam)));
2039 #endif
2040 if (wParam == SIZE_MINIMIZED) {
2041 SetWindowText(hwnd,
2042 cfg.win_name_always ? window_name : icon_name);
2043 break;
2044 }
2045 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2046 SetWindowText(hwnd, window_name);
2047
2048 if (cfg.resize_action == RESIZE_DISABLED) {
2049 /* A resize, well it better be a minimize. */
2050 reset_window(-1);
2051 } else {
2052
2053 int width, height, w, h;
2054
2055 width = LOWORD(lParam);
2056 height = HIWORD(lParam);
2057
2058 if (!resizing) {
2059 if (wParam == SIZE_MAXIMIZED) {
2060 was_zoomed = 1;
2061 prev_rows = rows;
2062 prev_cols = cols;
2063 if (cfg.resize_action != RESIZE_FONT) {
2064 w = width / font_width;
2065 if (w < 1) w = 1;
2066 h = height / font_height;
2067 if (h < 1) h = 1;
2068
2069 term_size(h, w, cfg.savelines);
2070 }
2071 reset_window(0);
2072 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2073 was_zoomed = 0;
2074 if (cfg.resize_action != RESIZE_FONT)
2075 term_size(prev_rows, prev_cols, cfg.savelines);
2076 reset_window(0);
2077 }
2078 /* This is an unexpected resize, these will normally happen
2079 * if the window is too large. Probably either the user
2080 * selected a huge font or the screen size has changed.
2081 *
2082 * This is also called with minimize.
2083 */
2084 else reset_window(-1);
2085 }
2086
2087 /*
2088 * Don't call back->size in mid-resize. (To prevent
2089 * massive numbers of resize events getting sent
2090 * down the connection during an NT opaque drag.)
2091 */
2092 if (resizing) {
2093 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2094 need_backend_resize = TRUE;
2095 w = (width-cfg.window_border*2) / font_width;
2096 if (w < 1) w = 1;
2097 h = (height-cfg.window_border*2) / font_height;
2098 if (h < 1) h = 1;
2099
2100 cfg.height = h;
2101 cfg.width = w;
2102 } else
2103 reset_window(0);
2104 }
2105 }
2106 return 0;
2107 case WM_VSCROLL:
2108 switch (LOWORD(wParam)) {
2109 case SB_BOTTOM:
2110 term_scroll(-1, 0);
2111 break;
2112 case SB_TOP:
2113 term_scroll(+1, 0);
2114 break;
2115 case SB_LINEDOWN:
2116 term_scroll(0, +1);
2117 break;
2118 case SB_LINEUP:
2119 term_scroll(0, -1);
2120 break;
2121 case SB_PAGEDOWN:
2122 term_scroll(0, +rows / 2);
2123 break;
2124 case SB_PAGEUP:
2125 term_scroll(0, -rows / 2);
2126 break;
2127 case SB_THUMBPOSITION:
2128 case SB_THUMBTRACK:
2129 term_scroll(1, HIWORD(wParam));
2130 break;
2131 }
2132 break;
2133 case WM_PALETTECHANGED:
2134 if ((HWND) wParam != hwnd && pal != NULL) {
2135 HDC hdc = get_ctx();
2136 if (hdc) {
2137 if (RealizePalette(hdc) > 0)
2138 UpdateColors(hdc);
2139 free_ctx(hdc);
2140 }
2141 }
2142 break;
2143 case WM_QUERYNEWPALETTE:
2144 if (pal != NULL) {
2145 HDC hdc = get_ctx();
2146 if (hdc) {
2147 if (RealizePalette(hdc) > 0)
2148 UpdateColors(hdc);
2149 free_ctx(hdc);
2150 return TRUE;
2151 }
2152 }
2153 return FALSE;
2154 case WM_KEYDOWN:
2155 case WM_SYSKEYDOWN:
2156 case WM_KEYUP:
2157 case WM_SYSKEYUP:
2158 /*
2159 * Add the scan code and keypress timing to the random
2160 * number noise.
2161 */
2162 noise_ultralight(lParam);
2163
2164 /*
2165 * We don't do TranslateMessage since it disassociates the
2166 * resulting CHAR message from the KEYDOWN that sparked it,
2167 * which we occasionally don't want. Instead, we process
2168 * KEYDOWN, and call the Win32 translator functions so that
2169 * we get the translations under _our_ control.
2170 */
2171 {
2172 unsigned char buf[20];
2173 int len;
2174
2175 if (wParam == VK_PROCESSKEY) {
2176 MSG m;
2177 m.hwnd = hwnd;
2178 m.message = WM_KEYDOWN;
2179 m.wParam = wParam;
2180 m.lParam = lParam & 0xdfff;
2181 TranslateMessage(&m);
2182 } else {
2183 len = TranslateKey(message, wParam, lParam, buf);
2184 if (len == -1)
2185 return DefWindowProc(hwnd, message, wParam, lParam);
2186
2187 if (len != 0) {
2188 /*
2189 * Interrupt an ongoing paste. I'm not sure
2190 * this is sensible, but for the moment it's
2191 * preferable to having to faff about buffering
2192 * things.
2193 */
2194 term_nopaste();
2195
2196 /*
2197 * We need not bother about stdin backlogs
2198 * here, because in GUI PuTTY we can't do
2199 * anything about it anyway; there's no means
2200 * of asking Windows to hold off on KEYDOWN
2201 * messages. We _have_ to buffer everything
2202 * we're sent.
2203 */
2204 ldisc_send(buf, len, 1);
2205 show_mouseptr(0);
2206 }
2207 }
2208 }
2209 net_pending_errors();
2210 return 0;
2211 case WM_INPUTLANGCHANGE:
2212 {
2213 /* wParam == Font number */
2214 /* lParam == Locale */
2215 char lbuf[20];
2216 HKL NewInputLocale = (HKL) lParam;
2217
2218 // lParam == GetKeyboardLayout(0);
2219
2220 GetLocaleInfo(LOWORD(NewInputLocale),
2221 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2222
2223 kbd_codepage = atoi(lbuf);
2224 }
2225 break;
2226 case WM_IME_COMPOSITION:
2227 {
2228 HIMC hIMC;
2229 int n;
2230 char *buff;
2231
2232 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2233 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2234
2235 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2236 break; /* fall back to DefWindowProc */
2237
2238 hIMC = ImmGetContext(hwnd);
2239 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2240
2241 if (n > 0) {
2242 buff = (char*) smalloc(n);
2243 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2244 luni_send((unsigned short *)buff, n / 2, 1);
2245 free(buff);
2246 }
2247 ImmReleaseContext(hwnd, hIMC);
2248 return 1;
2249 }
2250
2251 case WM_IME_CHAR:
2252 if (wParam & 0xFF00) {
2253 unsigned char buf[2];
2254
2255 buf[1] = wParam;
2256 buf[0] = wParam >> 8;
2257 lpage_send(kbd_codepage, buf, 2, 1);
2258 } else {
2259 char c = (unsigned char) wParam;
2260 lpage_send(kbd_codepage, &c, 1, 1);
2261 }
2262 return (0);
2263 case WM_CHAR:
2264 case WM_SYSCHAR:
2265 /*
2266 * Nevertheless, we are prepared to deal with WM_CHAR
2267 * messages, should they crop up. So if someone wants to
2268 * post the things to us as part of a macro manoeuvre,
2269 * we're ready to cope.
2270 */
2271 {
2272 char c = (unsigned char)wParam;
2273 lpage_send(CP_ACP, &c, 1, 1);
2274 }
2275 return 0;
2276 case WM_SETCURSOR:
2277 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2278 SetCursor(LoadCursor(NULL, IDC_ARROW));
2279 return TRUE;
2280 }
2281 }
2282
2283 return DefWindowProc(hwnd, message, wParam, lParam);
2284 }
2285
2286 /*
2287 * Move the system caret. (We maintain one, even though it's
2288 * invisible, for the benefit of blind people: apparently some
2289 * helper software tracks the system caret, so we should arrange to
2290 * have one.)
2291 */
2292 void sys_cursor(int x, int y)
2293 {
2294 COMPOSITIONFORM cf;
2295 HIMC hIMC;
2296
2297 if (!has_focus) return;
2298
2299 SetCaretPos(x * font_width + offset_width,
2300 y * font_height + offset_height);
2301
2302 /* IMM calls on Win98 and beyond only */
2303 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2304
2305 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2306 osVersion.dwMinorVersion == 0) return; /* 95 */
2307
2308 /* we should have the IMM functions */
2309 hIMC = ImmGetContext(hwnd);
2310 cf.dwStyle = CFS_POINT;
2311 cf.ptCurrentPos.x = x * font_width;
2312 cf.ptCurrentPos.y = y * font_height;
2313 ImmSetCompositionWindow(hIMC, &cf);
2314
2315 ImmReleaseContext(hwnd, hIMC);
2316 }
2317
2318 /*
2319 * Draw a line of text in the window, at given character
2320 * coordinates, in given attributes.
2321 *
2322 * We are allowed to fiddle with the contents of `text'.
2323 */
2324 void do_text(Context ctx, int x, int y, char *text, int len,
2325 unsigned long attr, int lattr)
2326 {
2327 COLORREF fg, bg, t;
2328 int nfg, nbg, nfont;
2329 HDC hdc = ctx;
2330 RECT line_box;
2331 int force_manual_underline = 0;
2332 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2333 int char_width = fnt_width;
2334 int text_adjust = 0;
2335 static int *IpDx = 0, IpDxLEN = 0;
2336
2337 if (attr & ATTR_WIDE)
2338 char_width *= 2;
2339
2340 if (len > IpDxLEN || IpDx[0] != char_width) {
2341 int i;
2342 if (len > IpDxLEN) {
2343 sfree(IpDx);
2344 IpDx = smalloc((len + 16) * sizeof(int));
2345 IpDxLEN = (len + 16);
2346 }
2347 for (i = 0; i < IpDxLEN; i++)
2348 IpDx[i] = char_width;
2349 }
2350
2351 /* Only want the left half of double width lines */
2352 if (lattr != LATTR_NORM && x*2 >= cols)
2353 return;
2354
2355 x *= fnt_width;
2356 y *= font_height;
2357 x += offset_width;
2358 y += offset_height;
2359
2360 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2361 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2362 attr ^= ATTR_CUR_XOR;
2363 }
2364
2365 nfont = 0;
2366 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2367 /* Assume a poorman font is borken in other ways too. */
2368 lattr = LATTR_WIDE;
2369 } else
2370 switch (lattr) {
2371 case LATTR_NORM:
2372 break;
2373 case LATTR_WIDE:
2374 nfont |= FONT_WIDE;
2375 break;
2376 default:
2377 nfont |= FONT_WIDE + FONT_HIGH;
2378 break;
2379 }
2380 if (attr & ATTR_NARROW)
2381 nfont |= FONT_NARROW;
2382
2383 /* Special hack for the VT100 linedraw glyphs. */
2384 if ((attr & CSET_MASK) == 0x2300) {
2385 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2386 switch ((unsigned char) (text[0])) {
2387 case 0xBA:
2388 text_adjust = -2 * font_height / 5;
2389 break;
2390 case 0xBB:
2391 text_adjust = -1 * font_height / 5;
2392 break;
2393 case 0xBC:
2394 text_adjust = font_height / 5;
2395 break;
2396 case 0xBD:
2397 text_adjust = 2 * font_height / 5;
2398 break;
2399 }
2400 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2401 text_adjust *= 2;
2402 attr &= ~CSET_MASK;
2403 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2404 attr |= (unitab_xterm['q'] & CSET_MASK);
2405 if (attr & ATTR_UNDER) {
2406 attr &= ~ATTR_UNDER;
2407 force_manual_underline = 1;
2408 }
2409 }
2410 }
2411
2412 /* Anything left as an original character set is unprintable. */
2413 if (DIRECT_CHAR(attr)) {
2414 attr &= ~CSET_MASK;
2415 attr |= 0xFF00;
2416 memset(text, 0xFD, len);
2417 }
2418
2419 /* OEM CP */
2420 if ((attr & CSET_MASK) == ATTR_OEMCP)
2421 nfont |= FONT_OEM;
2422
2423 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2424 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2425 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2426 nfont |= FONT_BOLD;
2427 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2428 nfont |= FONT_UNDERLINE;
2429 another_font(nfont);
2430 if (!fonts[nfont]) {
2431 if (nfont & FONT_UNDERLINE)
2432 force_manual_underline = 1;
2433 /* Don't do the same for manual bold, it could be bad news. */
2434
2435 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2436 }
2437 another_font(nfont);
2438 if (!fonts[nfont])
2439 nfont = FONT_NORMAL;
2440 if (attr & ATTR_REVERSE) {
2441 t = nfg;
2442 nfg = nbg;
2443 nbg = t;
2444 }
2445 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2446 nfg++;
2447 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2448 nbg++;
2449 fg = colours[nfg];
2450 bg = colours[nbg];
2451 SelectObject(hdc, fonts[nfont]);
2452 SetTextColor(hdc, fg);
2453 SetBkColor(hdc, bg);
2454 SetBkMode(hdc, OPAQUE);
2455 line_box.left = x;
2456 line_box.top = y;
2457 line_box.right = x + char_width * len;
2458 line_box.bottom = y + font_height;
2459
2460 /* Only want the left half of double width lines */
2461 if (line_box.right > font_width*cols+offset_width)
2462 line_box.right = font_width*cols+offset_width;
2463
2464 /* We're using a private area for direct to font. (512 chars.) */
2465 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2466 /* Ho Hum, dbcs fonts are a PITA! */
2467 /* To display on W9x I have to convert to UCS */
2468 static wchar_t *uni_buf = 0;
2469 static int uni_len = 0;
2470 int nlen, mptr;
2471 if (len > uni_len) {
2472 sfree(uni_buf);
2473 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2474 }
2475
2476 for(nlen = mptr = 0; mptr<len; mptr++) {
2477 uni_buf[nlen] = 0xFFFD;
2478 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2479 IpDx[nlen] += char_width;
2480 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2481 text+mptr, 2, uni_buf+nlen, 1);
2482 mptr++;
2483 }
2484 else
2485 {
2486 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2487 text+mptr, 1, uni_buf+nlen, 1);
2488 }
2489 nlen++;
2490 }
2491 if (nlen <= 0)
2492 return; /* Eeek! */
2493
2494 ExtTextOutW(hdc, x,
2495 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2496 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2497 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2498 SetBkMode(hdc, TRANSPARENT);
2499 ExtTextOutW(hdc, x - 1,
2500 y - font_height * (lattr ==
2501 LATTR_BOT) + text_adjust,
2502 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2503 }
2504
2505 IpDx[0] = -1;
2506 } else if (DIRECT_FONT(attr)) {
2507 ExtTextOut(hdc, x,
2508 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2509 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2510 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2511 SetBkMode(hdc, TRANSPARENT);
2512
2513 /* GRR: This draws the character outside it's box and can leave
2514 * 'droppings' even with the clip box! I suppose I could loop it
2515 * one character at a time ... yuk.
2516 *
2517 * Or ... I could do a test print with "W", and use +1 or -1 for this
2518 * shift depending on if the leftmost column is blank...
2519 */
2520 ExtTextOut(hdc, x - 1,
2521 y - font_height * (lattr ==
2522 LATTR_BOT) + text_adjust,
2523 ETO_CLIPPED, &line_box, text, len, IpDx);
2524 }
2525 } else {
2526 /* And 'normal' unicode characters */
2527 static WCHAR *wbuf = NULL;
2528 static int wlen = 0;
2529 int i;
2530 if (wlen < len) {
2531 sfree(wbuf);
2532 wlen = len;
2533 wbuf = smalloc(wlen * sizeof(WCHAR));
2534 }
2535 for (i = 0; i < len; i++)
2536 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2537
2538 ExtTextOutW(hdc, x,
2539 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2540 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2541
2542 /* And the shadow bold hack. */
2543 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2544 SetBkMode(hdc, TRANSPARENT);
2545 ExtTextOutW(hdc, x - 1,
2546 y - font_height * (lattr ==
2547 LATTR_BOT) + text_adjust,
2548 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2549 }
2550 }
2551 if (lattr != LATTR_TOP && (force_manual_underline ||
2552 (und_mode == UND_LINE
2553 && (attr & ATTR_UNDER)))) {
2554 HPEN oldpen;
2555 int dec = descent;
2556 if (lattr == LATTR_BOT)
2557 dec = dec * 2 - font_height;
2558
2559 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2560 MoveToEx(hdc, x, y + dec, NULL);
2561 LineTo(hdc, x + len * char_width, y + dec);
2562 oldpen = SelectObject(hdc, oldpen);
2563 DeleteObject(oldpen);
2564 }
2565 }
2566
2567 void do_cursor(Context ctx, int x, int y, char *text, int len,
2568 unsigned long attr, int lattr)
2569 {
2570
2571 int fnt_width;
2572 int char_width;
2573 HDC hdc = ctx;
2574 int ctype = cfg.cursor_type;
2575
2576 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2577 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2578 do_text(ctx, x, y, text, len, attr, lattr);
2579 return;
2580 }
2581 ctype = 2;
2582 attr |= TATTR_RIGHTCURS;
2583 }
2584
2585 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2586 if (attr & ATTR_WIDE)
2587 char_width *= 2;
2588 x *= fnt_width;
2589 y *= font_height;
2590 x += offset_width;
2591 y += offset_height;
2592
2593 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2594 POINT pts[5];
2595 HPEN oldpen;
2596 pts[0].x = pts[1].x = pts[4].x = x;
2597 pts[2].x = pts[3].x = x + char_width - 1;
2598 pts[0].y = pts[3].y = pts[4].y = y;
2599 pts[1].y = pts[2].y = y + font_height - 1;
2600 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2601 Polyline(hdc, pts, 5);
2602 oldpen = SelectObject(hdc, oldpen);
2603 DeleteObject(oldpen);
2604 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2605 int startx, starty, dx, dy, length, i;
2606 if (ctype == 1) {
2607 startx = x;
2608 starty = y + descent;
2609 dx = 1;
2610 dy = 0;
2611 length = char_width;
2612 } else {
2613 int xadjust = 0;
2614 if (attr & TATTR_RIGHTCURS)
2615 xadjust = char_width - 1;
2616 startx = x + xadjust;
2617 starty = y;
2618 dx = 0;
2619 dy = 1;
2620 length = font_height;
2621 }
2622 if (attr & TATTR_ACTCURS) {
2623 HPEN oldpen;
2624 oldpen =
2625 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2626 MoveToEx(hdc, startx, starty, NULL);
2627 LineTo(hdc, startx + dx * length, starty + dy * length);
2628 oldpen = SelectObject(hdc, oldpen);
2629 DeleteObject(oldpen);
2630 } else {
2631 for (i = 0; i < length; i++) {
2632 if (i % 2 == 0) {
2633 SetPixel(hdc, startx, starty, colours[23]);
2634 }
2635 startx += dx;
2636 starty += dy;
2637 }
2638 }
2639 }
2640 }
2641
2642 /* This function gets the actual width of a character in the normal font.
2643 */
2644 int CharWidth(Context ctx, int uc) {
2645 HDC hdc = ctx;
2646 int ibuf = 0;
2647
2648 /* If the font max is the same as the font ave width then this
2649 * function is a no-op.
2650 */
2651 if (!font_dualwidth) return 1;
2652
2653 switch (uc & CSET_MASK) {
2654 case ATTR_ASCII:
2655 uc = unitab_line[uc & 0xFF];
2656 break;
2657 case ATTR_LINEDRW:
2658 uc = unitab_xterm[uc & 0xFF];
2659 break;
2660 case ATTR_SCOACS:
2661 uc = unitab_scoacs[uc & 0xFF];
2662 break;
2663 }
2664 if (DIRECT_FONT(uc)) {
2665 if (dbcs_screenfont) return 1;
2666
2667 /* Speedup, I know of no font where ascii is the wrong width */
2668 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2669 return 1;
2670
2671 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2672 SelectObject(hdc, fonts[FONT_NORMAL]);
2673 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2674 another_font(FONT_OEM);
2675 if (!fonts[FONT_OEM]) return 0;
2676
2677 SelectObject(hdc, fonts[FONT_OEM]);
2678 } else
2679 return 0;
2680
2681 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2682 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2683 return 0;
2684 } else {
2685 /* Speedup, I know of no font where ascii is the wrong width */
2686 if (uc >= ' ' && uc <= '~') return 1;
2687
2688 SelectObject(hdc, fonts[FONT_NORMAL]);
2689 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2690 /* Okay that one worked */ ;
2691 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2692 /* This should work on 9x too, but it's "less accurate" */ ;
2693 else
2694 return 0;
2695 }
2696
2697 ibuf += font_width / 2 -1;
2698 ibuf /= font_width;
2699
2700 return ibuf;
2701 }
2702
2703 /*
2704 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2705 * codes. Returns number of bytes used or zero to drop the message
2706 * or -1 to forward the message to windows.
2707 */
2708 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2709 unsigned char *output)
2710 {
2711 BYTE keystate[256];
2712 int scan, left_alt = 0, key_down, shift_state;
2713 int r, i, code;
2714 unsigned char *p = output;
2715 static int alt_sum = 0;
2716
2717 HKL kbd_layout = GetKeyboardLayout(0);
2718
2719 static WORD keys[3];
2720 static int compose_char = 0;
2721 static WPARAM compose_key = 0;
2722
2723 r = GetKeyboardState(keystate);
2724 if (!r)
2725 memset(keystate, 0, sizeof(keystate));
2726 else {
2727 #if 0
2728 #define SHOW_TOASCII_RESULT
2729 { /* Tell us all about key events */
2730 static BYTE oldstate[256];
2731 static int first = 1;
2732 static int scan;
2733 int ch;
2734 if (first)
2735 memcpy(oldstate, keystate, sizeof(oldstate));
2736 first = 0;
2737
2738 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2739 debug(("+"));
2740 } else if ((HIWORD(lParam) & KF_UP)
2741 && scan == (HIWORD(lParam) & 0xFF)) {
2742 debug((". U"));
2743 } else {
2744 debug((".\n"));
2745 if (wParam >= VK_F1 && wParam <= VK_F20)
2746 debug(("K_F%d", wParam + 1 - VK_F1));
2747 else
2748 switch (wParam) {
2749 case VK_SHIFT:
2750 debug(("SHIFT"));
2751 break;
2752 case VK_CONTROL:
2753 debug(("CTRL"));
2754 break;
2755 case VK_MENU:
2756 debug(("ALT"));
2757 break;
2758 default:
2759 debug(("VK_%02x", wParam));
2760 }
2761 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2762 debug(("*"));
2763 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2764
2765 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2766 if (ch >= ' ' && ch <= '~')
2767 debug((", '%c'", ch));
2768 else if (ch)
2769 debug((", $%02x", ch));
2770
2771 if (keys[0])
2772 debug((", KB0=%02x", keys[0]));
2773 if (keys[1])
2774 debug((", KB1=%02x", keys[1]));
2775 if (keys[2])
2776 debug((", KB2=%02x", keys[2]));
2777
2778 if ((keystate[VK_SHIFT] & 0x80) != 0)
2779 debug((", S"));
2780 if ((keystate[VK_CONTROL] & 0x80) != 0)
2781 debug((", C"));
2782 if ((HIWORD(lParam) & KF_EXTENDED))
2783 debug((", E"));
2784 if ((HIWORD(lParam) & KF_UP))
2785 debug((", U"));
2786 }
2787
2788 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2789 else if ((HIWORD(lParam) & KF_UP))
2790 oldstate[wParam & 0xFF] ^= 0x80;
2791 else
2792 oldstate[wParam & 0xFF] ^= 0x81;
2793
2794 for (ch = 0; ch < 256; ch++)
2795 if (oldstate[ch] != keystate[ch])
2796 debug((", M%02x=%02x", ch, keystate[ch]));
2797
2798 memcpy(oldstate, keystate, sizeof(oldstate));
2799 }
2800 #endif
2801
2802 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2803 keystate[VK_RMENU] = keystate[VK_MENU];
2804 }
2805
2806
2807 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2808 if ((cfg.funky_type == 3 ||
2809 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2810 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2811
2812 wParam = VK_EXECUTE;
2813
2814 /* UnToggle NUMLock */
2815 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2816 keystate[VK_NUMLOCK] ^= 1;
2817 }
2818
2819 /* And write back the 'adjusted' state */
2820 SetKeyboardState(keystate);
2821 }
2822
2823 /* Disable Auto repeat if required */
2824 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2825 return 0;
2826
2827 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2828 left_alt = 1;
2829
2830 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2831
2832 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2833 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2834 if (cfg.ctrlaltkeys)
2835 keystate[VK_MENU] = 0;
2836 else {
2837 keystate[VK_RMENU] = 0x80;
2838 left_alt = 0;
2839 }
2840 }
2841
2842 alt_pressed = (left_alt && key_down);
2843
2844 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2845 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2846 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2847
2848 /* Note if AltGr was pressed and if it was used as a compose key */
2849 if (!compose_state) {
2850 compose_key = 0x100;
2851 if (cfg.compose_key) {
2852 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2853 compose_key = wParam;
2854 }
2855 if (wParam == VK_APPS)
2856 compose_key = wParam;
2857 }
2858
2859 if (wParam == compose_key) {
2860 if (compose_state == 0
2861 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2862 1;
2863 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2864 compose_state = 2;
2865 else
2866 compose_state = 0;
2867 } else if (compose_state == 1 && wParam != VK_CONTROL)
2868 compose_state = 0;
2869
2870 /*
2871 * Record that we pressed key so the scroll window can be reset, but
2872 * be careful to avoid Shift-UP/Down
2873 */
2874 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2875 seen_key_event = 1;
2876 }
2877
2878 if (compose_state > 1 && left_alt)
2879 compose_state = 0;
2880
2881 /* Sanitize the number pad if not using a PC NumPad */
2882 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2883 && cfg.funky_type != 2)
2884 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2885 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2886 int nParam = 0;
2887 switch (wParam) {
2888 case VK_INSERT:
2889 nParam = VK_NUMPAD0;
2890 break;
2891 case VK_END:
2892 nParam = VK_NUMPAD1;
2893 break;
2894 case VK_DOWN:
2895 nParam = VK_NUMPAD2;
2896 break;
2897 case VK_NEXT:
2898 nParam = VK_NUMPAD3;
2899 break;
2900 case VK_LEFT:
2901 nParam = VK_NUMPAD4;
2902 break;
2903 case VK_CLEAR:
2904 nParam = VK_NUMPAD5;
2905 break;
2906 case VK_RIGHT:
2907 nParam = VK_NUMPAD6;
2908 break;
2909 case VK_HOME:
2910 nParam = VK_NUMPAD7;
2911 break;
2912 case VK_UP:
2913 nParam = VK_NUMPAD8;
2914 break;
2915 case VK_PRIOR:
2916 nParam = VK_NUMPAD9;
2917 break;
2918 case VK_DELETE:
2919 nParam = VK_DECIMAL;
2920 break;
2921 }
2922 if (nParam) {
2923 if (keystate[VK_NUMLOCK] & 1)
2924 shift_state |= 1;
2925 wParam = nParam;
2926 }
2927 }
2928 }
2929
2930 /* If a key is pressed and AltGr is not active */
2931 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2932 /* Okay, prepare for most alts then ... */
2933 if (left_alt)
2934 *p++ = '\033';
2935
2936 /* Lets see if it's a pattern we know all about ... */
2937 if (wParam == VK_PRIOR && shift_state == 1) {
2938 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2939 return 0;
2940 }
2941 if (wParam == VK_NEXT && shift_state == 1) {
2942 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2943 return 0;
2944 }
2945 if (wParam == VK_INSERT && shift_state == 1) {
2946 term_do_paste();
2947 return 0;
2948 }
2949 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2950 return -1;
2951 }
2952 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2953 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2954 return -1;
2955 }
2956 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2957 flip_full_screen();
2958 return -1;
2959 }
2960 /* Control-Numlock for app-keypad mode switch */
2961 if (wParam == VK_PAUSE && shift_state == 2) {
2962 app_keypad_keys ^= 1;
2963 return 0;
2964 }
2965
2966 /* Nethack keypad */
2967 if (cfg.nethack_keypad && !left_alt) {
2968 switch (wParam) {
2969 case VK_NUMPAD1:
2970 *p++ = shift_state ? 'B' : 'b';
2971 return p - output;
2972 case VK_NUMPAD2:
2973 *p++ = shift_state ? 'J' : 'j';
2974 return p - output;
2975 case VK_NUMPAD3:
2976 *p++ = shift_state ? 'N' : 'n';
2977 return p - output;
2978 case VK_NUMPAD4:
2979 *p++ = shift_state ? 'H' : 'h';
2980 return p - output;
2981 case VK_NUMPAD5:
2982 *p++ = shift_state ? '.' : '.';
2983 return p - output;
2984 case VK_NUMPAD6:
2985 *p++ = shift_state ? 'L' : 'l';
2986 return p - output;
2987 case VK_NUMPAD7:
2988 *p++ = shift_state ? 'Y' : 'y';
2989 return p - output;
2990 case VK_NUMPAD8:
2991 *p++ = shift_state ? 'K' : 'k';
2992 return p - output;
2993 case VK_NUMPAD9:
2994 *p++ = shift_state ? 'U' : 'u';
2995 return p - output;
2996 }
2997 }
2998
2999 /* Application Keypad */
3000 if (!left_alt) {
3001 int xkey = 0;
3002
3003 if (cfg.funky_type == 3 ||
3004 (cfg.funky_type <= 1 &&
3005 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3006 case VK_EXECUTE:
3007 xkey = 'P';
3008 break;
3009 case VK_DIVIDE:
3010 xkey = 'Q';
3011 break;
3012 case VK_MULTIPLY:
3013 xkey = 'R';
3014 break;
3015 case VK_SUBTRACT:
3016 xkey = 'S';
3017 break;
3018 }
3019 if (app_keypad_keys && !cfg.no_applic_k)
3020 switch (wParam) {
3021 case VK_NUMPAD0:
3022 xkey = 'p';
3023 break;
3024 case VK_NUMPAD1:
3025 xkey = 'q';
3026 break;
3027 case VK_NUMPAD2:
3028 xkey = 'r';
3029 break;
3030 case VK_NUMPAD3:
3031 xkey = 's';
3032 break;
3033 case VK_NUMPAD4:
3034 xkey = 't';
3035 break;
3036 case VK_NUMPAD5:
3037 xkey = 'u';
3038 break;
3039 case VK_NUMPAD6:
3040 xkey = 'v';
3041 break;
3042 case VK_NUMPAD7:
3043 xkey = 'w';
3044 break;
3045 case VK_NUMPAD8:
3046 xkey = 'x';
3047 break;
3048 case VK_NUMPAD9:
3049 xkey = 'y';
3050 break;
3051
3052 case VK_DECIMAL:
3053 xkey = 'n';
3054 break;
3055 case VK_ADD:
3056 if (cfg.funky_type == 2) {
3057 if (shift_state)
3058 xkey = 'l';
3059 else
3060 xkey = 'k';
3061 } else if (shift_state)
3062 xkey = 'm';
3063 else
3064 xkey = 'l';
3065 break;
3066
3067 case VK_DIVIDE:
3068 if (cfg.funky_type == 2)
3069 xkey = 'o';
3070 break;
3071 case VK_MULTIPLY:
3072 if (cfg.funky_type == 2)
3073 xkey = 'j';
3074 break;
3075 case VK_SUBTRACT:
3076 if (cfg.funky_type == 2)
3077 xkey = 'm';
3078 break;
3079
3080 case VK_RETURN:
3081 if (HIWORD(lParam) & KF_EXTENDED)
3082 xkey = 'M';
3083 break;
3084 }
3085 if (xkey) {
3086 if (vt52_mode) {
3087 if (xkey >= 'P' && xkey <= 'S')
3088 p += sprintf((char *) p, "\x1B%c", xkey);
3089 else
3090 p += sprintf((char *) p, "\x1B?%c", xkey);
3091 } else
3092 p += sprintf((char *) p, "\x1BO%c", xkey);
3093 return p - output;
3094 }
3095 }
3096
3097 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3098 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3099 *p++ = 0;
3100 return -2;
3101 }
3102 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3103 *p++ = 0x1B;
3104 *p++ = '[';
3105 *p++ = 'Z';
3106 return p - output;
3107 }
3108 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3109 *p++ = 0;
3110 return p - output;
3111 }
3112 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3113 *p++ = 160;
3114 return p - output;
3115 }
3116 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3117 *p++ = 3;
3118 *p++ = 0;
3119 return -2;
3120 }
3121 if (wParam == VK_PAUSE) { /* Break/Pause */
3122 *p++ = 26;
3123 *p++ = 0;
3124 return -2;
3125 }
3126 /* Control-2 to Control-8 are special */
3127 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3128 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3129 return p - output;
3130 }
3131 if (shift_state == 2 && wParam == 0xBD) {
3132 *p++ = 0x1F;
3133 return p - output;
3134 }
3135 if (shift_state == 2 && wParam == 0xDF) {
3136 *p++ = 0x1C;
3137 return p - output;
3138 }
3139 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3140 *p++ = '\r';
3141 *p++ = '\n';
3142 return p - output;
3143 }
3144
3145 /*
3146 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3147 * for integer decimal nn.)
3148 *
3149 * We also deal with the weird ones here. Linux VCs replace F1
3150 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3151 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3152 * respectively.
3153 */
3154 code = 0;
3155 switch (wParam) {
3156 case VK_F1:
3157 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3158 break;
3159 case VK_F2:
3160 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3161 break;
3162 case VK_F3:
3163 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3164 break;
3165 case VK_F4:
3166 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3167 break;
3168 case VK_F5:
3169 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3170 break;
3171 case VK_F6:
3172 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3173 break;
3174 case VK_F7:
3175 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3176 break;
3177 case VK_F8:
3178 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3179 break;
3180 case VK_F9:
3181 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3182 break;
3183 case VK_F10:
3184 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3185 break;
3186 case VK_F11:
3187 code = 23;
3188 break;
3189 case VK_F12:
3190 code = 24;
3191 break;
3192 case VK_F13:
3193 code = 25;
3194 break;
3195 case VK_F14:
3196 code = 26;
3197 break;
3198 case VK_F15:
3199 code = 28;
3200 break;
3201 case VK_F16:
3202 code = 29;
3203 break;
3204 case VK_F17:
3205 code = 31;
3206 break;
3207 case VK_F18:
3208 code = 32;
3209 break;
3210 case VK_F19:
3211 code = 33;
3212 break;
3213 case VK_F20:
3214 code = 34;
3215 break;
3216 }
3217 if ((shift_state&2) == 0) switch (wParam) {
3218 case VK_HOME:
3219 code = 1;
3220 break;
3221 case VK_INSERT:
3222 code = 2;
3223 break;
3224 case VK_DELETE:
3225 code = 3;
3226 break;
3227 case VK_END:
3228 code = 4;
3229 break;
3230 case VK_PRIOR:
3231 code = 5;
3232 break;
3233 case VK_NEXT:
3234 code = 6;
3235 break;
3236 }
3237 /* Reorder edit keys to physical order */
3238 if (cfg.funky_type == 3 && code <= 6)
3239 code = "\0\2\1\4\5\3\6"[code];
3240
3241 if (vt52_mode && code > 0 && code <= 6) {
3242 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3243 return p - output;
3244 }
3245
3246 if (cfg.funky_type == 5 && /* SCO function keys */
3247 code >= 11 && code <= 34) {
3248 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3249 int index = 0;
3250 switch (wParam) {
3251 case VK_F1: index = 0; break;
3252 case VK_F2: index = 1; break;
3253 case VK_F3: index = 2; break;
3254 case VK_F4: index = 3; break;
3255 case VK_F5: index = 4; break;
3256 case VK_F6: index = 5; break;
3257 case VK_F7: index = 6; break;
3258 case VK_F8: index = 7; break;
3259 case VK_F9: index = 8; break;
3260 case VK_F10: index = 9; break;
3261 case VK_F11: index = 10; break;
3262 case VK_F12: index = 11; break;
3263 }
3264 if (keystate[VK_SHIFT] & 0x80) index += 12;
3265 if (keystate[VK_CONTROL] & 0x80) index += 24;
3266 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3267 return p - output;
3268 }
3269 if (cfg.funky_type == 5 && /* SCO small keypad */
3270 code >= 1 && code <= 6) {
3271 char codes[] = "HL.FIG";
3272 if (code == 3) {
3273 *p++ = '\x7F';
3274 } else {
3275 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3276 }
3277 return p - output;
3278 }
3279 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3280 int offt = 0;
3281 if (code > 15)
3282 offt++;
3283 if (code > 21)
3284 offt++;
3285 if (vt52_mode)
3286 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3287 else
3288 p +=
3289 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3290 return p - output;
3291 }
3292 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3293 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3294 return p - output;
3295 }
3296 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3297 if (vt52_mode)
3298 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3299 else
3300 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3301 return p - output;
3302 }
3303 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3304 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3305 return p - output;
3306 }
3307 if (code) {
3308 p += sprintf((char *) p, "\x1B[%d~", code);
3309 return p - output;
3310 }
3311
3312 /*
3313 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3314 * some reason seems to send VK_CLEAR to Windows...).
3315 */
3316 {
3317 char xkey = 0;
3318 switch (wParam) {
3319 case VK_UP:
3320 xkey = 'A';
3321 break;
3322 case VK_DOWN:
3323 xkey = 'B';
3324 break;
3325 case VK_RIGHT:
3326 xkey = 'C';
3327 break;
3328 case VK_LEFT:
3329 xkey = 'D';
3330 break;
3331 case VK_CLEAR:
3332 xkey = 'G';
3333 break;
3334 }
3335 if (xkey) {
3336 if (vt52_mode)
3337 p += sprintf((char *) p, "\x1B%c", xkey);
3338 else {
3339 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3340 /* VT100 & VT102 manuals both state the app cursor keys
3341 * only work if the app keypad is on.
3342 */
3343 if (!app_keypad_keys)
3344 app_flg = 0;
3345 /* Useful mapping of Ctrl-arrows */
3346 if (shift_state == 2)
3347 app_flg = !app_flg;
3348
3349 if (app_flg)
3350 p += sprintf((char *) p, "\x1BO%c", xkey);
3351 else
3352 p += sprintf((char *) p, "\x1B[%c", xkey);
3353 }
3354 return p - output;
3355 }
3356 }
3357
3358 /*
3359 * Finally, deal with Return ourselves. (Win95 seems to
3360 * foul it up when Alt is pressed, for some reason.)
3361 */
3362 if (wParam == VK_RETURN) { /* Return */
3363 *p++ = 0x0D;
3364 *p++ = 0;
3365 return -2;
3366 }
3367
3368 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3369 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3370 else
3371 alt_sum = 0;
3372 }
3373
3374 /* Okay we've done everything interesting; let windows deal with
3375 * the boring stuff */
3376 {
3377 BOOL capsOn=0;
3378
3379 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3380 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3381 capsOn= !left_alt;
3382 keystate[VK_CAPITAL] = 0;
3383 }
3384
3385 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3386 #ifdef SHOW_TOASCII_RESULT
3387 if (r == 1 && !key_down) {
3388 if (alt_sum) {
3389 if (in_utf || dbcs_screenfont)
3390 debug((", (U+%04x)", alt_sum));
3391 else
3392 debug((", LCH(%d)", alt_sum));
3393 } else {
3394 debug((", ACH(%d)", keys[0]));
3395 }
3396 } else if (r > 0) {
3397 int r1;
3398 debug((", ASC("));
3399 for (r1 = 0; r1 < r; r1++) {
3400 debug(("%s%d", r1 ? "," : "", keys[r1]));
3401 }
3402 debug((")"));
3403 }
3404 #endif
3405 if (r > 0) {
3406 WCHAR keybuf;
3407
3408 /*
3409 * Interrupt an ongoing paste. I'm not sure this is
3410 * sensible, but for the moment it's preferable to
3411 * having to faff about buffering things.
3412 */
3413 term_nopaste();
3414
3415 p = output;
3416 for (i = 0; i < r; i++) {
3417 unsigned char ch = (unsigned char) keys[i];
3418
3419 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3420 compose_char = ch;
3421 compose_state++;
3422 continue;
3423 }
3424 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3425 int nc;
3426 compose_state = 0;
3427
3428 if ((nc = check_compose(compose_char, ch)) == -1) {
3429 MessageBeep(MB_ICONHAND);
3430 return 0;
3431 }
3432 keybuf = nc;
3433 luni_send(&keybuf, 1, 1);
3434 continue;
3435 }
3436
3437 compose_state = 0;
3438
3439 if (!key_down) {
3440 if (alt_sum) {
3441 if (in_utf || dbcs_screenfont) {
3442 keybuf = alt_sum;
3443 luni_send(&keybuf, 1, 1);
3444 } else {
3445 ch = (char) alt_sum;
3446 /*
3447 * We need not bother about stdin
3448 * backlogs here, because in GUI PuTTY
3449 * we can't do anything about it
3450 * anyway; there's no means of asking
3451 * Windows to hold off on KEYDOWN
3452 * messages. We _have_ to buffer
3453 * everything we're sent.
3454 */
3455 ldisc_send(&ch, 1, 1);
3456 }
3457 alt_sum = 0;
3458 } else
3459 lpage_send(kbd_codepage, &ch, 1, 1);
3460 } else {
3461 if(capsOn && ch < 0x80) {
3462 WCHAR cbuf[2];
3463 cbuf[0] = 27;
3464 cbuf[1] = xlat_uskbd2cyrllic(ch);
3465 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3466 } else {
3467 char cbuf[2];
3468 cbuf[0] = '\033';
3469 cbuf[1] = ch;
3470 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3471 }
3472 }
3473 show_mouseptr(0);
3474 }
3475
3476 /* This is so the ALT-Numpad and dead keys work correctly. */
3477 keys[0] = 0;
3478
3479 return p - output;
3480 }
3481 /* If we're definitly not building up an ALT-54321 then clear it */
3482 if (!left_alt)
3483 keys[0] = 0;
3484 /* If we will be using alt_sum fix the 256s */
3485 else if (keys[0] && (in_utf || dbcs_screenfont))
3486 keys[0] = 10;
3487 }
3488
3489 /*
3490 * ALT alone may or may not want to bring up the System menu.
3491 * If it's not meant to, we return 0 on presses or releases of
3492 * ALT, to show that we've swallowed the keystroke. Otherwise
3493 * we return -1, which means Windows will give the keystroke
3494 * its default handling (i.e. bring up the System menu).
3495 */
3496 if (wParam == VK_MENU && !cfg.alt_only)
3497 return 0;
3498
3499 return -1;
3500 }
3501
3502 void set_title(char *title)
3503 {
3504 sfree(window_name);
3505 window_name = smalloc(1 + strlen(title));
3506 strcpy(window_name, title);
3507 if (cfg.win_name_always || !IsIconic(hwnd))
3508 SetWindowText(hwnd, title);
3509 }
3510
3511 void set_icon(char *title)
3512 {
3513 sfree(icon_name);
3514 icon_name = smalloc(1 + strlen(title));
3515 strcpy(icon_name, title);
3516 if (!cfg.win_name_always && IsIconic(hwnd))
3517 SetWindowText(hwnd, title);
3518 }
3519
3520 void set_sbar(int total, int start, int page)
3521 {
3522 SCROLLINFO si;
3523
3524 if (!cfg.scrollbar)
3525 return;
3526
3527 si.cbSize = sizeof(si);
3528 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3529 si.nMin = 0;
3530 si.nMax = total - 1;
3531 si.nPage = page;
3532 si.nPos = start;
3533 if (hwnd)
3534 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3535 }
3536
3537 Context get_ctx(void)
3538 {
3539 HDC hdc;
3540 if (hwnd) {
3541 hdc = GetDC(hwnd);
3542 if (hdc && pal)
3543 SelectPalette(hdc, pal, FALSE);
3544 return hdc;
3545 } else
3546 return NULL;
3547 }
3548
3549 void free_ctx(Context ctx)
3550 {
3551 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3552 ReleaseDC(hwnd, ctx);
3553 }
3554
3555 static void real_palette_set(int n, int r, int g, int b)
3556 {
3557 if (pal) {
3558 logpal->palPalEntry[n].peRed = r;
3559 logpal->palPalEntry[n].peGreen = g;
3560 logpal->palPalEntry[n].peBlue = b;
3561 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3562 colours[n] = PALETTERGB(r, g, b);
3563 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3564 } else
3565 colours[n] = RGB(r, g, b);
3566 }
3567
3568 void palette_set(int n, int r, int g, int b)
3569 {
3570 static const int first[21] = {
3571 0, 2, 4, 6, 8, 10, 12, 14,
3572 1, 3, 5, 7, 9, 11, 13, 15,
3573 16, 17, 18, 20, 22
3574 };
3575 real_palette_set(first[n], r, g, b);
3576 if (first[n] >= 18)
3577 real_palette_set(first[n] + 1, r, g, b);
3578 if (pal) {
3579 HDC hdc = get_ctx();
3580 UnrealizeObject(pal);
3581 RealizePalette(hdc);
3582 free_ctx(hdc);
3583 }
3584 }
3585
3586 void palette_reset(void)
3587 {
3588 int i;
3589
3590 for (i = 0; i < NCOLOURS; i++) {
3591 if (pal) {
3592 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3593 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3594 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3595 logpal->palPalEntry[i].peFlags = 0;
3596 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3597 defpal[i].rgbtGreen,
3598 defpal[i].rgbtBlue);
3599 } else
3600 colours[i] = RGB(defpal[i].rgbtRed,
3601 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3602 }
3603
3604 if (pal) {
3605 HDC hdc;
3606 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3607 hdc = get_ctx();
3608 RealizePalette(hdc);
3609 free_ctx(hdc);
3610 }
3611 }
3612
3613 void write_aclip(char *data, int len, int must_deselect)
3614 {
3615 HGLOBAL clipdata;
3616 void *lock;
3617
3618 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3619 if (!clipdata)
3620 return;
3621 lock = GlobalLock(clipdata);
3622 if (!lock)
3623 return;
3624 memcpy(lock, data, len);
3625 ((unsigned char *) lock)[len] = 0;
3626 GlobalUnlock(clipdata);
3627
3628 if (!must_deselect)
3629 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3630
3631 if (OpenClipboard(hwnd)) {
3632 EmptyClipboard();
3633 SetClipboardData(CF_TEXT, clipdata);
3634 CloseClipboard();
3635 } else
3636 GlobalFree(clipdata);
3637
3638 if (!must_deselect)
3639 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3640 }
3641
3642 /*
3643 * Note: unlike write_aclip() this will not append a nul.
3644 */
3645 void write_clip(wchar_t * data, int len, int must_deselect)
3646 {
3647 HGLOBAL clipdata, clipdata2, clipdata3;
3648 int len2;
3649 void *lock, *lock2, *lock3;
3650
3651 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3652
3653 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3654 len * sizeof(wchar_t));
3655 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3656
3657 if (!clipdata || !clipdata2 || !clipdata3) {
3658 if (clipdata)
3659 GlobalFree(clipdata);
3660 if (clipdata2)
3661 GlobalFree(clipdata2);
3662 if (clipdata3)
3663 GlobalFree(clipdata3);
3664 return;
3665 }
3666 if (!(lock = GlobalLock(clipdata)))
3667 return;
3668 if (!(lock2 = GlobalLock(clipdata2)))
3669 return;
3670
3671 memcpy(lock, data, len * sizeof(wchar_t));
3672 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3673
3674 if (cfg.rtf_paste) {
3675 wchar_t unitab[256];
3676 char *rtf = NULL;
3677 unsigned char *tdata = (unsigned char *)lock2;
3678 wchar_t *udata = (wchar_t *)lock;
3679 int rtflen = 0, uindex = 0, tindex = 0;
3680 int rtfsize = 0;
3681 int multilen, blen, alen, totallen, i;
3682 char before[16], after[4];
3683
3684 get_unitab(CP_ACP, unitab, 0);
3685
3686 rtfsize = 100 + strlen(cfg.font);
3687 rtf = smalloc(rtfsize);
3688 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3689 GetACP(), cfg.font);
3690 rtflen = strlen(rtf);
3691
3692 /*
3693 * We want to construct a piece of RTF that specifies the
3694 * same Unicode text. To do this we will read back in
3695 * parallel from the Unicode data in `udata' and the
3696 * non-Unicode data in `tdata'. For each character in
3697 * `tdata' which becomes the right thing in `udata' when
3698 * looked up in `unitab', we just copy straight over from
3699 * tdata. For each one that doesn't, we must WCToMB it
3700 * individually and produce a \u escape sequence.
3701 *
3702 * It would probably be more robust to just bite the bullet
3703 * and WCToMB each individual Unicode character one by one,
3704 * then MBToWC each one back to see if it was an accurate
3705 * translation; but that strikes me as a horrifying number
3706 * of Windows API calls so I want to see if this faster way
3707 * will work. If it screws up badly we can always revert to
3708 * the simple and slow way.
3709 */
3710 while (tindex < len2 && uindex < len &&
3711 tdata[tindex] && udata[uindex]) {
3712 if (tindex + 1 < len2 &&
3713 tdata[tindex] == '\r' &&
3714 tdata[tindex+1] == '\n') {
3715 tindex++;
3716 uindex++;
3717 }
3718 if (unitab[tdata[tindex]] == udata[uindex]) {
3719 multilen = 1;
3720 before[0] = '\0';
3721 after[0] = '\0';
3722 blen = alen = 0;
3723 } else {
3724 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3725 NULL, 0, NULL, NULL);
3726 if (multilen != 1) {
3727 blen = sprintf(before, "{\\u%d", udata[uindex]);
3728 alen = 1; strcpy(after, "}");
3729 } else {
3730 blen = sprintf(before, "\\u%d", udata[uindex]);
3731 alen = 0; after[0] = '\0';
3732 }
3733 }
3734 assert(tindex + multilen <= len2);
3735 totallen = blen + alen;
3736 for (i = 0; i < multilen; i++) {
3737 if (tdata[tindex+i] == '\\' ||
3738 tdata[tindex+i] == '{' ||
3739 tdata[tindex+i] == '}')
3740 totallen += 2;
3741 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3742 totallen += 6; /* \par\r\n */
3743 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3744 totallen += 4;
3745 else
3746 totallen++;
3747 }
3748
3749 if (rtfsize < rtflen + totallen + 3) {
3750 rtfsize = rtflen + totallen + 512;
3751 rtf = srealloc(rtf, rtfsize);
3752 }
3753
3754 strcpy(rtf + rtflen, before); rtflen += blen;
3755 for (i = 0; i < multilen; i++) {
3756 if (tdata[tindex+i] == '\\' ||
3757 tdata[tindex+i] == '{' ||
3758 tdata[tindex+i] == '}') {
3759 rtf[rtflen++] = '\\';
3760 rtf[rtflen++] = tdata[tindex+i];
3761 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3762 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3763 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3764 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3765 } else {
3766 rtf[rtflen++] = tdata[tindex+i];
3767 }
3768 }
3769 strcpy(rtf + rtflen, after); rtflen += alen;
3770
3771 tindex += multilen;
3772 uindex++;
3773 }
3774
3775 strcpy(rtf + rtflen, "}");
3776 rtflen += 2;
3777
3778 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3779 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3780 strcpy(lock3, rtf);
3781 GlobalUnlock(clipdata3);
3782 }
3783 sfree(rtf);
3784 } else
3785 clipdata3 = NULL;
3786
3787 GlobalUnlock(clipdata);
3788 GlobalUnlock(clipdata2);
3789
3790 if (!must_deselect)
3791 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3792
3793 if (OpenClipboard(hwnd)) {
3794 EmptyClipboard();
3795 SetClipboardData(CF_UNICODETEXT, clipdata);
3796 SetClipboardData(CF_TEXT, clipdata2);
3797 if (clipdata3)
3798 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3799 CloseClipboard();
3800 } else {
3801 GlobalFree(clipdata);
3802 GlobalFree(clipdata2);
3803 }
3804
3805 if (!must_deselect)
3806 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3807 }
3808
3809 void get_clip(wchar_t ** p, int *len)
3810 {
3811 static HGLOBAL clipdata = NULL;
3812 static wchar_t *converted = 0;
3813 wchar_t *p2;
3814
3815 if (converted) {
3816 sfree(converted);
3817 converted = 0;
3818 }
3819 if (!p) {
3820 if (clipdata)
3821 GlobalUnlock(clipdata);
3822 clipdata = NULL;
3823 return;
3824 } else if (OpenClipboard(NULL)) {
3825 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3826 CloseClipboard();
3827 *p = GlobalLock(clipdata);
3828 if (*p) {
3829 for (p2 = *p; *p2; p2++);
3830 *len = p2 - *p;
3831 return;
3832 }
3833 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3834 char *s;
3835 int i;
3836 CloseClipboard();
3837 s = GlobalLock(clipdata);
3838 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3839 *p = converted = smalloc(i * sizeof(wchar_t));
3840 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3841 *len = i - 1;
3842 return;
3843 } else
3844 CloseClipboard();
3845 }
3846
3847 *p = NULL;
3848 *len = 0;
3849 }
3850
3851 #if 0
3852 /*
3853 * Move `lines' lines from position `from' to position `to' in the
3854 * window.
3855 */
3856 void optimised_move(int to, int from, int lines)
3857 {
3858 RECT r;
3859 int min, max;
3860
3861 min = (to < from ? to : from);
3862 max = to + from - min;
3863
3864 r.left = offset_width;
3865 r.right = offset_width + cols * font_width;
3866 r.top = offset_height + min * font_height;
3867 r.bottom = offset_height + (max + lines) * font_height;
3868 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3869 }
3870 #endif
3871
3872 /*
3873 * Print a message box and perform a fatal exit.
3874 */
3875 void fatalbox(char *fmt, ...)
3876 {
3877 va_list ap;
3878 char stuff[200];
3879
3880 va_start(ap, fmt);
3881 vsprintf(stuff, fmt, ap);
3882 va_end(ap);
3883 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3884 exit(1);
3885 }
3886
3887 /*
3888 * Manage window caption / taskbar flashing, if enabled.
3889 * 0 = stop, 1 = maintain, 2 = start
3890 */
3891 static void flash_window(int mode)
3892 {
3893 static long last_flash = 0;
3894 static int flashing = 0;
3895 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3896 /* stop */
3897 if (flashing) {
3898 FlashWindow(hwnd, FALSE);
3899 flashing = 0;
3900 }
3901
3902 } else if (mode == 2) {
3903 /* start */
3904 if (!flashing) {
3905 last_flash = GetTickCount();
3906 flashing = 1;
3907 FlashWindow(hwnd, TRUE);
3908 }
3909
3910 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3911 /* maintain */
3912 if (flashing) {
3913 long now = GetTickCount();
3914 long fdiff = now - last_flash;
3915 if (fdiff < 0 || fdiff > 450) {
3916 last_flash = now;
3917 FlashWindow(hwnd, TRUE); /* toggle */
3918 }
3919 }
3920 }
3921 }
3922
3923 /*
3924 * Beep.
3925 */
3926 void beep(int mode)
3927 {
3928 if (mode == BELL_DEFAULT) {
3929 /*
3930 * For MessageBeep style bells, we want to be careful of
3931 * timing, because they don't have the nice property of
3932 * PlaySound bells that each one cancels the previous
3933 * active one. So we limit the rate to one per 50ms or so.
3934 */
3935 static long lastbeep = 0;
3936 long beepdiff;
3937
3938 beepdiff = GetTickCount() - lastbeep;
3939 if (beepdiff >= 0 && beepdiff < 50)
3940 return;
3941 MessageBeep(MB_OK);
3942 /*
3943 * The above MessageBeep call takes time, so we record the
3944 * time _after_ it finishes rather than before it starts.
3945 */
3946 lastbeep = GetTickCount();
3947 } else if (mode == BELL_WAVEFILE) {
3948 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3949 char buf[sizeof(cfg.bell_wavefile) + 80];
3950 sprintf(buf, "Unable to play sound file\n%s\n"
3951 "Using default sound instead", cfg.bell_wavefile);
3952 MessageBox(hwnd, buf, "PuTTY Sound Error",
3953 MB_OK | MB_ICONEXCLAMATION);
3954 cfg.beep = BELL_DEFAULT;
3955 }
3956 }
3957 /* Otherwise, either visual bell or disabled; do nothing here */
3958 if (!has_focus) {
3959 flash_window(2); /* start */
3960 }
3961 }
3962
3963 /*
3964 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3965 * implementation.
3966 * Revised by <wez@thebrainroom.com>
3967 */
3968 static void flip_full_screen(void)
3969 {
3970 WINDOWPLACEMENT wp;
3971 LONG style;
3972
3973 wp.length = sizeof(wp);
3974 GetWindowPlacement(hwnd, &wp);
3975
3976 full_screen = !full_screen;
3977
3978 if (full_screen) {
3979 if (wp.showCmd == SW_SHOWMAXIMIZED) {
3980 /* Ooops it was already 'zoomed' we have to unzoom it before
3981 * everything will work right.
3982 */
3983 wp.showCmd = SW_SHOWNORMAL;
3984 SetWindowPlacement(hwnd, &wp);
3985 }
3986
3987 style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3988 style &= ~WS_VSCROLL;
3989 if (cfg.scrollbar_in_fullscreen)
3990 style |= WS_VSCROLL;
3991 SetWindowLong(hwnd, GWL_STYLE, style);
3992
3993 /* This seems to be needed otherwize explorer doesn't notice
3994 * we want to go fullscreen and it's bar is still visible
3995 */
3996 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
3997 SWP_NOACTIVATE | SWP_NOCOPYBITS |
3998 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
3999 SWP_FRAMECHANGED);
4000
4001 wp.showCmd = SW_SHOWMAXIMIZED;
4002 SetWindowPlacement(hwnd, &wp);
4003 } else {
4004 style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4005 style &= ~WS_VSCROLL;
4006 if (cfg.scrollbar)
4007 style |= WS_VSCROLL;
4008 SetWindowLong(hwnd, GWL_STYLE, style);
4009
4010 /* Don't need to do a SetWindowPos as the resize will force a
4011 * full redraw.
4012 */
4013 wp.showCmd = SW_SHOWNORMAL;
4014 SetWindowPlacement(hwnd, &wp);
4015 }
4016
4017 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4018 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4019 }