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