]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
3fd445f8e06b84e15c87d0b87f1f1b7234264985
[PuTTY.git] / 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 extra_width, extra_height;
75
76 static int pending_netevent = 0;
77 static WPARAM pend_netevent_wParam = 0;
78 static LPARAM pend_netevent_lParam = 0;
79 static void enact_pending_netevent(void);
80 static void flash_window(int mode);
81
82 static time_t last_movement = 0;
83
84 #define FONT_NORMAL 0
85 #define FONT_BOLD 1
86 #define FONT_UNDERLINE 2
87 #define FONT_BOLDUND 3
88 #define FONT_WIDE       0x04
89 #define FONT_HIGH       0x08
90 #define FONT_NARROW     0x10
91 #define FONT_OEM        0x20
92 #define FONT_OEMBOLD    0x21
93 #define FONT_OEMUND     0x22
94 #define FONT_OEMBOLDUND 0x23
95 #define FONT_MSGOTHIC   0x40
96 #define FONT_MINGLIU    0x60
97 #define FONT_GULIMCHE   0x80
98 #define FONT_MAXNO      0x8F
99 #define FONT_SHIFT      5
100 static HFONT fonts[FONT_MAXNO];
101 static int fontflag[FONT_MAXNO];
102 static enum {
103     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
104 } bold_mode;
105 static enum {
106     UND_LINE, UND_FONT
107 } und_mode;
108 static int descent;
109
110 #define NCOLOURS 24
111 static COLORREF colours[NCOLOURS];
112 static HPALETTE pal;
113 static LPLOGPALETTE logpal;
114 static RGBTRIPLE defpal[NCOLOURS];
115
116 static HWND hwnd;
117
118 static HBITMAP caretbm;
119
120 static int dbltime, lasttime, lastact;
121 static Mouse_Button lastbtn;
122
123 /* this allows xterm-style mouse handling. */
124 static int send_raw_mouse = 0;
125 static int wheel_accumulator = 0;
126
127 static char *window_name, *icon_name;
128
129 static int compose_state = 0;
130
131 static OSVERSIONINFOEX osVersion;
132
133 /* Dummy routine, only required in plink. */
134 void ldisc_update(int echo, int edit)
135 {
136 }
137
138 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
139 {
140     static char appname[] = "PuTTY";
141     WORD winsock_ver;
142     WSADATA wsadata;
143     WNDCLASS wndclass;
144     MSG msg;
145     int guess_width, guess_height;
146
147     hinst = inst;
148     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
149
150     winsock_ver = MAKEWORD(1, 1);
151     if (WSAStartup(winsock_ver, &wsadata)) {
152         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
153                    MB_OK | MB_ICONEXCLAMATION);
154         return 1;
155     }
156     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
157         MessageBox(NULL, "WinSock version is incompatible with 1.1",
158                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
159         WSACleanup();
160         return 1;
161     }
162     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
163     sk_init();
164
165     InitCommonControls();
166
167     /* Ensure a Maximize setting in Explorer doesn't maximise the
168      * config box. */
169     defuse_showwindow();
170
171     {
172         ZeroMemory(&osVersion, sizeof(osVersion));
173         osVersion.dwOSVersionInfoSize = sizeof(osVersion);
174
175         if(!GetVersionEx ((OSVERSIONINFO *) &osVersion)) {
176         // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
177
178         osVersion.dwOSVersionInfoSize = sizeof (osVersion);
179         if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion))
180             return FALSE;
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_READ | FD_WRITE | FD_OOB | FD_CLOSE;
713     } else {
714         msg = events = 0;
715     }
716     if (!hwnd)
717         return "do_select(): internal error (hwnd==NULL)";
718     if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
719         switch (WSAGetLastError()) {
720           case WSAENETDOWN:
721             return "Network is down";
722           default:
723             return "WSAAsyncSelect(): unknown error";
724         }
725     }
726     return NULL;
727 }
728
729 /*
730  * set or clear the "raw mouse message" mode
731  */
732 void set_raw_mouse_mode(int activate)
733 {
734     send_raw_mouse = activate;
735     SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
736 }
737
738 /*
739  * Print a message box and close the connection.
740  */
741 void connection_fatal(char *fmt, ...)
742 {
743     va_list ap;
744     char stuff[200];
745
746     va_start(ap, fmt);
747     vsprintf(stuff, fmt, ap);
748     va_end(ap);
749     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
750     if (cfg.close_on_exit == COE_ALWAYS)
751         PostQuitMessage(1);
752     else {
753         session_closed = TRUE;
754         SetWindowText(hwnd, "PuTTY (inactive)");
755     }
756 }
757
758 /*
759  * Actually do the job requested by a WM_NETEVENT
760  */
761 static void enact_pending_netevent(void)
762 {
763     static int reentering = 0;
764     extern int select_result(WPARAM, LPARAM);
765     int ret;
766
767     if (reentering)
768         return;                        /* don't unpend the pending */
769
770     pending_netevent = FALSE;
771
772     reentering = 1;
773     ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
774     reentering = 0;
775
776     if (ret == 0 && !session_closed) {
777         /* Abnormal exits will already have set session_closed and taken
778          * appropriate action. */
779         if (cfg.close_on_exit == COE_ALWAYS ||
780             cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
781         else {
782             session_closed = TRUE;
783             SetWindowText(hwnd, "PuTTY (inactive)");
784             MessageBox(hwnd, "Connection closed by remote host",
785                        "PuTTY", MB_OK | MB_ICONINFORMATION);
786         }
787     }
788 }
789
790 /*
791  * Copy the colour palette from the configuration data into defpal.
792  * This is non-trivial because the colour indices are different.
793  */
794 static void cfgtopalette(void)
795 {
796     int i;
797     static const int ww[] = {
798         6, 7, 8, 9, 10, 11, 12, 13,
799         14, 15, 16, 17, 18, 19, 20, 21,
800         0, 1, 2, 3, 4, 4, 5, 5
801     };
802
803     for (i = 0; i < 24; i++) {
804         int w = ww[i];
805         defpal[i].rgbtRed = cfg.colours[w][0];
806         defpal[i].rgbtGreen = cfg.colours[w][1];
807         defpal[i].rgbtBlue = cfg.colours[w][2];
808     }
809 }
810
811 /*
812  * Set up the colour palette.
813  */
814 static void init_palette(void)
815 {
816     int i;
817     HDC hdc = GetDC(hwnd);
818     if (hdc) {
819         if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
820             logpal = smalloc(sizeof(*logpal)
821                              - sizeof(logpal->palPalEntry)
822                              + NCOLOURS * sizeof(PALETTEENTRY));
823             logpal->palVersion = 0x300;
824             logpal->palNumEntries = NCOLOURS;
825             for (i = 0; i < NCOLOURS; i++) {
826                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
827                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
828                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
829                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
830             }
831             pal = CreatePalette(logpal);
832             if (pal) {
833                 SelectPalette(hdc, pal, FALSE);
834                 RealizePalette(hdc);
835                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
836             }
837         }
838         ReleaseDC(hwnd, hdc);
839     }
840     if (pal)
841         for (i = 0; i < NCOLOURS; i++)
842             colours[i] = PALETTERGB(defpal[i].rgbtRed,
843                                     defpal[i].rgbtGreen,
844                                     defpal[i].rgbtBlue);
845     else
846         for (i = 0; i < NCOLOURS; i++)
847             colours[i] = RGB(defpal[i].rgbtRed,
848                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
849 }
850
851 /*
852  * Initialise all the fonts we will need initially. There may be as many as
853  * three or as few as one.  The other (poentially) twentyone fonts are done
854  * if/when they are needed.
855  *
856  * We also:
857  *
858  * - check the font width and height, correcting our guesses if
859  *   necessary.
860  *
861  * - verify that the bold font is the same width as the ordinary
862  *   one, and engage shadow bolding if not.
863  * 
864  * - verify that the underlined font is the same width as the
865  *   ordinary one (manual underlining by means of line drawing can
866  *   be done in a pinch).
867  */
868 static void init_fonts(int pick_width)
869 {
870     TEXTMETRIC tm;
871     CPINFO cpinfo;
872     int fontsize[3];
873     int i;
874     HDC hdc;
875     int fw_dontcare, fw_bold;
876
877     for (i = 0; i < FONT_MAXNO; i++)
878         fonts[i] = NULL;
879
880     if (cfg.fontisbold) {
881         fw_dontcare = FW_BOLD;
882         fw_bold = FW_HEAVY;
883     } else {
884         fw_dontcare = FW_DONTCARE;
885         fw_bold = FW_BOLD;
886     }
887
888     hdc = GetDC(hwnd);
889
890     font_height = cfg.fontheight;
891     if (font_height > 0) {
892         font_height =
893             -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
894     }
895     font_width = pick_width;
896
897 #define f(i,c,w,u) \
898     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
899                            c, OUT_DEFAULT_PRECIS, \
900                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
901                            FIXED_PITCH | FF_DONTCARE, cfg.font)
902
903     f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
904
905     SelectObject(hdc, fonts[FONT_NORMAL]);
906     GetTextMetrics(hdc, &tm);
907     font_height = tm.tmHeight;
908     font_width = tm.tmAveCharWidth;
909
910     {
911         CHARSETINFO info;
912         DWORD cset = tm.tmCharSet;
913         memset(&info, 0xFF, sizeof(info));
914
915         /* !!! Yes the next line is right */
916         if (cset == OEM_CHARSET)
917             font_codepage = GetOEMCP();
918         else
919             if (TranslateCharsetInfo
920                 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
921                 info.ciACP;
922         else
923             font_codepage = -1;
924
925         GetCPInfo(font_codepage, &cpinfo);
926         dbcs_screenfont = (cpinfo.MaxCharSize > 1);
927     }
928
929     f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
930
931     /*
932      * Some fonts, e.g. 9-pt Courier, draw their underlines
933      * outside their character cell. We successfully prevent
934      * screen corruption by clipping the text output, but then
935      * we lose the underline completely. Here we try to work
936      * out whether this is such a font, and if it is, we set a
937      * flag that causes underlines to be drawn by hand.
938      *
939      * Having tried other more sophisticated approaches (such
940      * as examining the TEXTMETRIC structure or requesting the
941      * height of a string), I think we'll do this the brute
942      * force way: we create a small bitmap, draw an underlined
943      * space on it, and test to see whether any pixels are
944      * foreground-coloured. (Since we expect the underline to
945      * go all the way across the character cell, we only search
946      * down a single column of the bitmap, half way across.)
947      */
948     {
949         HDC und_dc;
950         HBITMAP und_bm, und_oldbm;
951         int i, gotit;
952         COLORREF c;
953
954         und_dc = CreateCompatibleDC(hdc);
955         und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
956         und_oldbm = SelectObject(und_dc, und_bm);
957         SelectObject(und_dc, fonts[FONT_UNDERLINE]);
958         SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
959         SetTextColor(und_dc, RGB(255, 255, 255));
960         SetBkColor(und_dc, RGB(0, 0, 0));
961         SetBkMode(und_dc, OPAQUE);
962         ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
963         gotit = FALSE;
964         for (i = 0; i < font_height; i++) {
965             c = GetPixel(und_dc, font_width / 2, i);
966             if (c != RGB(0, 0, 0))
967                 gotit = TRUE;
968         }
969         SelectObject(und_dc, und_oldbm);
970         DeleteObject(und_bm);
971         DeleteDC(und_dc);
972         if (!gotit) {
973             und_mode = UND_LINE;
974             DeleteObject(fonts[FONT_UNDERLINE]);
975             fonts[FONT_UNDERLINE] = 0;
976         }
977     }
978
979     if (bold_mode == BOLD_FONT) {
980         f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
981     }
982 #undef f
983
984     descent = tm.tmAscent + 1;
985     if (descent >= font_height)
986         descent = font_height - 1;
987
988     for (i = 0; i < 3; i++) {
989         if (fonts[i]) {
990             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
991                 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
992             else
993                 fontsize[i] = -i;
994         } else
995             fontsize[i] = -i;
996     }
997
998     ReleaseDC(hwnd, hdc);
999
1000     if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1001         und_mode = UND_LINE;
1002         DeleteObject(fonts[FONT_UNDERLINE]);
1003         fonts[FONT_UNDERLINE] = 0;
1004     }
1005
1006     if (bold_mode == BOLD_FONT &&
1007         fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1008         bold_mode = BOLD_SHADOW;
1009         DeleteObject(fonts[FONT_BOLD]);
1010         fonts[FONT_BOLD] = 0;
1011     }
1012     fontflag[0] = fontflag[1] = fontflag[2] = 1;
1013
1014     init_ucs_tables();
1015 }
1016
1017 static void another_font(int fontno)
1018 {
1019     int basefont;
1020     int fw_dontcare, fw_bold;
1021     int c, u, w, x;
1022     char *s;
1023
1024     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1025         return;
1026
1027     basefont = (fontno & ~(FONT_BOLDUND));
1028     if (basefont != fontno && !fontflag[basefont])
1029         another_font(basefont);
1030
1031     if (cfg.fontisbold) {
1032         fw_dontcare = FW_BOLD;
1033         fw_bold = FW_HEAVY;
1034     } else {
1035         fw_dontcare = FW_DONTCARE;
1036         fw_bold = FW_BOLD;
1037     }
1038
1039     c = cfg.fontcharset;
1040     w = fw_dontcare;
1041     u = FALSE;
1042     s = cfg.font;
1043     x = font_width;
1044
1045     if (fontno & FONT_WIDE)
1046         x *= 2;
1047     if (fontno & FONT_NARROW)
1048         x /= 2;
1049     if (fontno & FONT_OEM)
1050         c = OEM_CHARSET;
1051     if (fontno & FONT_BOLD)
1052         w = fw_bold;
1053     if (fontno & FONT_UNDERLINE)
1054         u = TRUE;
1055
1056     fonts[fontno] =
1057         CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1058                    FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1059                    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1060                    FIXED_PITCH | FF_DONTCARE, s);
1061
1062     fontflag[fontno] = 1;
1063 }
1064
1065 static void deinit_fonts(void)
1066 {
1067     int i;
1068     for (i = 0; i < FONT_MAXNO; i++) {
1069         if (fonts[i])
1070             DeleteObject(fonts[i]);
1071         fonts[i] = 0;
1072         fontflag[i] = 0;
1073     }
1074 }
1075
1076 void request_resize(int w, int h, int refont)
1077 {
1078     int width, height;
1079
1080     /* If the window is maximized supress resizing attempts */
1081     if (IsZoomed(hwnd))
1082         return;
1083
1084     if (refont && w != cols && (cols == 80 || cols == 132)) {
1085         /* If font width too big for screen should we shrink the font more ? */
1086         if (w == 132)
1087             font_width = ((font_width * cols + w / 2) / w);
1088         else
1089             font_width = 0;
1090         deinit_fonts();
1091         bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1092         und_mode = UND_FONT;
1093         init_fonts(font_width);
1094     } else {
1095         static int first_time = 1;
1096         static RECT ss;
1097
1098         switch (first_time) {
1099           case 1:
1100             /* Get the size of the screen */
1101             if (GetClientRect(GetDesktopWindow(), &ss))
1102                 /* first_time = 0 */ ;
1103             else {
1104                 first_time = 2;
1105                 break;
1106             }
1107           case 0:
1108             /* Make sure the values are sane */
1109             width = (ss.right - ss.left - extra_width) / font_width;
1110             height = (ss.bottom - ss.top - extra_height) / font_height;
1111
1112             if (w > width)
1113                 w = width;
1114             if (h > height)
1115                 h = height;
1116             if (w < 15)
1117                 w = 15;
1118             if (h < 1)
1119                 w = 1;
1120         }
1121     }
1122
1123     width = extra_width + font_width * w;
1124     height = extra_height + font_height * h;
1125
1126     SetWindowPos(hwnd, NULL, 0, 0, width, height,
1127                  SWP_NOACTIVATE | SWP_NOCOPYBITS |
1128                  SWP_NOMOVE | SWP_NOZORDER);
1129 }
1130
1131 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1132 {
1133     int thistime = GetMessageTime();
1134
1135     if (send_raw_mouse) {
1136         term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1137         return;
1138     }
1139
1140     if (lastbtn == b && thistime - lasttime < dbltime) {
1141         lastact = (lastact == MA_CLICK ? MA_2CLK :
1142                    lastact == MA_2CLK ? MA_3CLK :
1143                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1144     } else {
1145         lastbtn = b;
1146         lastact = MA_CLICK;
1147     }
1148     if (lastact != MA_NOTHING)
1149         term_mouse(b, lastact, x, y, shift, ctrl);
1150     lasttime = thistime;
1151 }
1152
1153 /*
1154  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1155  * into a cooked one (SELECT, EXTEND, PASTE).
1156  */
1157 Mouse_Button translate_button(Mouse_Button button)
1158 {
1159     if (button == MBT_LEFT)
1160         return MBT_SELECT;
1161     if (button == MBT_MIDDLE)
1162         return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1163     if (button == MBT_RIGHT)
1164         return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1165     return 0;                          /* shouldn't happen */
1166 }
1167
1168 static void show_mouseptr(int show)
1169 {
1170     static int cursor_visible = 1;
1171     if (!cfg.hide_mouseptr)            /* override if this feature disabled */
1172         show = 1;
1173     if (cursor_visible && !show)
1174         ShowCursor(FALSE);
1175     else if (!cursor_visible && show)
1176         ShowCursor(TRUE);
1177     cursor_visible = show;
1178 }
1179
1180 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1181                                 WPARAM wParam, LPARAM lParam)
1182 {
1183     HDC hdc;
1184     static int ignore_size = FALSE;
1185     static int ignore_clip = FALSE;
1186     static int just_reconfigged = FALSE;
1187     static int resizing = FALSE;
1188     static int need_backend_resize = FALSE;
1189     static int defered_resize = FALSE;
1190
1191     switch (message) {
1192       case WM_TIMER:
1193         if (pending_netevent)
1194             enact_pending_netevent();
1195         if (inbuf_head)
1196             term_out();
1197         noise_regular();
1198         HideCaret(hwnd);
1199         term_update();
1200         ShowCaret(hwnd);
1201         if (cfg.ping_interval > 0) {
1202             time_t now;
1203             time(&now);
1204             if (now - last_movement > cfg.ping_interval) {
1205                 back->special(TS_PING);
1206                 last_movement = now;
1207             }
1208         }
1209         return 0;
1210       case WM_CREATE:
1211         break;
1212       case WM_CLOSE:
1213         show_mouseptr(1);
1214         if (!cfg.warn_on_close || session_closed ||
1215             MessageBox(hwnd,
1216                        "Are you sure you want to close this session?",
1217                        "PuTTY Exit Confirmation",
1218                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1219             DestroyWindow(hwnd);
1220         return 0;
1221       case WM_DESTROY:
1222         show_mouseptr(1);
1223         PostQuitMessage(0);
1224         return 0;
1225       case WM_SYSCOMMAND:
1226         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1227           case IDM_SHOWLOG:
1228             showeventlog(hwnd);
1229             break;
1230           case IDM_NEWSESS:
1231           case IDM_DUPSESS:
1232           case IDM_SAVEDSESS:
1233             {
1234                 char b[2048];
1235                 char c[30], *cl;
1236                 int freecl = FALSE;
1237                 STARTUPINFO si;
1238                 PROCESS_INFORMATION pi;
1239                 HANDLE filemap = NULL;
1240
1241                 if (wParam == IDM_DUPSESS) {
1242                     /*
1243                      * Allocate a file-mapping memory chunk for the
1244                      * config structure.
1245                      */
1246                     SECURITY_ATTRIBUTES sa;
1247                     Config *p;
1248
1249                     sa.nLength = sizeof(sa);
1250                     sa.lpSecurityDescriptor = NULL;
1251                     sa.bInheritHandle = TRUE;
1252                     filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1253                                                 &sa,
1254                                                 PAGE_READWRITE,
1255                                                 0, sizeof(Config), NULL);
1256                     if (filemap) {
1257                         p = (Config *) MapViewOfFile(filemap,
1258                                                      FILE_MAP_WRITE,
1259                                                      0, 0, sizeof(Config));
1260                         if (p) {
1261                             *p = cfg;  /* structure copy */
1262                             UnmapViewOfFile(p);
1263                         }
1264                     }
1265                     sprintf(c, "putty &%p", filemap);
1266                     cl = c;
1267                 } else if (wParam == IDM_SAVEDSESS) {
1268                     char *session =
1269                         sessions[(lParam - IDM_SAVED_MIN) / 16];
1270                     cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1271                     if (!cl)
1272                         cl = NULL;     /* not a very important failure mode */
1273                     else {
1274                         sprintf(cl, "putty @%s", session);
1275                         freecl = TRUE;
1276                     }
1277                 } else
1278                     cl = NULL;
1279
1280                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1281                 si.cb = sizeof(si);
1282                 si.lpReserved = NULL;
1283                 si.lpDesktop = NULL;
1284                 si.lpTitle = NULL;
1285                 si.dwFlags = 0;
1286                 si.cbReserved2 = 0;
1287                 si.lpReserved2 = NULL;
1288                 CreateProcess(b, cl, NULL, NULL, TRUE,
1289                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1290
1291                 if (filemap)
1292                     CloseHandle(filemap);
1293                 if (freecl)
1294                     sfree(cl);
1295             }
1296             break;
1297           case IDM_RECONF:
1298             {
1299                 int prev_alwaysontop = cfg.alwaysontop;
1300                 int prev_sunken_edge = cfg.sunken_edge;
1301                 char oldlogfile[FILENAME_MAX];
1302                 int oldlogtype;
1303                 int need_setwpos = FALSE;
1304                 int old_fwidth, old_fheight;
1305
1306                 strcpy(oldlogfile, cfg.logfilename);
1307                 oldlogtype = cfg.logtype;
1308                 old_fwidth = font_width;
1309                 old_fheight = font_height;
1310                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1311
1312                 if (!do_reconfig(hwnd))
1313                     break;
1314
1315                 if (strcmp(oldlogfile, cfg.logfilename) ||
1316                     oldlogtype != cfg.logtype) {
1317                     logfclose();       /* reset logging */
1318                     logfopen();
1319                 }
1320
1321                 just_reconfigged = TRUE;
1322                 deinit_fonts();
1323                 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1324                 und_mode = UND_FONT;
1325                 init_fonts(0);
1326                 sfree(logpal);
1327                 /*
1328                  * Flush the line discipline's edit buffer in the
1329                  * case where local editing has just been disabled.
1330                  */
1331                 ldisc_send(NULL, 0);
1332                 if (pal)
1333                     DeleteObject(pal);
1334                 logpal = NULL;
1335                 pal = NULL;
1336                 cfgtopalette();
1337                 init_palette();
1338
1339                 /* Enable or disable the scroll bar, etc */
1340                 {
1341                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1342                     LONG nexflag, exflag =
1343                         GetWindowLong(hwnd, GWL_EXSTYLE);
1344
1345                     nexflag = exflag;
1346                     if (cfg.alwaysontop != prev_alwaysontop) {
1347                         if (cfg.alwaysontop) {
1348                             nexflag |= WS_EX_TOPMOST;
1349                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1350                                          SWP_NOMOVE | SWP_NOSIZE);
1351                         } else {
1352                             nexflag &= ~(WS_EX_TOPMOST);
1353                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1354                                          SWP_NOMOVE | SWP_NOSIZE);
1355                         }
1356                     }
1357                     if (cfg.sunken_edge)
1358                         nexflag |= WS_EX_CLIENTEDGE;
1359                     else
1360                         nexflag &= ~(WS_EX_CLIENTEDGE);
1361
1362                     nflg = flag;
1363                     if (cfg.scrollbar)
1364                         nflg |= WS_VSCROLL;
1365                     else
1366                         nflg &= ~WS_VSCROLL;
1367                     if (cfg.locksize)
1368                         nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1369                     else
1370                         nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1371
1372                     if (nflg != flag || nexflag != exflag) {
1373                         RECT cr, wr;
1374
1375                         if (nflg != flag)
1376                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1377                         if (nexflag != exflag)
1378                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1379
1380                         SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1381
1382                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1383                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1384                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1385                                      | SWP_FRAMECHANGED);
1386
1387                         GetWindowRect(hwnd, &wr);
1388                         GetClientRect(hwnd, &cr);
1389                         extra_width =
1390                             wr.right - wr.left - cr.right + cr.left;
1391                         extra_height =
1392                             wr.bottom - wr.top - cr.bottom + cr.top;
1393                         need_setwpos = TRUE;
1394                     }
1395                 }
1396
1397                 if (cfg.height != rows ||
1398                     cfg.width != cols ||
1399                     old_fwidth != font_width ||
1400                     old_fheight != font_height ||
1401                     cfg.savelines != savelines ||
1402                     cfg.sunken_edge != prev_sunken_edge)
1403                         need_setwpos = TRUE;
1404
1405                 if (IsZoomed(hwnd)) {
1406                     int w, h;
1407                     RECT cr;
1408                     if (need_setwpos)
1409                         defered_resize = TRUE;
1410
1411                     GetClientRect(hwnd, &cr);
1412                     w = cr.right - cr.left;
1413                     h = cr.bottom - cr.top;
1414                     w = w / font_width;
1415                     if (w < 1)
1416                         w = 1;
1417                     h = h / font_height;
1418                     if (h < 1)
1419                         h = 1;
1420
1421                     term_size(h, w, cfg.savelines);
1422                     InvalidateRect(hwnd, NULL, TRUE);
1423                     back->size();
1424                 } else {
1425                     term_size(cfg.height, cfg.width, cfg.savelines);
1426                     InvalidateRect(hwnd, NULL, TRUE);
1427                     if (need_setwpos) {
1428                         SetWindowPos(hwnd, NULL, 0, 0,
1429                                      extra_width + font_width * cfg.width,
1430                                      extra_height +
1431                                      font_height * cfg.height,
1432                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1433                                      SWP_NOMOVE | SWP_NOZORDER);
1434                     }
1435                 }
1436                 /* Oops */
1437                 if (cfg.locksize && IsZoomed(hwnd))
1438                     force_normal(hwnd);
1439                 set_title(cfg.wintitle);
1440                 if (IsIconic(hwnd)) {
1441                     SetWindowText(hwnd,
1442                                   cfg.win_name_always ? window_name :
1443                                   icon_name);
1444                 }
1445             }
1446             break;
1447           case IDM_COPYALL:
1448             term_copyall();
1449             break;
1450           case IDM_CLRSB:
1451             term_clrsb();
1452             break;
1453           case IDM_RESET:
1454             term_pwron();
1455             break;
1456           case IDM_TEL_AYT:
1457             back->special(TS_AYT);
1458             break;
1459           case IDM_TEL_BRK:
1460             back->special(TS_BRK);
1461             break;
1462           case IDM_TEL_SYNCH:
1463             back->special(TS_SYNCH);
1464             break;
1465           case IDM_TEL_EC:
1466             back->special(TS_EC);
1467             break;
1468           case IDM_TEL_EL:
1469             back->special(TS_EL);
1470             break;
1471           case IDM_TEL_GA:
1472             back->special(TS_GA);
1473             break;
1474           case IDM_TEL_NOP:
1475             back->special(TS_NOP);
1476             break;
1477           case IDM_TEL_ABORT:
1478             back->special(TS_ABORT);
1479             break;
1480           case IDM_TEL_AO:
1481             back->special(TS_AO);
1482             break;
1483           case IDM_TEL_IP:
1484             back->special(TS_IP);
1485             break;
1486           case IDM_TEL_SUSP:
1487             back->special(TS_SUSP);
1488             break;
1489           case IDM_TEL_EOR:
1490             back->special(TS_EOR);
1491             break;
1492           case IDM_TEL_EOF:
1493             back->special(TS_EOF);
1494             break;
1495           case IDM_ABOUT:
1496             showabout(hwnd);
1497             break;
1498           default:
1499             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1500                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1501             }
1502         }
1503         break;
1504
1505 #define X_POS(l) ((int)(short)LOWORD(l))
1506 #define Y_POS(l) ((int)(short)HIWORD(l))
1507
1508 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1509 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1510 #define WHEEL_DELTA 120
1511       case WM_MOUSEWHEEL:
1512         {
1513             wheel_accumulator += (short) HIWORD(wParam);
1514             wParam = LOWORD(wParam);
1515
1516             /* process events when the threshold is reached */
1517             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1518                 int b;
1519
1520                 /* reduce amount for next time */
1521                 if (wheel_accumulator > 0) {
1522                     b = MBT_WHEEL_UP;
1523                     wheel_accumulator -= WHEEL_DELTA;
1524                 } else if (wheel_accumulator < 0) {
1525                     b = MBT_WHEEL_DOWN;
1526                     wheel_accumulator += WHEEL_DELTA;
1527                 } else
1528                     break;
1529
1530                 if (send_raw_mouse) {
1531                     /* send a mouse-down followed by a mouse up */
1532                     term_mouse(b,
1533                                MA_CLICK,
1534                                TO_CHR_X(X_POS(lParam)),
1535                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1536                                wParam & MK_CONTROL);
1537                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1538                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1539                                wParam & MK_CONTROL);
1540                 } else {
1541                     /* trigger a scroll */
1542                     term_scroll(0,
1543                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1544                 }
1545             }
1546             return 0;
1547         }
1548       case WM_LBUTTONDOWN:
1549       case WM_MBUTTONDOWN:
1550       case WM_RBUTTONDOWN:
1551       case WM_LBUTTONUP:
1552       case WM_MBUTTONUP:
1553       case WM_RBUTTONUP:
1554         {
1555             int button, press;
1556             switch (message) {
1557               case WM_LBUTTONDOWN:
1558                 button = MBT_LEFT;
1559                 press = 1;
1560                 break;
1561               case WM_MBUTTONDOWN:
1562                 button = MBT_MIDDLE;
1563                 press = 1;
1564                 break;
1565               case WM_RBUTTONDOWN:
1566                 button = MBT_RIGHT;
1567                 press = 1;
1568                 break;
1569               case WM_LBUTTONUP:
1570                 button = MBT_LEFT;
1571                 press = 0;
1572                 break;
1573               case WM_MBUTTONUP:
1574                 button = MBT_MIDDLE;
1575                 press = 0;
1576                 break;
1577               case WM_RBUTTONUP:
1578                 button = MBT_RIGHT;
1579                 press = 0;
1580                 break;
1581               default:
1582                 button = press = 0;    /* shouldn't happen */
1583             }
1584             show_mouseptr(1);
1585             if (press) {
1586                 click(button,
1587                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1588                       wParam & MK_SHIFT, wParam & MK_CONTROL);
1589                 SetCapture(hwnd);
1590             } else {
1591                 term_mouse(button, MA_RELEASE,
1592                            TO_CHR_X(X_POS(lParam)),
1593                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1594                            wParam & MK_CONTROL);
1595                 ReleaseCapture();
1596             }
1597         }
1598         return 0;
1599       case WM_MOUSEMOVE:
1600         show_mouseptr(1);
1601         /*
1602          * Add the mouse position and message time to the random
1603          * number noise.
1604          */
1605         noise_ultralight(lParam);
1606
1607         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1608             Mouse_Button b;
1609             if (wParam & MK_LBUTTON)
1610                 b = MBT_LEFT;
1611             else if (wParam & MK_MBUTTON)
1612                 b = MBT_MIDDLE;
1613             else
1614                 b = MBT_RIGHT;
1615             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1616                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1617                        wParam & MK_CONTROL);
1618         }
1619         return 0;
1620       case WM_NCMOUSEMOVE:
1621         show_mouseptr(1);
1622         noise_ultralight(lParam);
1623         return 0;
1624       case WM_IGNORE_CLIP:
1625         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1626         break;
1627       case WM_DESTROYCLIPBOARD:
1628         if (!ignore_clip)
1629             term_deselect();
1630         ignore_clip = FALSE;
1631         return 0;
1632       case WM_PAINT:
1633         {
1634             PAINTSTRUCT p;
1635             HideCaret(hwnd);
1636             hdc = BeginPaint(hwnd, &p);
1637             if (pal) {
1638                 SelectPalette(hdc, pal, TRUE);
1639                 RealizePalette(hdc);
1640             }
1641             term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1642                        p.rcPaint.right, p.rcPaint.bottom);
1643             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1644             SelectObject(hdc, GetStockObject(WHITE_PEN));
1645             EndPaint(hwnd, &p);
1646             ShowCaret(hwnd);
1647         }
1648         return 0;
1649       case WM_NETEVENT:
1650         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1651          * but the only one that's likely to try to overload us is FD_READ.
1652          * This means buffering just one is fine.
1653          */
1654         if (pending_netevent)
1655             enact_pending_netevent();
1656
1657         pending_netevent = TRUE;
1658         pend_netevent_wParam = wParam;
1659         pend_netevent_lParam = lParam;
1660         time(&last_movement);
1661         return 0;
1662       case WM_SETFOCUS:
1663         has_focus = TRUE;
1664         CreateCaret(hwnd, caretbm, font_width, font_height);
1665         ShowCaret(hwnd);
1666         flash_window(0);               /* stop */
1667         compose_state = 0;
1668         term_out();
1669         term_update();
1670         break;
1671       case WM_KILLFOCUS:
1672         show_mouseptr(1);
1673         has_focus = FALSE;
1674         DestroyCaret();
1675         term_out();
1676         term_update();
1677         break;
1678       case WM_IGNORE_SIZE:
1679         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1680         break;
1681       case WM_ENTERSIZEMOVE:
1682         EnableSizeTip(1);
1683         resizing = TRUE;
1684         need_backend_resize = FALSE;
1685         break;
1686       case WM_EXITSIZEMOVE:
1687         EnableSizeTip(0);
1688         resizing = FALSE;
1689         if (need_backend_resize)
1690             back->size();
1691         break;
1692       case WM_SIZING:
1693         {
1694             int width, height, w, h, ew, eh;
1695             LPRECT r = (LPRECT) lParam;
1696
1697             width = r->right - r->left - extra_width;
1698             height = r->bottom - r->top - extra_height;
1699             w = (width + font_width / 2) / font_width;
1700             if (w < 1)
1701                 w = 1;
1702             h = (height + font_height / 2) / font_height;
1703             if (h < 1)
1704                 h = 1;
1705             UpdateSizeTip(hwnd, w, h);
1706             ew = width - w * font_width;
1707             eh = height - h * font_height;
1708             if (ew != 0) {
1709                 if (wParam == WMSZ_LEFT ||
1710                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1711                     r->left += ew;
1712                 else
1713                     r->right -= ew;
1714             }
1715             if (eh != 0) {
1716                 if (wParam == WMSZ_TOP ||
1717                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1718                     r->top += eh;
1719                 else
1720                     r->bottom -= eh;
1721             }
1722             if (ew || eh)
1723                 return 1;
1724             else
1725                 return 0;
1726         }
1727         /* break;  (never reached) */
1728       case WM_SIZE:
1729         if (wParam == SIZE_MINIMIZED) {
1730             SetWindowText(hwnd,
1731                           cfg.win_name_always ? window_name : icon_name);
1732             break;
1733         }
1734         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1735             SetWindowText(hwnd, window_name);
1736         if (!ignore_size) {
1737             int width, height, w, h;
1738 #if 0                                  /* we have fixed this using WM_SIZING now */
1739             int ew, eh;
1740 #endif
1741
1742             width = LOWORD(lParam);
1743             height = HIWORD(lParam);
1744             w = width / font_width;
1745             if (w < 1)
1746                 w = 1;
1747             h = height / font_height;
1748             if (h < 1)
1749                 h = 1;
1750 #if 0                                  /* we have fixed this using WM_SIZING now */
1751             ew = width - w * font_width;
1752             eh = height - h * font_height;
1753             if (ew != 0 || eh != 0) {
1754                 RECT r;
1755                 GetWindowRect(hwnd, &r);
1756                 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1757                 SetWindowPos(hwnd, NULL, 0, 0,
1758                              r.right - r.left - ew, r.bottom - r.top - eh,
1759                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1760             }
1761 #endif
1762             if (w != cols || h != rows || just_reconfigged) {
1763                 term_invalidate();
1764                 term_size(h, w, cfg.savelines);
1765                 /*
1766                  * Don't call back->size in mid-resize. (To prevent
1767                  * massive numbers of resize events getting sent
1768                  * down the connection during an NT opaque drag.)
1769                  */
1770                 if (!resizing)
1771                     back->size();
1772                 else {
1773                     need_backend_resize = TRUE;
1774                     cfg.height = h;
1775                     cfg.width = w;
1776                 }
1777                 just_reconfigged = FALSE;
1778             }
1779         }
1780         if (wParam == SIZE_RESTORED && defered_resize) {
1781             defered_resize = FALSE;
1782             SetWindowPos(hwnd, NULL, 0, 0,
1783                          extra_width + font_width * cfg.width,
1784                          extra_height + font_height * cfg.height,
1785                          SWP_NOACTIVATE | SWP_NOCOPYBITS |
1786                          SWP_NOMOVE | SWP_NOZORDER);
1787         }
1788         ignore_size = FALSE;
1789         return 0;
1790       case WM_VSCROLL:
1791         switch (LOWORD(wParam)) {
1792           case SB_BOTTOM:
1793             term_scroll(-1, 0);
1794             break;
1795           case SB_TOP:
1796             term_scroll(+1, 0);
1797             break;
1798           case SB_LINEDOWN:
1799             term_scroll(0, +1);
1800             break;
1801           case SB_LINEUP:
1802             term_scroll(0, -1);
1803             break;
1804           case SB_PAGEDOWN:
1805             term_scroll(0, +rows / 2);
1806             break;
1807           case SB_PAGEUP:
1808             term_scroll(0, -rows / 2);
1809             break;
1810           case SB_THUMBPOSITION:
1811           case SB_THUMBTRACK:
1812             term_scroll(1, HIWORD(wParam));
1813             break;
1814         }
1815         break;
1816       case WM_PALETTECHANGED:
1817         if ((HWND) wParam != hwnd && pal != NULL) {
1818             HDC hdc = get_ctx();
1819             if (hdc) {
1820                 if (RealizePalette(hdc) > 0)
1821                     UpdateColors(hdc);
1822                 free_ctx(hdc);
1823             }
1824         }
1825         break;
1826       case WM_QUERYNEWPALETTE:
1827         if (pal != NULL) {
1828             HDC hdc = get_ctx();
1829             if (hdc) {
1830                 if (RealizePalette(hdc) > 0)
1831                     UpdateColors(hdc);
1832                 free_ctx(hdc);
1833                 return TRUE;
1834             }
1835         }
1836         return FALSE;
1837       case WM_KEYDOWN:
1838       case WM_SYSKEYDOWN:
1839       case WM_KEYUP:
1840       case WM_SYSKEYUP:
1841         /*
1842          * Add the scan code and keypress timing to the random
1843          * number noise.
1844          */
1845         noise_ultralight(lParam);
1846
1847         /*
1848          * We don't do TranslateMessage since it disassociates the
1849          * resulting CHAR message from the KEYDOWN that sparked it,
1850          * which we occasionally don't want. Instead, we process
1851          * KEYDOWN, and call the Win32 translator functions so that
1852          * we get the translations under _our_ control.
1853          */
1854         {
1855             unsigned char buf[20];
1856             int len;
1857
1858             if (wParam == VK_PROCESSKEY) {
1859                 MSG m;
1860                 m.hwnd = hwnd;
1861                 m.message = WM_KEYDOWN;
1862                 m.wParam = wParam;
1863                 m.lParam = lParam & 0xdfff;
1864                 TranslateMessage(&m);
1865             } else {
1866                 len = TranslateKey(message, wParam, lParam, buf);
1867                 if (len == -1)
1868                     return DefWindowProc(hwnd, message, wParam, lParam);
1869                 ldisc_send(buf, len);
1870
1871                 if (len > 0)
1872                     show_mouseptr(0);
1873             }
1874         }
1875         return 0;
1876       case WM_INPUTLANGCHANGE:
1877         {
1878             /* wParam == Font number */
1879             /* lParam == Locale */
1880             char lbuf[20];
1881             HKL NewInputLocale = (HKL) lParam;
1882
1883             // lParam == GetKeyboardLayout(0);
1884
1885             GetLocaleInfo(LOWORD(NewInputLocale),
1886                           LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1887
1888             kbd_codepage = atoi(lbuf);
1889         }
1890         break;
1891       case WM_IME_COMPOSITION:
1892         {
1893             HIMC hIMC;
1894             int n;
1895             char *buff;
1896    
1897             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
1898                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1899
1900             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1901                 break; /* fall back to DefWindowProc */
1902
1903             hIMC = ImmGetContext(hwnd);
1904             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1905
1906             if (n > 0) {
1907                 buff = (char*) smalloc(n);
1908                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1909                 luni_send((unsigned short *)buff, n / 2);
1910                 free(buff);
1911             }
1912             ImmReleaseContext(hwnd, hIMC);
1913             return 1;
1914         }
1915
1916       case WM_IME_CHAR:
1917         if (wParam & 0xFF00) {
1918             unsigned char buf[2];
1919
1920             buf[1] = wParam;
1921             buf[0] = wParam >> 8;
1922             lpage_send(kbd_codepage, buf, 2);
1923         } else {
1924             char c = (unsigned char) wParam;
1925             lpage_send(kbd_codepage, &c, 1);
1926         }
1927         return (0);
1928       case WM_CHAR:
1929       case WM_SYSCHAR:
1930         /*
1931          * Nevertheless, we are prepared to deal with WM_CHAR
1932          * messages, should they crop up. So if someone wants to
1933          * post the things to us as part of a macro manoeuvre,
1934          * we're ready to cope.
1935          */
1936         {
1937             char c = (unsigned char)wParam;
1938             lpage_send(CP_ACP, &c, 1);
1939         }
1940         return 0;
1941       case WM_SETCURSOR:
1942         if (send_raw_mouse) {
1943             SetCursor(LoadCursor(NULL, IDC_ARROW));
1944             return TRUE;
1945         }
1946     }
1947
1948     return DefWindowProc(hwnd, message, wParam, lParam);
1949 }
1950
1951 /*
1952  * Move the system caret. (We maintain one, even though it's
1953  * invisible, for the benefit of blind people: apparently some
1954  * helper software tracks the system caret, so we should arrange to
1955  * have one.)
1956  */
1957 void sys_cursor(int x, int y)
1958 {
1959     COMPOSITIONFORM cf;
1960     HIMC hIMC;
1961
1962     if (!has_focus) return;
1963     
1964     SetCaretPos(x * font_width, y * font_height);
1965
1966     /* IMM calls on Win98 and beyond only */
1967     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1968     
1969     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1970             osVersion.dwMinorVersion == 0) return; /* 95 */
1971
1972     /* we should have the IMM functions */
1973     hIMC = ImmGetContext(hwnd);
1974     cf.dwStyle = CFS_POINT;
1975     cf.ptCurrentPos.x = x * font_width;
1976     cf.ptCurrentPos.y = y * font_height;
1977     ImmSetCompositionWindow(hIMC, &cf);
1978
1979     ImmReleaseContext(hwnd, hIMC);
1980 }
1981
1982 /*
1983  * Draw a line of text in the window, at given character
1984  * coordinates, in given attributes.
1985  *
1986  * We are allowed to fiddle with the contents of `text'.
1987  */
1988 void do_text(Context ctx, int x, int y, char *text, int len,
1989              unsigned long attr, int lattr)
1990 {
1991     COLORREF fg, bg, t;
1992     int nfg, nbg, nfont;
1993     HDC hdc = ctx;
1994     RECT line_box;
1995     int force_manual_underline = 0;
1996     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1997     int char_width = fnt_width;
1998     int text_adjust = 0;
1999     static int *IpDx = 0, IpDxLEN = 0;
2000
2001     if (attr & ATTR_WIDE)
2002         char_width *= 2;
2003
2004     if (len > IpDxLEN || IpDx[0] != char_width) {
2005         int i;
2006         if (len > IpDxLEN) {
2007             sfree(IpDx);
2008             IpDx = smalloc((len + 16) * sizeof(int));
2009             IpDxLEN = (len + 16);
2010         }
2011         for (i = 0; i < IpDxLEN; i++)
2012             IpDx[i] = char_width;
2013     }
2014
2015     x *= fnt_width;
2016     y *= font_height;
2017
2018     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2019         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2020         attr ^= ATTR_CUR_XOR;
2021     }
2022
2023     nfont = 0;
2024     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2025         /* Assume a poorman font is borken in other ways too. */
2026         lattr = LATTR_WIDE;
2027     } else
2028         switch (lattr) {
2029           case LATTR_NORM:
2030             break;
2031           case LATTR_WIDE:
2032             nfont |= FONT_WIDE;
2033             break;
2034           default:
2035             nfont |= FONT_WIDE + FONT_HIGH;
2036             break;
2037         }
2038
2039     /* Special hack for the VT100 linedraw glyphs. */
2040     if ((attr & CSET_MASK) == 0x2300) {
2041         if (!dbcs_screenfont &&
2042             text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2043             switch ((unsigned char) (text[0])) {
2044               case 0xBA:
2045                 text_adjust = -2 * font_height / 5;
2046                 break;
2047               case 0xBB:
2048                 text_adjust = -1 * font_height / 5;
2049                 break;
2050               case 0xBC:
2051                 text_adjust = font_height / 5;
2052                 break;
2053               case 0xBD:
2054                 text_adjust = 2 * font_height / 5;
2055                 break;
2056             }
2057             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2058                 text_adjust *= 2;
2059             attr &= ~CSET_MASK;
2060             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2061             attr |= (unitab_xterm['q'] & CSET_MASK);
2062             if (attr & ATTR_UNDER) {
2063                 attr &= ~ATTR_UNDER;
2064                 force_manual_underline = 1;
2065             }
2066         }
2067     }
2068
2069     /* Anything left as an original character set is unprintable. */
2070     if (DIRECT_CHAR(attr)) {
2071         attr &= ~CSET_MASK;
2072         attr |= 0xFF00;
2073         memset(text, 0xFF, len);
2074     }
2075
2076     /* OEM CP */
2077     if ((attr & CSET_MASK) == ATTR_OEMCP)
2078         nfont |= FONT_OEM;
2079
2080     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2081     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2082     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2083         nfont |= FONT_BOLD;
2084     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2085         nfont |= FONT_UNDERLINE;
2086     another_font(nfont);
2087     if (!fonts[nfont]) {
2088         if (nfont & FONT_UNDERLINE)
2089             force_manual_underline = 1;
2090         /* Don't do the same for manual bold, it could be bad news. */
2091
2092         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2093     }
2094     another_font(nfont);
2095     if (!fonts[nfont])
2096         nfont = FONT_NORMAL;
2097     if (attr & ATTR_REVERSE) {
2098         t = nfg;
2099         nfg = nbg;
2100         nbg = t;
2101     }
2102     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2103         nfg++;
2104     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2105         nbg++;
2106     fg = colours[nfg];
2107     bg = colours[nbg];
2108     SelectObject(hdc, fonts[nfont]);
2109     SetTextColor(hdc, fg);
2110     SetBkColor(hdc, bg);
2111     SetBkMode(hdc, OPAQUE);
2112     line_box.left = x;
2113     line_box.top = y;
2114     line_box.right = x + char_width * len;
2115     line_box.bottom = y + font_height;
2116
2117     /* We're using a private area for direct to font. (512 chars.) */
2118     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2119         /* Ho Hum, dbcs fonts are a PITA! */
2120         /* To display on W9x I have to convert to UCS */
2121         static wchar_t *uni_buf = 0;
2122         static int uni_len = 0;
2123         int nlen;
2124         if (len > uni_len) {
2125             sfree(uni_buf);
2126             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2127         }
2128         nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2129                                    text, len, uni_buf, uni_len);
2130
2131         if (nlen <= 0)
2132             return;                    /* Eeek! */
2133
2134         ExtTextOutW(hdc, x,
2135                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2136                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2137         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2138             SetBkMode(hdc, TRANSPARENT);
2139             ExtTextOutW(hdc, x - 1,
2140                         y - font_height * (lattr ==
2141                                            LATTR_BOT) + text_adjust,
2142                         ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2143         }
2144     } else if (DIRECT_FONT(attr)) {
2145         ExtTextOut(hdc, x,
2146                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2147                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2148         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2149             SetBkMode(hdc, TRANSPARENT);
2150
2151             /* GRR: This draws the character outside it's box and can leave
2152              * 'droppings' even with the clip box! I suppose I could loop it
2153              * one character at a time ... yuk. 
2154              * 
2155              * Or ... I could do a test print with "W", and use +1 or -1 for this
2156              * shift depending on if the leftmost column is blank...
2157              */
2158             ExtTextOut(hdc, x - 1,
2159                        y - font_height * (lattr ==
2160                                           LATTR_BOT) + text_adjust,
2161                        ETO_CLIPPED, &line_box, text, len, IpDx);
2162         }
2163     } else {
2164         /* And 'normal' unicode characters */
2165         static WCHAR *wbuf = NULL;
2166         static int wlen = 0;
2167         int i;
2168         if (wlen < len) {
2169             sfree(wbuf);
2170             wlen = len;
2171             wbuf = smalloc(wlen * sizeof(WCHAR));
2172         }
2173         for (i = 0; i < len; i++)
2174             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2175
2176         ExtTextOutW(hdc, x,
2177                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2178                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2179
2180         /* And the shadow bold hack. */
2181         if (bold_mode == BOLD_SHADOW) {
2182             SetBkMode(hdc, TRANSPARENT);
2183             ExtTextOutW(hdc, x - 1,
2184                         y - font_height * (lattr ==
2185                                            LATTR_BOT) + text_adjust,
2186                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2187         }
2188     }
2189     if (lattr != LATTR_TOP && (force_manual_underline ||
2190                                (und_mode == UND_LINE
2191                                 && (attr & ATTR_UNDER)))) {
2192         HPEN oldpen;
2193         int dec = descent;
2194         if (lattr == LATTR_BOT)
2195             dec = dec * 2 - font_height;
2196
2197         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2198         MoveToEx(hdc, x, y + dec, NULL);
2199         LineTo(hdc, x + len * char_width, y + dec);
2200         oldpen = SelectObject(hdc, oldpen);
2201         DeleteObject(oldpen);
2202     }
2203 }
2204
2205 void do_cursor(Context ctx, int x, int y, char *text, int len,
2206                unsigned long attr, int lattr)
2207 {
2208
2209     int fnt_width;
2210     int char_width;
2211     HDC hdc = ctx;
2212     int ctype = cfg.cursor_type;
2213
2214     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2215         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2216             do_text(ctx, x, y, text, len, attr, lattr);
2217             return;
2218         }
2219         ctype = 2;
2220         attr |= TATTR_RIGHTCURS;
2221     }
2222
2223     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2224     if (attr & ATTR_WIDE)
2225         char_width *= 2;
2226     x *= fnt_width;
2227     y *= font_height;
2228
2229     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2230         POINT pts[5];
2231         HPEN oldpen;
2232         pts[0].x = pts[1].x = pts[4].x = x;
2233         pts[2].x = pts[3].x = x + char_width - 1;
2234         pts[0].y = pts[3].y = pts[4].y = y;
2235         pts[1].y = pts[2].y = y + font_height - 1;
2236         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2237         Polyline(hdc, pts, 5);
2238         oldpen = SelectObject(hdc, oldpen);
2239         DeleteObject(oldpen);
2240     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2241         int startx, starty, dx, dy, length, i;
2242         if (ctype == 1) {
2243             startx = x;
2244             starty = y + descent;
2245             dx = 1;
2246             dy = 0;
2247             length = char_width;
2248         } else {
2249             int xadjust = 0;
2250             if (attr & TATTR_RIGHTCURS)
2251                 xadjust = char_width - 1;
2252             startx = x + xadjust;
2253             starty = y;
2254             dx = 0;
2255             dy = 1;
2256             length = font_height;
2257         }
2258         if (attr & TATTR_ACTCURS) {
2259             HPEN oldpen;
2260             oldpen =
2261                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2262             MoveToEx(hdc, startx, starty, NULL);
2263             LineTo(hdc, startx + dx * length, starty + dy * length);
2264             oldpen = SelectObject(hdc, oldpen);
2265             DeleteObject(oldpen);
2266         } else {
2267             for (i = 0; i < length; i++) {
2268                 if (i % 2 == 0) {
2269                     SetPixel(hdc, startx, starty, colours[23]);
2270                 }
2271                 startx += dx;
2272                 starty += dy;
2273             }
2274         }
2275     }
2276 }
2277
2278 /*
2279  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2280  * codes. Returns number of bytes used or zero to drop the message
2281  * or -1 to forward the message to windows.
2282  */
2283 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2284                         unsigned char *output)
2285 {
2286     BYTE keystate[256];
2287     int scan, left_alt = 0, key_down, shift_state;
2288     int r, i, code;
2289     unsigned char *p = output;
2290     static int alt_state = 0;
2291     static int alt_sum = 0;
2292
2293     HKL kbd_layout = GetKeyboardLayout(0);
2294
2295     static WORD keys[3];
2296     static int compose_char = 0;
2297     static WPARAM compose_key = 0;
2298
2299     r = GetKeyboardState(keystate);
2300     if (!r)
2301         memset(keystate, 0, sizeof(keystate));
2302     else {
2303 #if 0
2304 #define SHOW_TOASCII_RESULT
2305         {                              /* Tell us all about key events */
2306             static BYTE oldstate[256];
2307             static int first = 1;
2308             static int scan;
2309             int ch;
2310             if (first)
2311                 memcpy(oldstate, keystate, sizeof(oldstate));
2312             first = 0;
2313
2314             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2315                 debug(("+"));
2316             } else if ((HIWORD(lParam) & KF_UP)
2317                        && scan == (HIWORD(lParam) & 0xFF)) {
2318                 debug((". U"));
2319             } else {
2320                 debug((".\n"));
2321                 if (wParam >= VK_F1 && wParam <= VK_F20)
2322                     debug(("K_F%d", wParam + 1 - VK_F1));
2323                 else
2324                     switch (wParam) {
2325                       case VK_SHIFT:
2326                         debug(("SHIFT"));
2327                         break;
2328                       case VK_CONTROL:
2329                         debug(("CTRL"));
2330                         break;
2331                       case VK_MENU:
2332                         debug(("ALT"));
2333                         break;
2334                       default:
2335                         debug(("VK_%02x", wParam));
2336                     }
2337                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2338                     debug(("*"));
2339                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2340
2341                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2342                 if (ch >= ' ' && ch <= '~')
2343                     debug((", '%c'", ch));
2344                 else if (ch)
2345                     debug((", $%02x", ch));
2346
2347                 if (keys[0])
2348                     debug((", KB0=%02x", keys[0]));
2349                 if (keys[1])
2350                     debug((", KB1=%02x", keys[1]));
2351                 if (keys[2])
2352                     debug((", KB2=%02x", keys[2]));
2353
2354                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2355                     debug((", S"));
2356                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2357                     debug((", C"));
2358                 if ((HIWORD(lParam) & KF_EXTENDED))
2359                     debug((", E"));
2360                 if ((HIWORD(lParam) & KF_UP))
2361                     debug((", U"));
2362             }
2363
2364             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2365             else if ((HIWORD(lParam) & KF_UP))
2366                 oldstate[wParam & 0xFF] ^= 0x80;
2367             else
2368                 oldstate[wParam & 0xFF] ^= 0x81;
2369
2370             for (ch = 0; ch < 256; ch++)
2371                 if (oldstate[ch] != keystate[ch])
2372                     debug((", M%02x=%02x", ch, keystate[ch]));
2373
2374             memcpy(oldstate, keystate, sizeof(oldstate));
2375         }
2376 #endif
2377
2378         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2379             keystate[VK_RMENU] = keystate[VK_MENU];
2380         }
2381
2382
2383         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2384         if ((cfg.funky_type == 3 ||
2385              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2386             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2387
2388             wParam = VK_EXECUTE;
2389
2390             /* UnToggle NUMLock */
2391             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2392                 keystate[VK_NUMLOCK] ^= 1;
2393         }
2394
2395         /* And write back the 'adjusted' state */
2396         SetKeyboardState(keystate);
2397     }
2398
2399     /* Disable Auto repeat if required */
2400     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2401         return 0;
2402
2403     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2404         left_alt = 1;
2405
2406     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2407
2408     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2409     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2410         if (cfg.ctrlaltkeys)
2411             keystate[VK_MENU] = 0;
2412         else {
2413             keystate[VK_RMENU] = 0x80;
2414             left_alt = 0;
2415         }
2416     }
2417
2418     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2419     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2420         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2421
2422     /* Note if AltGr was pressed and if it was used as a compose key */
2423     if (!compose_state) {
2424         compose_key = 0x100;
2425         if (cfg.compose_key) {
2426             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2427                 compose_key = wParam;
2428         }
2429         if (wParam == VK_APPS)
2430             compose_key = wParam;
2431     }
2432
2433     if (wParam == compose_key) {
2434         if (compose_state == 0
2435             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2436                 1;
2437         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2438             compose_state = 2;
2439         else
2440             compose_state = 0;
2441     } else if (compose_state == 1 && wParam != VK_CONTROL)
2442         compose_state = 0;
2443
2444     /* 
2445      * Record that we pressed key so the scroll window can be reset, but
2446      * be careful to avoid Shift-UP/Down
2447      */
2448     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2449         seen_key_event = 1;
2450     }
2451
2452     /* Make sure we're not pasting */
2453     if (key_down)
2454         term_nopaste();
2455
2456     if (compose_state > 1 && left_alt)
2457         compose_state = 0;
2458
2459     /* Sanitize the number pad if not using a PC NumPad */
2460     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2461                      && cfg.funky_type != 2)
2462         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2463         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2464             int nParam = 0;
2465             switch (wParam) {
2466               case VK_INSERT:
2467                 nParam = VK_NUMPAD0;
2468                 break;
2469               case VK_END:
2470                 nParam = VK_NUMPAD1;
2471                 break;
2472               case VK_DOWN:
2473                 nParam = VK_NUMPAD2;
2474                 break;
2475               case VK_NEXT:
2476                 nParam = VK_NUMPAD3;
2477                 break;
2478               case VK_LEFT:
2479                 nParam = VK_NUMPAD4;
2480                 break;
2481               case VK_CLEAR:
2482                 nParam = VK_NUMPAD5;
2483                 break;
2484               case VK_RIGHT:
2485                 nParam = VK_NUMPAD6;
2486                 break;
2487               case VK_HOME:
2488                 nParam = VK_NUMPAD7;
2489                 break;
2490               case VK_UP:
2491                 nParam = VK_NUMPAD8;
2492                 break;
2493               case VK_PRIOR:
2494                 nParam = VK_NUMPAD9;
2495                 break;
2496               case VK_DELETE:
2497                 nParam = VK_DECIMAL;
2498                 break;
2499             }
2500             if (nParam) {
2501                 if (keystate[VK_NUMLOCK] & 1)
2502                     shift_state |= 1;
2503                 wParam = nParam;
2504             }
2505         }
2506     }
2507
2508     /* If a key is pressed and AltGr is not active */
2509     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2510         /* Okay, prepare for most alts then ... */
2511         if (left_alt)
2512             *p++ = '\033';
2513
2514         /* Lets see if it's a pattern we know all about ... */
2515         if (wParam == VK_PRIOR && shift_state == 1) {
2516             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2517             return 0;
2518         }
2519         if (wParam == VK_NEXT && shift_state == 1) {
2520             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2521             return 0;
2522         }
2523         if (wParam == VK_INSERT && shift_state == 1) {
2524             term_do_paste();
2525             return 0;
2526         }
2527         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2528             return -1;
2529         }
2530         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2531             alt_state = 0;
2532             PostMessage(hwnd, WM_CHAR, ' ', 0);
2533             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2534             return -1;
2535         }
2536         /* Control-Numlock for app-keypad mode switch */
2537         if (wParam == VK_PAUSE && shift_state == 2) {
2538             app_keypad_keys ^= 1;
2539             return 0;
2540         }
2541
2542         /* Nethack keypad */
2543         if (cfg.nethack_keypad && !left_alt) {
2544             switch (wParam) {
2545               case VK_NUMPAD1:
2546                 *p++ = shift_state ? 'B' : 'b';
2547                 return p - output;
2548               case VK_NUMPAD2:
2549                 *p++ = shift_state ? 'J' : 'j';
2550                 return p - output;
2551               case VK_NUMPAD3:
2552                 *p++ = shift_state ? 'N' : 'n';
2553                 return p - output;
2554               case VK_NUMPAD4:
2555                 *p++ = shift_state ? 'H' : 'h';
2556                 return p - output;
2557               case VK_NUMPAD5:
2558                 *p++ = shift_state ? '.' : '.';
2559                 return p - output;
2560               case VK_NUMPAD6:
2561                 *p++ = shift_state ? 'L' : 'l';
2562                 return p - output;
2563               case VK_NUMPAD7:
2564                 *p++ = shift_state ? 'Y' : 'y';
2565                 return p - output;
2566               case VK_NUMPAD8:
2567                 *p++ = shift_state ? 'K' : 'k';
2568                 return p - output;
2569               case VK_NUMPAD9:
2570                 *p++ = shift_state ? 'U' : 'u';
2571                 return p - output;
2572             }
2573         }
2574
2575         /* Application Keypad */
2576         if (!left_alt) {
2577             int xkey = 0;
2578
2579             if (cfg.funky_type == 3 ||
2580                 (cfg.funky_type <= 1 &&
2581                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2582                   case VK_EXECUTE:
2583                     xkey = 'P';
2584                     break;
2585                   case VK_DIVIDE:
2586                     xkey = 'Q';
2587                     break;
2588                   case VK_MULTIPLY:
2589                     xkey = 'R';
2590                     break;
2591                   case VK_SUBTRACT:
2592                     xkey = 'S';
2593                     break;
2594                 }
2595             if (app_keypad_keys && !cfg.no_applic_k)
2596                 switch (wParam) {
2597                   case VK_NUMPAD0:
2598                     xkey = 'p';
2599                     break;
2600                   case VK_NUMPAD1:
2601                     xkey = 'q';
2602                     break;
2603                   case VK_NUMPAD2:
2604                     xkey = 'r';
2605                     break;
2606                   case VK_NUMPAD3:
2607                     xkey = 's';
2608                     break;
2609                   case VK_NUMPAD4:
2610                     xkey = 't';
2611                     break;
2612                   case VK_NUMPAD5:
2613                     xkey = 'u';
2614                     break;
2615                   case VK_NUMPAD6:
2616                     xkey = 'v';
2617                     break;
2618                   case VK_NUMPAD7:
2619                     xkey = 'w';
2620                     break;
2621                   case VK_NUMPAD8:
2622                     xkey = 'x';
2623                     break;
2624                   case VK_NUMPAD9:
2625                     xkey = 'y';
2626                     break;
2627
2628                   case VK_DECIMAL:
2629                     xkey = 'n';
2630                     break;
2631                   case VK_ADD:
2632                     if (cfg.funky_type == 2) {
2633                         if (shift_state)
2634                             xkey = 'l';
2635                         else
2636                             xkey = 'k';
2637                     } else if (shift_state)
2638                         xkey = 'm';
2639                     else
2640                         xkey = 'l';
2641                     break;
2642
2643                   case VK_DIVIDE:
2644                     if (cfg.funky_type == 2)
2645                         xkey = 'o';
2646                     break;
2647                   case VK_MULTIPLY:
2648                     if (cfg.funky_type == 2)
2649                         xkey = 'j';
2650                     break;
2651                   case VK_SUBTRACT:
2652                     if (cfg.funky_type == 2)
2653                         xkey = 'm';
2654                     break;
2655
2656                   case VK_RETURN:
2657                     if (HIWORD(lParam) & KF_EXTENDED)
2658                         xkey = 'M';
2659                     break;
2660                 }
2661             if (xkey) {
2662                 if (vt52_mode) {
2663                     if (xkey >= 'P' && xkey <= 'S')
2664                         p += sprintf((char *) p, "\x1B%c", xkey);
2665                     else
2666                         p += sprintf((char *) p, "\x1B?%c", xkey);
2667                 } else
2668                     p += sprintf((char *) p, "\x1BO%c", xkey);
2669                 return p - output;
2670             }
2671         }
2672
2673         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
2674             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2675             *p++ = 0;
2676             return -2;
2677         }
2678         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
2679             *p++ = 0x1B;
2680             *p++ = '[';
2681             *p++ = 'Z';
2682             return p - output;
2683         }
2684         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
2685             *p++ = 0;
2686             return p - output;
2687         }
2688         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
2689             *p++ = 160;
2690             return p - output;
2691         }
2692         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
2693             *p++ = 3;
2694             *p++ = 0;
2695             return -2;
2696         }
2697         if (wParam == VK_PAUSE) {      /* Break/Pause */
2698             *p++ = 26;
2699             *p++ = 0;
2700             return -2;
2701         }
2702         /* Control-2 to Control-8 are special */
2703         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2704             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2705             return p - output;
2706         }
2707         if (shift_state == 2 && wParam == 0xBD) {
2708             *p++ = 0x1F;
2709             return p - output;
2710         }
2711         if (shift_state == 2 && wParam == 0xDF) {
2712             *p++ = 0x1C;
2713             return p - output;
2714         }
2715         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2716             *p++ = '\r';
2717             *p++ = '\n';
2718             return p - output;
2719         }
2720
2721         /*
2722          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2723          * for integer decimal nn.)
2724          *
2725          * We also deal with the weird ones here. Linux VCs replace F1
2726          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2727          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2728          * respectively.
2729          */
2730         code = 0;
2731         switch (wParam) {
2732           case VK_F1:
2733             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2734             break;
2735           case VK_F2:
2736             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2737             break;
2738           case VK_F3:
2739             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2740             break;
2741           case VK_F4:
2742             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2743             break;
2744           case VK_F5:
2745             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2746             break;
2747           case VK_F6:
2748             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2749             break;
2750           case VK_F7:
2751             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2752             break;
2753           case VK_F8:
2754             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2755             break;
2756           case VK_F9:
2757             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2758             break;
2759           case VK_F10:
2760             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2761             break;
2762           case VK_F11:
2763             code = 23;
2764             break;
2765           case VK_F12:
2766             code = 24;
2767             break;
2768           case VK_F13:
2769             code = 25;
2770             break;
2771           case VK_F14:
2772             code = 26;
2773             break;
2774           case VK_F15:
2775             code = 28;
2776             break;
2777           case VK_F16:
2778             code = 29;
2779             break;
2780           case VK_F17:
2781             code = 31;
2782             break;
2783           case VK_F18:
2784             code = 32;
2785             break;
2786           case VK_F19:
2787             code = 33;
2788             break;
2789           case VK_F20:
2790             code = 34;
2791             break;
2792           case VK_HOME:
2793             code = 1;
2794             break;
2795           case VK_INSERT:
2796             code = 2;
2797             break;
2798           case VK_DELETE:
2799             code = 3;
2800             break;
2801           case VK_END:
2802             code = 4;
2803             break;
2804           case VK_PRIOR:
2805             code = 5;
2806             break;
2807           case VK_NEXT:
2808             code = 6;
2809             break;
2810         }
2811         /* Reorder edit keys to physical order */
2812         if (cfg.funky_type == 3 && code <= 6)
2813             code = "\0\2\1\4\5\3\6"[code];
2814
2815         if (vt52_mode && code > 0 && code <= 6) {
2816             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2817             return p - output;
2818         }
2819
2820         if (cfg.funky_type == 5 &&     /* SCO function keys */
2821             code >= 11 && code <= 34) {
2822             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2823             int index = 0;
2824             switch (wParam) {
2825               case VK_F1: index = 0; break;
2826               case VK_F2: index = 1; break;
2827               case VK_F3: index = 2; break;
2828               case VK_F4: index = 3; break;
2829               case VK_F5: index = 4; break;
2830               case VK_F6: index = 5; break;
2831               case VK_F7: index = 6; break;
2832               case VK_F8: index = 7; break;
2833               case VK_F9: index = 8; break;
2834               case VK_F10: index = 9; break;
2835               case VK_F11: index = 10; break;
2836               case VK_F12: index = 11; break;
2837             }
2838             if (keystate[VK_SHIFT] & 0x80) index += 12;
2839             if (keystate[VK_CONTROL] & 0x80) index += 24;
2840             p += sprintf((char *) p, "\x1B[%c", codes[index]);
2841             return p - output;
2842         }
2843         if (cfg.funky_type == 5 &&     /* SCO small keypad */
2844             code >= 1 && code <= 6) {
2845             char codes[] = "HL.FIG";
2846             if (code == 3) {
2847                 *p++ = '\x7F';
2848             } else {
2849                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2850             }
2851             return p - output;
2852         }
2853         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2854             int offt = 0;
2855             if (code > 15)
2856                 offt++;
2857             if (code > 21)
2858                 offt++;
2859             if (vt52_mode)
2860                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2861             else
2862                 p +=
2863                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2864             return p - output;
2865         }
2866         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2867             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2868             return p - output;
2869         }
2870         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2871             if (vt52_mode)
2872                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2873             else
2874                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2875             return p - output;
2876         }
2877         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2878             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2879             return p - output;
2880         }
2881         if (code) {
2882             p += sprintf((char *) p, "\x1B[%d~", code);
2883             return p - output;
2884         }
2885
2886         /*
2887          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2888          * some reason seems to send VK_CLEAR to Windows...).
2889          */
2890         {
2891             char xkey = 0;
2892             switch (wParam) {
2893               case VK_UP:
2894                 xkey = 'A';
2895                 break;
2896               case VK_DOWN:
2897                 xkey = 'B';
2898                 break;
2899               case VK_RIGHT:
2900                 xkey = 'C';
2901                 break;
2902               case VK_LEFT:
2903                 xkey = 'D';
2904                 break;
2905               case VK_CLEAR:
2906                 xkey = 'G';
2907                 break;
2908             }
2909             if (xkey) {
2910                 if (vt52_mode)
2911                     p += sprintf((char *) p, "\x1B%c", xkey);
2912                 else {
2913                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2914                     /* VT100 & VT102 manuals both state the app cursor keys
2915                      * only work if the app keypad is on.
2916                      */
2917                     if (!app_keypad_keys)
2918                         app_flg = 0;
2919                     /* Useful mapping of Ctrl-arrows */
2920                     if (shift_state == 2)
2921                         app_flg = !app_flg;
2922
2923                     if (app_flg)
2924                         p += sprintf((char *) p, "\x1BO%c", xkey);
2925                     else
2926                         p += sprintf((char *) p, "\x1B[%c", xkey);
2927                 }
2928                 return p - output;
2929             }
2930         }
2931
2932         /*
2933          * Finally, deal with Return ourselves. (Win95 seems to
2934          * foul it up when Alt is pressed, for some reason.)
2935          */
2936         if (wParam == VK_RETURN) {     /* Return */
2937             *p++ = 0x0D;
2938             *p++ = 0;
2939             return -2;
2940         }
2941
2942         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2943             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2944         else
2945             alt_sum = 0;
2946     }
2947
2948     /* Okay we've done everything interesting; let windows deal with 
2949      * the boring stuff */
2950     {
2951         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2952 #ifdef SHOW_TOASCII_RESULT
2953         if (r == 1 && !key_down) {
2954             if (alt_sum) {
2955                 if (in_utf || dbcs_screenfont)
2956                     debug((", (U+%04x)", alt_sum));
2957                 else
2958                     debug((", LCH(%d)", alt_sum));
2959             } else {
2960                 debug((", ACH(%d)", keys[0]));
2961             }
2962         } else if (r > 0) {
2963             int r1;
2964             debug((", ASC("));
2965             for (r1 = 0; r1 < r; r1++) {
2966                 debug(("%s%d", r1 ? "," : "", keys[r1]));
2967             }
2968             debug((")"));
2969         }
2970 #endif
2971         if (r > 0) {
2972             WCHAR keybuf;
2973             p = output;
2974             for (i = 0; i < r; i++) {
2975                 unsigned char ch = (unsigned char) keys[i];
2976
2977                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2978                     compose_char = ch;
2979                     compose_state++;
2980                     continue;
2981                 }
2982                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2983                     int nc;
2984                     compose_state = 0;
2985
2986                     if ((nc = check_compose(compose_char, ch)) == -1) {
2987                         MessageBeep(MB_ICONHAND);
2988                         return 0;
2989                     }
2990                     keybuf = nc;
2991                     luni_send(&keybuf, 1);
2992                     continue;
2993                 }
2994
2995                 compose_state = 0;
2996
2997                 if (!key_down) {
2998                     if (alt_sum) {
2999                         if (in_utf || dbcs_screenfont) {
3000                             keybuf = alt_sum;
3001                             luni_send(&keybuf, 1);
3002                         } else {
3003                             ch = (char) alt_sum;
3004                             ldisc_send(&ch, 1);
3005                         }
3006                         alt_sum = 0;
3007                     } else
3008                         lpage_send(kbd_codepage, &ch, 1);
3009                 } else {
3010                     static char cbuf[] = "\033 ";
3011                     cbuf[1] = ch;
3012                     lpage_send(kbd_codepage, cbuf + !left_alt,
3013                                1 + !!left_alt);
3014                 }
3015             }
3016
3017             /* This is so the ALT-Numpad and dead keys work correctly. */
3018             keys[0] = 0;
3019
3020             return p - output;
3021         }
3022         /* If we're definitly not building up an ALT-54321 then clear it */
3023         if (!left_alt)
3024             keys[0] = 0;
3025         /* If we will be using alt_sum fix the 256s */
3026         else if (keys[0] && (in_utf || dbcs_screenfont))
3027             keys[0] = 10;
3028     }
3029
3030     /* ALT alone may or may not want to bring up the System menu */
3031     if (wParam == VK_MENU) {
3032         if (cfg.alt_only) {
3033             if (message == WM_SYSKEYDOWN)
3034                 alt_state = 1;
3035             else if (message == WM_SYSKEYUP && alt_state)
3036                 PostMessage(hwnd, WM_CHAR, ' ', 0);
3037             if (message == WM_SYSKEYUP)
3038                 alt_state = 0;
3039         } else
3040             return 0;
3041     } else
3042         alt_state = 0;
3043
3044     return -1;
3045 }
3046
3047 void set_title(char *title)
3048 {
3049     sfree(window_name);
3050     window_name = smalloc(1 + strlen(title));
3051     strcpy(window_name, title);
3052     if (cfg.win_name_always || !IsIconic(hwnd))
3053         SetWindowText(hwnd, title);
3054 }
3055
3056 void set_icon(char *title)
3057 {
3058     sfree(icon_name);
3059     icon_name = smalloc(1 + strlen(title));
3060     strcpy(icon_name, title);
3061     if (!cfg.win_name_always && IsIconic(hwnd))
3062         SetWindowText(hwnd, title);
3063 }
3064
3065 void set_sbar(int total, int start, int page)
3066 {
3067     SCROLLINFO si;
3068
3069     if (!cfg.scrollbar)
3070         return;
3071
3072     si.cbSize = sizeof(si);
3073     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3074     si.nMin = 0;
3075     si.nMax = total - 1;
3076     si.nPage = page;
3077     si.nPos = start;
3078     if (hwnd)
3079         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3080 }
3081
3082 Context get_ctx(void)
3083 {
3084     HDC hdc;
3085     if (hwnd) {
3086         hdc = GetDC(hwnd);
3087         if (hdc && pal)
3088             SelectPalette(hdc, pal, FALSE);
3089         return hdc;
3090     } else
3091         return NULL;
3092 }
3093
3094 void free_ctx(Context ctx)
3095 {
3096     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3097     ReleaseDC(hwnd, ctx);
3098 }
3099
3100 static void real_palette_set(int n, int r, int g, int b)
3101 {
3102     if (pal) {
3103         logpal->palPalEntry[n].peRed = r;
3104         logpal->palPalEntry[n].peGreen = g;
3105         logpal->palPalEntry[n].peBlue = b;
3106         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3107         colours[n] = PALETTERGB(r, g, b);
3108         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3109     } else
3110         colours[n] = RGB(r, g, b);
3111 }
3112
3113 void palette_set(int n, int r, int g, int b)
3114 {
3115     static const int first[21] = {
3116         0, 2, 4, 6, 8, 10, 12, 14,
3117         1, 3, 5, 7, 9, 11, 13, 15,
3118         16, 17, 18, 20, 22
3119     };
3120     real_palette_set(first[n], r, g, b);
3121     if (first[n] >= 18)
3122         real_palette_set(first[n] + 1, r, g, b);
3123     if (pal) {
3124         HDC hdc = get_ctx();
3125         UnrealizeObject(pal);
3126         RealizePalette(hdc);
3127         free_ctx(hdc);
3128     }
3129 }
3130
3131 void palette_reset(void)
3132 {
3133     int i;
3134
3135     for (i = 0; i < NCOLOURS; i++) {
3136         if (pal) {
3137             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3138             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3139             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3140             logpal->palPalEntry[i].peFlags = 0;
3141             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3142                                     defpal[i].rgbtGreen,
3143                                     defpal[i].rgbtBlue);
3144         } else
3145             colours[i] = RGB(defpal[i].rgbtRed,
3146                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3147     }
3148
3149     if (pal) {
3150         HDC hdc;
3151         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3152         hdc = get_ctx();
3153         RealizePalette(hdc);
3154         free_ctx(hdc);
3155     }
3156 }
3157
3158 void write_aclip(char *data, int len, int must_deselect)
3159 {
3160     HGLOBAL clipdata;
3161     void *lock;
3162
3163     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3164     if (!clipdata)
3165         return;
3166     lock = GlobalLock(clipdata);
3167     if (!lock)
3168         return;
3169     memcpy(lock, data, len);
3170     ((unsigned char *) lock)[len] = 0;
3171     GlobalUnlock(clipdata);
3172
3173     if (!must_deselect)
3174         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3175
3176     if (OpenClipboard(hwnd)) {
3177         EmptyClipboard();
3178         SetClipboardData(CF_TEXT, clipdata);
3179         CloseClipboard();
3180     } else
3181         GlobalFree(clipdata);
3182
3183     if (!must_deselect)
3184         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3185 }
3186
3187 /*
3188  * Note: unlike write_aclip() this will not append a nul.
3189  */
3190 void write_clip(wchar_t * data, int len, int must_deselect)
3191 {
3192     HGLOBAL clipdata;
3193     HGLOBAL clipdata2;
3194     int len2;
3195     void *lock, *lock2;
3196
3197     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3198
3199     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3200                            len * sizeof(wchar_t));
3201     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3202
3203     if (!clipdata || !clipdata2) {
3204         if (clipdata)
3205             GlobalFree(clipdata);
3206         if (clipdata2)
3207             GlobalFree(clipdata2);
3208         return;
3209     }
3210     if (!(lock = GlobalLock(clipdata)))
3211         return;
3212     if (!(lock2 = GlobalLock(clipdata2)))
3213         return;
3214
3215     memcpy(lock, data, len * sizeof(wchar_t));
3216     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3217
3218     GlobalUnlock(clipdata);
3219     GlobalUnlock(clipdata2);
3220
3221     if (!must_deselect)
3222         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3223
3224     if (OpenClipboard(hwnd)) {
3225         EmptyClipboard();
3226         SetClipboardData(CF_UNICODETEXT, clipdata);
3227         SetClipboardData(CF_TEXT, clipdata2);
3228         CloseClipboard();
3229     } else {
3230         GlobalFree(clipdata);
3231         GlobalFree(clipdata2);
3232     }
3233
3234     if (!must_deselect)
3235         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3236 }
3237
3238 void get_clip(wchar_t ** p, int *len)
3239 {
3240     static HGLOBAL clipdata = NULL;
3241     static wchar_t *converted = 0;
3242     wchar_t *p2;
3243
3244     if (converted) {
3245         sfree(converted);
3246         converted = 0;
3247     }
3248     if (!p) {
3249         if (clipdata)
3250             GlobalUnlock(clipdata);
3251         clipdata = NULL;
3252         return;
3253     } else if (OpenClipboard(NULL)) {
3254         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3255             CloseClipboard();
3256             *p = GlobalLock(clipdata);
3257             if (*p) {
3258                 for (p2 = *p; *p2; p2++);
3259                 *len = p2 - *p;
3260                 return;
3261             }
3262         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3263             char *s;
3264             int i;
3265             CloseClipboard();
3266             s = GlobalLock(clipdata);
3267             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3268             *p = converted = smalloc(i * sizeof(wchar_t));
3269             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3270             *len = i - 1;
3271             return;
3272         } else
3273             CloseClipboard();
3274     }
3275
3276     *p = NULL;
3277     *len = 0;
3278 }
3279
3280 #if 0
3281 /*
3282  * Move `lines' lines from position `from' to position `to' in the
3283  * window.
3284  */
3285 void optimised_move(int to, int from, int lines)
3286 {
3287     RECT r;
3288     int min, max;
3289
3290     min = (to < from ? to : from);
3291     max = to + from - min;
3292
3293     r.left = 0;
3294     r.right = cols * font_width;
3295     r.top = min * font_height;
3296     r.bottom = (max + lines) * font_height;
3297     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3298 }
3299 #endif
3300
3301 /*
3302  * Print a message box and perform a fatal exit.
3303  */
3304 void fatalbox(char *fmt, ...)
3305 {
3306     va_list ap;
3307     char stuff[200];
3308
3309     va_start(ap, fmt);
3310     vsprintf(stuff, fmt, ap);
3311     va_end(ap);
3312     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3313     exit(1);
3314 }
3315
3316 /*
3317  * Manage window caption / taskbar flashing, if enabled.
3318  * 0 = stop, 1 = maintain, 2 = start
3319  */
3320 static void flash_window(int mode)
3321 {
3322     static long last_flash = 0;
3323     static int flashing = 0;
3324     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3325         /* stop */
3326         if (flashing) {
3327             FlashWindow(hwnd, FALSE);
3328             flashing = 0;
3329         }
3330
3331     } else if (mode == 2) {
3332         /* start */
3333         if (!flashing) {
3334             last_flash = GetTickCount();
3335             flashing = 1;
3336             FlashWindow(hwnd, TRUE);
3337         }
3338
3339     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3340         /* maintain */
3341         if (flashing) {
3342             long now = GetTickCount();
3343             long fdiff = now - last_flash;
3344             if (fdiff < 0 || fdiff > 450) {
3345                 last_flash = now;
3346                 FlashWindow(hwnd, TRUE);        /* toggle */
3347             }
3348         }
3349     }
3350 }
3351
3352 /*
3353  * Beep.
3354  */
3355 void beep(int mode)
3356 {
3357     if (mode == BELL_DEFAULT) {
3358         /*
3359          * For MessageBeep style bells, we want to be careful of
3360          * timing, because they don't have the nice property of
3361          * PlaySound bells that each one cancels the previous
3362          * active one. So we limit the rate to one per 50ms or so.
3363          */
3364         static long lastbeep = 0;
3365         long beepdiff;
3366
3367         beepdiff = GetTickCount() - lastbeep;
3368         if (beepdiff >= 0 && beepdiff < 50)
3369             return;
3370         MessageBeep(MB_OK);
3371         /*
3372          * The above MessageBeep call takes time, so we record the
3373          * time _after_ it finishes rather than before it starts.
3374          */
3375         lastbeep = GetTickCount();
3376     } else if (mode == BELL_WAVEFILE) {
3377         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3378             char buf[sizeof(cfg.bell_wavefile) + 80];
3379             sprintf(buf, "Unable to play sound file\n%s\n"
3380                     "Using default sound instead", cfg.bell_wavefile);
3381             MessageBox(hwnd, buf, "PuTTY Sound Error",
3382                        MB_OK | MB_ICONEXCLAMATION);
3383             cfg.beep = BELL_DEFAULT;
3384         }
3385     }
3386     /* Otherwise, either visual bell or disabled; do nothing here */
3387     if (!has_focus) {
3388         flash_window(2);               /* start */
3389     }
3390 }