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