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