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