]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
609b83731d60d66cb557a9b26b4369e6f8fc8bd7
[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           default:
1497             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1498                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1499             }
1500         }
1501         break;
1502
1503 #define X_POS(l) ((int)(short)LOWORD(l))
1504 #define Y_POS(l) ((int)(short)HIWORD(l))
1505
1506 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1507 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1508 #define WHEEL_DELTA 120
1509       case WM_MOUSEWHEEL:
1510         {
1511             wheel_accumulator += (short) HIWORD(wParam);
1512             wParam = LOWORD(wParam);
1513
1514             /* process events when the threshold is reached */
1515             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1516                 int b;
1517
1518                 /* reduce amount for next time */
1519                 if (wheel_accumulator > 0) {
1520                     b = MBT_WHEEL_UP;
1521                     wheel_accumulator -= WHEEL_DELTA;
1522                 } else if (wheel_accumulator < 0) {
1523                     b = MBT_WHEEL_DOWN;
1524                     wheel_accumulator += WHEEL_DELTA;
1525                 } else
1526                     break;
1527
1528                 if (send_raw_mouse) {
1529                     /* send a mouse-down followed by a mouse up */
1530                     term_mouse(b,
1531                                MA_CLICK,
1532                                TO_CHR_X(X_POS(lParam)),
1533                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1534                                wParam & MK_CONTROL);
1535                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1536                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1537                                wParam & MK_CONTROL);
1538                 } else {
1539                     /* trigger a scroll */
1540                     term_scroll(0,
1541                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1542                 }
1543             }
1544             return 0;
1545         }
1546       case WM_LBUTTONDOWN:
1547       case WM_MBUTTONDOWN:
1548       case WM_RBUTTONDOWN:
1549       case WM_LBUTTONUP:
1550       case WM_MBUTTONUP:
1551       case WM_RBUTTONUP:
1552         {
1553             int button, press;
1554             switch (message) {
1555               case WM_LBUTTONDOWN:
1556                 button = MBT_LEFT;
1557                 press = 1;
1558                 break;
1559               case WM_MBUTTONDOWN:
1560                 button = MBT_MIDDLE;
1561                 press = 1;
1562                 break;
1563               case WM_RBUTTONDOWN:
1564                 button = MBT_RIGHT;
1565                 press = 1;
1566                 break;
1567               case WM_LBUTTONUP:
1568                 button = MBT_LEFT;
1569                 press = 0;
1570                 break;
1571               case WM_MBUTTONUP:
1572                 button = MBT_MIDDLE;
1573                 press = 0;
1574                 break;
1575               case WM_RBUTTONUP:
1576                 button = MBT_RIGHT;
1577                 press = 0;
1578                 break;
1579               default:
1580                 button = press = 0;    /* shouldn't happen */
1581             }
1582             show_mouseptr(1);
1583             if (press) {
1584                 click(button,
1585                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1586                       wParam & MK_SHIFT, wParam & MK_CONTROL);
1587                 SetCapture(hwnd);
1588             } else {
1589                 term_mouse(button, MA_RELEASE,
1590                            TO_CHR_X(X_POS(lParam)),
1591                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1592                            wParam & MK_CONTROL);
1593                 ReleaseCapture();
1594             }
1595         }
1596         return 0;
1597       case WM_MOUSEMOVE:
1598         show_mouseptr(1);
1599         /*
1600          * Add the mouse position and message time to the random
1601          * number noise.
1602          */
1603         noise_ultralight(lParam);
1604
1605         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1606             Mouse_Button b;
1607             if (wParam & MK_LBUTTON)
1608                 b = MBT_LEFT;
1609             else if (wParam & MK_MBUTTON)
1610                 b = MBT_MIDDLE;
1611             else
1612                 b = MBT_RIGHT;
1613             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1614                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1615                        wParam & MK_CONTROL);
1616         }
1617         return 0;
1618       case WM_NCMOUSEMOVE:
1619         show_mouseptr(1);
1620         noise_ultralight(lParam);
1621         return 0;
1622       case WM_IGNORE_CLIP:
1623         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1624         break;
1625       case WM_DESTROYCLIPBOARD:
1626         if (!ignore_clip)
1627             term_deselect();
1628         ignore_clip = FALSE;
1629         return 0;
1630       case WM_PAINT:
1631         {
1632             PAINTSTRUCT p;
1633             HideCaret(hwnd);
1634             hdc = BeginPaint(hwnd, &p);
1635             if (pal) {
1636                 SelectPalette(hdc, pal, TRUE);
1637                 RealizePalette(hdc);
1638             }
1639             term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1640                        p.rcPaint.right, p.rcPaint.bottom);
1641             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1642             SelectObject(hdc, GetStockObject(WHITE_PEN));
1643             EndPaint(hwnd, &p);
1644             ShowCaret(hwnd);
1645         }
1646         return 0;
1647       case WM_NETEVENT:
1648         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1649          * but the only one that's likely to try to overload us is FD_READ.
1650          * This means buffering just one is fine.
1651          */
1652         if (pending_netevent)
1653             enact_pending_netevent();
1654
1655         pending_netevent = TRUE;
1656         pend_netevent_wParam = wParam;
1657         pend_netevent_lParam = lParam;
1658         time(&last_movement);
1659         return 0;
1660       case WM_SETFOCUS:
1661         has_focus = TRUE;
1662         CreateCaret(hwnd, caretbm, font_width, font_height);
1663         ShowCaret(hwnd);
1664         flash_window(0);               /* stop */
1665         compose_state = 0;
1666         term_out();
1667         term_update();
1668         break;
1669       case WM_KILLFOCUS:
1670         show_mouseptr(1);
1671         has_focus = FALSE;
1672         DestroyCaret();
1673         term_out();
1674         term_update();
1675         break;
1676       case WM_IGNORE_SIZE:
1677         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1678         break;
1679       case WM_ENTERSIZEMOVE:
1680         EnableSizeTip(1);
1681         resizing = TRUE;
1682         need_backend_resize = FALSE;
1683         break;
1684       case WM_EXITSIZEMOVE:
1685         EnableSizeTip(0);
1686         resizing = FALSE;
1687         if (need_backend_resize)
1688             back->size();
1689         break;
1690       case WM_SIZING:
1691         {
1692             int width, height, w, h, ew, eh;
1693             LPRECT r = (LPRECT) lParam;
1694
1695             width = r->right - r->left - extra_width;
1696             height = r->bottom - r->top - extra_height;
1697             w = (width + font_width / 2) / font_width;
1698             if (w < 1)
1699                 w = 1;
1700             h = (height + font_height / 2) / font_height;
1701             if (h < 1)
1702                 h = 1;
1703             UpdateSizeTip(hwnd, w, h);
1704             ew = width - w * font_width;
1705             eh = height - h * font_height;
1706             if (ew != 0) {
1707                 if (wParam == WMSZ_LEFT ||
1708                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1709                     r->left += ew;
1710                 else
1711                     r->right -= ew;
1712             }
1713             if (eh != 0) {
1714                 if (wParam == WMSZ_TOP ||
1715                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1716                     r->top += eh;
1717                 else
1718                     r->bottom -= eh;
1719             }
1720             if (ew || eh)
1721                 return 1;
1722             else
1723                 return 0;
1724         }
1725         /* break;  (never reached) */
1726       case WM_SIZE:
1727         if (wParam == SIZE_MINIMIZED) {
1728             SetWindowText(hwnd,
1729                           cfg.win_name_always ? window_name : icon_name);
1730             break;
1731         }
1732         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1733             SetWindowText(hwnd, window_name);
1734         if (!ignore_size) {
1735             int width, height, w, h;
1736 #if 0                                  /* we have fixed this using WM_SIZING now */
1737             int ew, eh;
1738 #endif
1739
1740             width = LOWORD(lParam);
1741             height = HIWORD(lParam);
1742             w = width / font_width;
1743             if (w < 1)
1744                 w = 1;
1745             h = height / font_height;
1746             if (h < 1)
1747                 h = 1;
1748 #if 0                                  /* we have fixed this using WM_SIZING now */
1749             ew = width - w * font_width;
1750             eh = height - h * font_height;
1751             if (ew != 0 || eh != 0) {
1752                 RECT r;
1753                 GetWindowRect(hwnd, &r);
1754                 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1755                 SetWindowPos(hwnd, NULL, 0, 0,
1756                              r.right - r.left - ew, r.bottom - r.top - eh,
1757                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1758             }
1759 #endif
1760             if (w != cols || h != rows || just_reconfigged) {
1761                 term_invalidate();
1762                 term_size(h, w, cfg.savelines);
1763                 /*
1764                  * Don't call back->size in mid-resize. (To prevent
1765                  * massive numbers of resize events getting sent
1766                  * down the connection during an NT opaque drag.)
1767                  */
1768                 if (!resizing)
1769                     back->size();
1770                 else {
1771                     need_backend_resize = TRUE;
1772                     cfg.height = h;
1773                     cfg.width = w;
1774                 }
1775                 just_reconfigged = FALSE;
1776             }
1777         }
1778         if (wParam == SIZE_RESTORED && defered_resize) {
1779             defered_resize = FALSE;
1780             SetWindowPos(hwnd, NULL, 0, 0,
1781                          extra_width + font_width * cfg.width,
1782                          extra_height + font_height * cfg.height,
1783                          SWP_NOACTIVATE | SWP_NOCOPYBITS |
1784                          SWP_NOMOVE | SWP_NOZORDER);
1785         }
1786         ignore_size = FALSE;
1787         return 0;
1788       case WM_VSCROLL:
1789         switch (LOWORD(wParam)) {
1790           case SB_BOTTOM:
1791             term_scroll(-1, 0);
1792             break;
1793           case SB_TOP:
1794             term_scroll(+1, 0);
1795             break;
1796           case SB_LINEDOWN:
1797             term_scroll(0, +1);
1798             break;
1799           case SB_LINEUP:
1800             term_scroll(0, -1);
1801             break;
1802           case SB_PAGEDOWN:
1803             term_scroll(0, +rows / 2);
1804             break;
1805           case SB_PAGEUP:
1806             term_scroll(0, -rows / 2);
1807             break;
1808           case SB_THUMBPOSITION:
1809           case SB_THUMBTRACK:
1810             term_scroll(1, HIWORD(wParam));
1811             break;
1812         }
1813         break;
1814       case WM_PALETTECHANGED:
1815         if ((HWND) wParam != hwnd && pal != NULL) {
1816             HDC hdc = get_ctx();
1817             if (hdc) {
1818                 if (RealizePalette(hdc) > 0)
1819                     UpdateColors(hdc);
1820                 free_ctx(hdc);
1821             }
1822         }
1823         break;
1824       case WM_QUERYNEWPALETTE:
1825         if (pal != NULL) {
1826             HDC hdc = get_ctx();
1827             if (hdc) {
1828                 if (RealizePalette(hdc) > 0)
1829                     UpdateColors(hdc);
1830                 free_ctx(hdc);
1831                 return TRUE;
1832             }
1833         }
1834         return FALSE;
1835       case WM_KEYDOWN:
1836       case WM_SYSKEYDOWN:
1837       case WM_KEYUP:
1838       case WM_SYSKEYUP:
1839         /*
1840          * Add the scan code and keypress timing to the random
1841          * number noise.
1842          */
1843         noise_ultralight(lParam);
1844
1845         /*
1846          * We don't do TranslateMessage since it disassociates the
1847          * resulting CHAR message from the KEYDOWN that sparked it,
1848          * which we occasionally don't want. Instead, we process
1849          * KEYDOWN, and call the Win32 translator functions so that
1850          * we get the translations under _our_ control.
1851          */
1852         {
1853             unsigned char buf[20];
1854             int len;
1855
1856             if (wParam == VK_PROCESSKEY) {
1857                 MSG m;
1858                 m.hwnd = hwnd;
1859                 m.message = WM_KEYDOWN;
1860                 m.wParam = wParam;
1861                 m.lParam = lParam & 0xdfff;
1862                 TranslateMessage(&m);
1863             } else {
1864                 len = TranslateKey(message, wParam, lParam, buf);
1865                 if (len == -1)
1866                     return DefWindowProc(hwnd, message, wParam, lParam);
1867
1868                 if (len != 0) {
1869                     /*
1870                      * We need not bother about stdin backlogs
1871                      * here, because in GUI PuTTY we can't do
1872                      * anything about it anyway; there's no means
1873                      * of asking Windows to hold off on KEYDOWN
1874                      * messages. We _have_ to buffer everything
1875                      * we're sent.
1876                      */
1877                     ldisc_send(buf, len);
1878                     show_mouseptr(0);
1879                 }
1880             }
1881         }
1882         return 0;
1883       case WM_INPUTLANGCHANGE:
1884         {
1885             /* wParam == Font number */
1886             /* lParam == Locale */
1887             char lbuf[20];
1888             HKL NewInputLocale = (HKL) lParam;
1889
1890             // lParam == GetKeyboardLayout(0);
1891
1892             GetLocaleInfo(LOWORD(NewInputLocale),
1893                           LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1894
1895             kbd_codepage = atoi(lbuf);
1896         }
1897         break;
1898       case WM_IME_COMPOSITION:
1899         {
1900             HIMC hIMC;
1901             int n;
1902             char *buff;
1903    
1904             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
1905                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1906
1907             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1908                 break; /* fall back to DefWindowProc */
1909
1910             hIMC = ImmGetContext(hwnd);
1911             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1912
1913             if (n > 0) {
1914                 buff = (char*) smalloc(n);
1915                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1916                 luni_send((unsigned short *)buff, n / 2);
1917                 free(buff);
1918             }
1919             ImmReleaseContext(hwnd, hIMC);
1920             return 1;
1921         }
1922
1923       case WM_IME_CHAR:
1924         if (wParam & 0xFF00) {
1925             unsigned char buf[2];
1926
1927             buf[1] = wParam;
1928             buf[0] = wParam >> 8;
1929             lpage_send(kbd_codepage, buf, 2);
1930         } else {
1931             char c = (unsigned char) wParam;
1932             lpage_send(kbd_codepage, &c, 1);
1933         }
1934         return (0);
1935       case WM_CHAR:
1936       case WM_SYSCHAR:
1937         /*
1938          * Nevertheless, we are prepared to deal with WM_CHAR
1939          * messages, should they crop up. So if someone wants to
1940          * post the things to us as part of a macro manoeuvre,
1941          * we're ready to cope.
1942          */
1943         {
1944             char c = (unsigned char)wParam;
1945             lpage_send(CP_ACP, &c, 1);
1946         }
1947         return 0;
1948       case WM_SETCURSOR:
1949         if (send_raw_mouse) {
1950             SetCursor(LoadCursor(NULL, IDC_ARROW));
1951             return TRUE;
1952         }
1953     }
1954
1955     return DefWindowProc(hwnd, message, wParam, lParam);
1956 }
1957
1958 /*
1959  * Move the system caret. (We maintain one, even though it's
1960  * invisible, for the benefit of blind people: apparently some
1961  * helper software tracks the system caret, so we should arrange to
1962  * have one.)
1963  */
1964 void sys_cursor(int x, int y)
1965 {
1966     COMPOSITIONFORM cf;
1967     HIMC hIMC;
1968
1969     if (!has_focus) return;
1970     
1971     SetCaretPos(x * font_width, y * font_height);
1972
1973     /* IMM calls on Win98 and beyond only */
1974     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1975     
1976     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1977             osVersion.dwMinorVersion == 0) return; /* 95 */
1978
1979     /* we should have the IMM functions */
1980     hIMC = ImmGetContext(hwnd);
1981     cf.dwStyle = CFS_POINT;
1982     cf.ptCurrentPos.x = x * font_width;
1983     cf.ptCurrentPos.y = y * font_height;
1984     ImmSetCompositionWindow(hIMC, &cf);
1985
1986     ImmReleaseContext(hwnd, hIMC);
1987 }
1988
1989 /*
1990  * Draw a line of text in the window, at given character
1991  * coordinates, in given attributes.
1992  *
1993  * We are allowed to fiddle with the contents of `text'.
1994  */
1995 void do_text(Context ctx, int x, int y, char *text, int len,
1996              unsigned long attr, int lattr)
1997 {
1998     COLORREF fg, bg, t;
1999     int nfg, nbg, nfont;
2000     HDC hdc = ctx;
2001     RECT line_box;
2002     int force_manual_underline = 0;
2003     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2004     int char_width = fnt_width;
2005     int text_adjust = 0;
2006     static int *IpDx = 0, IpDxLEN = 0;
2007
2008     if (attr & ATTR_WIDE)
2009         char_width *= 2;
2010
2011     if (len > IpDxLEN || IpDx[0] != char_width) {
2012         int i;
2013         if (len > IpDxLEN) {
2014             sfree(IpDx);
2015             IpDx = smalloc((len + 16) * sizeof(int));
2016             IpDxLEN = (len + 16);
2017         }
2018         for (i = 0; i < IpDxLEN; i++)
2019             IpDx[i] = char_width;
2020     }
2021
2022     x *= fnt_width;
2023     y *= font_height;
2024
2025     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2026         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2027         attr ^= ATTR_CUR_XOR;
2028     }
2029
2030     nfont = 0;
2031     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2032         /* Assume a poorman font is borken in other ways too. */
2033         lattr = LATTR_WIDE;
2034     } else
2035         switch (lattr) {
2036           case LATTR_NORM:
2037             break;
2038           case LATTR_WIDE:
2039             nfont |= FONT_WIDE;
2040             break;
2041           default:
2042             nfont |= FONT_WIDE + FONT_HIGH;
2043             break;
2044         }
2045
2046     /* Special hack for the VT100 linedraw glyphs. */
2047     if ((attr & CSET_MASK) == 0x2300) {
2048         if (!dbcs_screenfont &&
2049             text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2050             switch ((unsigned char) (text[0])) {
2051               case 0xBA:
2052                 text_adjust = -2 * font_height / 5;
2053                 break;
2054               case 0xBB:
2055                 text_adjust = -1 * font_height / 5;
2056                 break;
2057               case 0xBC:
2058                 text_adjust = font_height / 5;
2059                 break;
2060               case 0xBD:
2061                 text_adjust = 2 * font_height / 5;
2062                 break;
2063             }
2064             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2065                 text_adjust *= 2;
2066             attr &= ~CSET_MASK;
2067             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2068             attr |= (unitab_xterm['q'] & CSET_MASK);
2069             if (attr & ATTR_UNDER) {
2070                 attr &= ~ATTR_UNDER;
2071                 force_manual_underline = 1;
2072             }
2073         }
2074     }
2075
2076     /* Anything left as an original character set is unprintable. */
2077     if (DIRECT_CHAR(attr)) {
2078         attr &= ~CSET_MASK;
2079         attr |= 0xFF00;
2080         memset(text, 0xFF, len);
2081     }
2082
2083     /* OEM CP */
2084     if ((attr & CSET_MASK) == ATTR_OEMCP)
2085         nfont |= FONT_OEM;
2086
2087     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2088     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2089     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2090         nfont |= FONT_BOLD;
2091     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2092         nfont |= FONT_UNDERLINE;
2093     another_font(nfont);
2094     if (!fonts[nfont]) {
2095         if (nfont & FONT_UNDERLINE)
2096             force_manual_underline = 1;
2097         /* Don't do the same for manual bold, it could be bad news. */
2098
2099         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2100     }
2101     another_font(nfont);
2102     if (!fonts[nfont])
2103         nfont = FONT_NORMAL;
2104     if (attr & ATTR_REVERSE) {
2105         t = nfg;
2106         nfg = nbg;
2107         nbg = t;
2108     }
2109     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2110         nfg++;
2111     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2112         nbg++;
2113     fg = colours[nfg];
2114     bg = colours[nbg];
2115     SelectObject(hdc, fonts[nfont]);
2116     SetTextColor(hdc, fg);
2117     SetBkColor(hdc, bg);
2118     SetBkMode(hdc, OPAQUE);
2119     line_box.left = x;
2120     line_box.top = y;
2121     line_box.right = x + char_width * len;
2122     line_box.bottom = y + font_height;
2123
2124     /* We're using a private area for direct to font. (512 chars.) */
2125     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2126         /* Ho Hum, dbcs fonts are a PITA! */
2127         /* To display on W9x I have to convert to UCS */
2128         static wchar_t *uni_buf = 0;
2129         static int uni_len = 0;
2130         int nlen;
2131         if (len > uni_len) {
2132             sfree(uni_buf);
2133             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2134         }
2135         nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2136                                    text, len, uni_buf, uni_len);
2137
2138         if (nlen <= 0)
2139             return;                    /* Eeek! */
2140
2141         ExtTextOutW(hdc, x,
2142                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2143                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2144         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2145             SetBkMode(hdc, TRANSPARENT);
2146             ExtTextOutW(hdc, x - 1,
2147                         y - font_height * (lattr ==
2148                                            LATTR_BOT) + text_adjust,
2149                         ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2150         }
2151     } else if (DIRECT_FONT(attr)) {
2152         ExtTextOut(hdc, x,
2153                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2154                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2155         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2156             SetBkMode(hdc, TRANSPARENT);
2157
2158             /* GRR: This draws the character outside it's box and can leave
2159              * 'droppings' even with the clip box! I suppose I could loop it
2160              * one character at a time ... yuk. 
2161              * 
2162              * Or ... I could do a test print with "W", and use +1 or -1 for this
2163              * shift depending on if the leftmost column is blank...
2164              */
2165             ExtTextOut(hdc, x - 1,
2166                        y - font_height * (lattr ==
2167                                           LATTR_BOT) + text_adjust,
2168                        ETO_CLIPPED, &line_box, text, len, IpDx);
2169         }
2170     } else {
2171         /* And 'normal' unicode characters */
2172         static WCHAR *wbuf = NULL;
2173         static int wlen = 0;
2174         int i;
2175         if (wlen < len) {
2176             sfree(wbuf);
2177             wlen = len;
2178             wbuf = smalloc(wlen * sizeof(WCHAR));
2179         }
2180         for (i = 0; i < len; i++)
2181             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2182
2183         ExtTextOutW(hdc, x,
2184                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2185                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2186
2187         /* And the shadow bold hack. */
2188         if (bold_mode == BOLD_SHADOW) {
2189             SetBkMode(hdc, TRANSPARENT);
2190             ExtTextOutW(hdc, x - 1,
2191                         y - font_height * (lattr ==
2192                                            LATTR_BOT) + text_adjust,
2193                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2194         }
2195     }
2196     if (lattr != LATTR_TOP && (force_manual_underline ||
2197                                (und_mode == UND_LINE
2198                                 && (attr & ATTR_UNDER)))) {
2199         HPEN oldpen;
2200         int dec = descent;
2201         if (lattr == LATTR_BOT)
2202             dec = dec * 2 - font_height;
2203
2204         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2205         MoveToEx(hdc, x, y + dec, NULL);
2206         LineTo(hdc, x + len * char_width, y + dec);
2207         oldpen = SelectObject(hdc, oldpen);
2208         DeleteObject(oldpen);
2209     }
2210 }
2211
2212 void do_cursor(Context ctx, int x, int y, char *text, int len,
2213                unsigned long attr, int lattr)
2214 {
2215
2216     int fnt_width;
2217     int char_width;
2218     HDC hdc = ctx;
2219     int ctype = cfg.cursor_type;
2220
2221     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2222         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2223             do_text(ctx, x, y, text, len, attr, lattr);
2224             return;
2225         }
2226         ctype = 2;
2227         attr |= TATTR_RIGHTCURS;
2228     }
2229
2230     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2231     if (attr & ATTR_WIDE)
2232         char_width *= 2;
2233     x *= fnt_width;
2234     y *= font_height;
2235
2236     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2237         POINT pts[5];
2238         HPEN oldpen;
2239         pts[0].x = pts[1].x = pts[4].x = x;
2240         pts[2].x = pts[3].x = x + char_width - 1;
2241         pts[0].y = pts[3].y = pts[4].y = y;
2242         pts[1].y = pts[2].y = y + font_height - 1;
2243         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2244         Polyline(hdc, pts, 5);
2245         oldpen = SelectObject(hdc, oldpen);
2246         DeleteObject(oldpen);
2247     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2248         int startx, starty, dx, dy, length, i;
2249         if (ctype == 1) {
2250             startx = x;
2251             starty = y + descent;
2252             dx = 1;
2253             dy = 0;
2254             length = char_width;
2255         } else {
2256             int xadjust = 0;
2257             if (attr & TATTR_RIGHTCURS)
2258                 xadjust = char_width - 1;
2259             startx = x + xadjust;
2260             starty = y;
2261             dx = 0;
2262             dy = 1;
2263             length = font_height;
2264         }
2265         if (attr & TATTR_ACTCURS) {
2266             HPEN oldpen;
2267             oldpen =
2268                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2269             MoveToEx(hdc, startx, starty, NULL);
2270             LineTo(hdc, startx + dx * length, starty + dy * length);
2271             oldpen = SelectObject(hdc, oldpen);
2272             DeleteObject(oldpen);
2273         } else {
2274             for (i = 0; i < length; i++) {
2275                 if (i % 2 == 0) {
2276                     SetPixel(hdc, startx, starty, colours[23]);
2277                 }
2278                 startx += dx;
2279                 starty += dy;
2280             }
2281         }
2282     }
2283 }
2284
2285 /*
2286  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2287  * codes. Returns number of bytes used or zero to drop the message
2288  * or -1 to forward the message to windows.
2289  */
2290 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2291                         unsigned char *output)
2292 {
2293     BYTE keystate[256];
2294     int scan, left_alt = 0, key_down, shift_state;
2295     int r, i, code;
2296     unsigned char *p = output;
2297     static int alt_state = 0;
2298     static int alt_sum = 0;
2299
2300     HKL kbd_layout = GetKeyboardLayout(0);
2301
2302     static WORD keys[3];
2303     static int compose_char = 0;
2304     static WPARAM compose_key = 0;
2305
2306     r = GetKeyboardState(keystate);
2307     if (!r)
2308         memset(keystate, 0, sizeof(keystate));
2309     else {
2310 #if 0
2311 #define SHOW_TOASCII_RESULT
2312         {                              /* Tell us all about key events */
2313             static BYTE oldstate[256];
2314             static int first = 1;
2315             static int scan;
2316             int ch;
2317             if (first)
2318                 memcpy(oldstate, keystate, sizeof(oldstate));
2319             first = 0;
2320
2321             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2322                 debug(("+"));
2323             } else if ((HIWORD(lParam) & KF_UP)
2324                        && scan == (HIWORD(lParam) & 0xFF)) {
2325                 debug((". U"));
2326             } else {
2327                 debug((".\n"));
2328                 if (wParam >= VK_F1 && wParam <= VK_F20)
2329                     debug(("K_F%d", wParam + 1 - VK_F1));
2330                 else
2331                     switch (wParam) {
2332                       case VK_SHIFT:
2333                         debug(("SHIFT"));
2334                         break;
2335                       case VK_CONTROL:
2336                         debug(("CTRL"));
2337                         break;
2338                       case VK_MENU:
2339                         debug(("ALT"));
2340                         break;
2341                       default:
2342                         debug(("VK_%02x", wParam));
2343                     }
2344                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2345                     debug(("*"));
2346                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2347
2348                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2349                 if (ch >= ' ' && ch <= '~')
2350                     debug((", '%c'", ch));
2351                 else if (ch)
2352                     debug((", $%02x", ch));
2353
2354                 if (keys[0])
2355                     debug((", KB0=%02x", keys[0]));
2356                 if (keys[1])
2357                     debug((", KB1=%02x", keys[1]));
2358                 if (keys[2])
2359                     debug((", KB2=%02x", keys[2]));
2360
2361                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2362                     debug((", S"));
2363                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2364                     debug((", C"));
2365                 if ((HIWORD(lParam) & KF_EXTENDED))
2366                     debug((", E"));
2367                 if ((HIWORD(lParam) & KF_UP))
2368                     debug((", U"));
2369             }
2370
2371             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2372             else if ((HIWORD(lParam) & KF_UP))
2373                 oldstate[wParam & 0xFF] ^= 0x80;
2374             else
2375                 oldstate[wParam & 0xFF] ^= 0x81;
2376
2377             for (ch = 0; ch < 256; ch++)
2378                 if (oldstate[ch] != keystate[ch])
2379                     debug((", M%02x=%02x", ch, keystate[ch]));
2380
2381             memcpy(oldstate, keystate, sizeof(oldstate));
2382         }
2383 #endif
2384
2385         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2386             keystate[VK_RMENU] = keystate[VK_MENU];
2387         }
2388
2389
2390         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2391         if ((cfg.funky_type == 3 ||
2392              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2393             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2394
2395             wParam = VK_EXECUTE;
2396
2397             /* UnToggle NUMLock */
2398             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2399                 keystate[VK_NUMLOCK] ^= 1;
2400         }
2401
2402         /* And write back the 'adjusted' state */
2403         SetKeyboardState(keystate);
2404     }
2405
2406     /* Disable Auto repeat if required */
2407     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2408         return 0;
2409
2410     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2411         left_alt = 1;
2412
2413     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2414
2415     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2416     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2417         if (cfg.ctrlaltkeys)
2418             keystate[VK_MENU] = 0;
2419         else {
2420             keystate[VK_RMENU] = 0x80;
2421             left_alt = 0;
2422         }
2423     }
2424
2425     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2426     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2427         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2428
2429     /* Note if AltGr was pressed and if it was used as a compose key */
2430     if (!compose_state) {
2431         compose_key = 0x100;
2432         if (cfg.compose_key) {
2433             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2434                 compose_key = wParam;
2435         }
2436         if (wParam == VK_APPS)
2437             compose_key = wParam;
2438     }
2439
2440     if (wParam == compose_key) {
2441         if (compose_state == 0
2442             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2443                 1;
2444         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2445             compose_state = 2;
2446         else
2447             compose_state = 0;
2448     } else if (compose_state == 1 && wParam != VK_CONTROL)
2449         compose_state = 0;
2450
2451     /* 
2452      * Record that we pressed key so the scroll window can be reset, but
2453      * be careful to avoid Shift-UP/Down
2454      */
2455     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2456         seen_key_event = 1;
2457     }
2458
2459     /* Make sure we're not pasting */
2460     if (key_down)
2461         term_nopaste();
2462
2463     if (compose_state > 1 && left_alt)
2464         compose_state = 0;
2465
2466     /* Sanitize the number pad if not using a PC NumPad */
2467     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2468                      && cfg.funky_type != 2)
2469         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2470         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2471             int nParam = 0;
2472             switch (wParam) {
2473               case VK_INSERT:
2474                 nParam = VK_NUMPAD0;
2475                 break;
2476               case VK_END:
2477                 nParam = VK_NUMPAD1;
2478                 break;
2479               case VK_DOWN:
2480                 nParam = VK_NUMPAD2;
2481                 break;
2482               case VK_NEXT:
2483                 nParam = VK_NUMPAD3;
2484                 break;
2485               case VK_LEFT:
2486                 nParam = VK_NUMPAD4;
2487                 break;
2488               case VK_CLEAR:
2489                 nParam = VK_NUMPAD5;
2490                 break;
2491               case VK_RIGHT:
2492                 nParam = VK_NUMPAD6;
2493                 break;
2494               case VK_HOME:
2495                 nParam = VK_NUMPAD7;
2496                 break;
2497               case VK_UP:
2498                 nParam = VK_NUMPAD8;
2499                 break;
2500               case VK_PRIOR:
2501                 nParam = VK_NUMPAD9;
2502                 break;
2503               case VK_DELETE:
2504                 nParam = VK_DECIMAL;
2505                 break;
2506             }
2507             if (nParam) {
2508                 if (keystate[VK_NUMLOCK] & 1)
2509                     shift_state |= 1;
2510                 wParam = nParam;
2511             }
2512         }
2513     }
2514
2515     /* If a key is pressed and AltGr is not active */
2516     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2517         /* Okay, prepare for most alts then ... */
2518         if (left_alt)
2519             *p++ = '\033';
2520
2521         /* Lets see if it's a pattern we know all about ... */
2522         if (wParam == VK_PRIOR && shift_state == 1) {
2523             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2524             return 0;
2525         }
2526         if (wParam == VK_NEXT && shift_state == 1) {
2527             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2528             return 0;
2529         }
2530         if (wParam == VK_INSERT && shift_state == 1) {
2531             term_do_paste();
2532             return 0;
2533         }
2534         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2535             return -1;
2536         }
2537         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2538             alt_state = 0;
2539             PostMessage(hwnd, WM_CHAR, ' ', 0);
2540             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2541             return -1;
2542         }
2543         /* Control-Numlock for app-keypad mode switch */
2544         if (wParam == VK_PAUSE && shift_state == 2) {
2545             app_keypad_keys ^= 1;
2546             return 0;
2547         }
2548
2549         /* Nethack keypad */
2550         if (cfg.nethack_keypad && !left_alt) {
2551             switch (wParam) {
2552               case VK_NUMPAD1:
2553                 *p++ = shift_state ? 'B' : 'b';
2554                 return p - output;
2555               case VK_NUMPAD2:
2556                 *p++ = shift_state ? 'J' : 'j';
2557                 return p - output;
2558               case VK_NUMPAD3:
2559                 *p++ = shift_state ? 'N' : 'n';
2560                 return p - output;
2561               case VK_NUMPAD4:
2562                 *p++ = shift_state ? 'H' : 'h';
2563                 return p - output;
2564               case VK_NUMPAD5:
2565                 *p++ = shift_state ? '.' : '.';
2566                 return p - output;
2567               case VK_NUMPAD6:
2568                 *p++ = shift_state ? 'L' : 'l';
2569                 return p - output;
2570               case VK_NUMPAD7:
2571                 *p++ = shift_state ? 'Y' : 'y';
2572                 return p - output;
2573               case VK_NUMPAD8:
2574                 *p++ = shift_state ? 'K' : 'k';
2575                 return p - output;
2576               case VK_NUMPAD9:
2577                 *p++ = shift_state ? 'U' : 'u';
2578                 return p - output;
2579             }
2580         }
2581
2582         /* Application Keypad */
2583         if (!left_alt) {
2584             int xkey = 0;
2585
2586             if (cfg.funky_type == 3 ||
2587                 (cfg.funky_type <= 1 &&
2588                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2589                   case VK_EXECUTE:
2590                     xkey = 'P';
2591                     break;
2592                   case VK_DIVIDE:
2593                     xkey = 'Q';
2594                     break;
2595                   case VK_MULTIPLY:
2596                     xkey = 'R';
2597                     break;
2598                   case VK_SUBTRACT:
2599                     xkey = 'S';
2600                     break;
2601                 }
2602             if (app_keypad_keys && !cfg.no_applic_k)
2603                 switch (wParam) {
2604                   case VK_NUMPAD0:
2605                     xkey = 'p';
2606                     break;
2607                   case VK_NUMPAD1:
2608                     xkey = 'q';
2609                     break;
2610                   case VK_NUMPAD2:
2611                     xkey = 'r';
2612                     break;
2613                   case VK_NUMPAD3:
2614                     xkey = 's';
2615                     break;
2616                   case VK_NUMPAD4:
2617                     xkey = 't';
2618                     break;
2619                   case VK_NUMPAD5:
2620                     xkey = 'u';
2621                     break;
2622                   case VK_NUMPAD6:
2623                     xkey = 'v';
2624                     break;
2625                   case VK_NUMPAD7:
2626                     xkey = 'w';
2627                     break;
2628                   case VK_NUMPAD8:
2629                     xkey = 'x';
2630                     break;
2631                   case VK_NUMPAD9:
2632                     xkey = 'y';
2633                     break;
2634
2635                   case VK_DECIMAL:
2636                     xkey = 'n';
2637                     break;
2638                   case VK_ADD:
2639                     if (cfg.funky_type == 2) {
2640                         if (shift_state)
2641                             xkey = 'l';
2642                         else
2643                             xkey = 'k';
2644                     } else if (shift_state)
2645                         xkey = 'm';
2646                     else
2647                         xkey = 'l';
2648                     break;
2649
2650                   case VK_DIVIDE:
2651                     if (cfg.funky_type == 2)
2652                         xkey = 'o';
2653                     break;
2654                   case VK_MULTIPLY:
2655                     if (cfg.funky_type == 2)
2656                         xkey = 'j';
2657                     break;
2658                   case VK_SUBTRACT:
2659                     if (cfg.funky_type == 2)
2660                         xkey = 'm';
2661                     break;
2662
2663                   case VK_RETURN:
2664                     if (HIWORD(lParam) & KF_EXTENDED)
2665                         xkey = 'M';
2666                     break;
2667                 }
2668             if (xkey) {
2669                 if (vt52_mode) {
2670                     if (xkey >= 'P' && xkey <= 'S')
2671                         p += sprintf((char *) p, "\x1B%c", xkey);
2672                     else
2673                         p += sprintf((char *) p, "\x1B?%c", xkey);
2674                 } else
2675                     p += sprintf((char *) p, "\x1BO%c", xkey);
2676                 return p - output;
2677             }
2678         }
2679
2680         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
2681             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2682             *p++ = 0;
2683             return -2;
2684         }
2685         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
2686             *p++ = 0x1B;
2687             *p++ = '[';
2688             *p++ = 'Z';
2689             return p - output;
2690         }
2691         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
2692             *p++ = 0;
2693             return p - output;
2694         }
2695         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
2696             *p++ = 160;
2697             return p - output;
2698         }
2699         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
2700             *p++ = 3;
2701             *p++ = 0;
2702             return -2;
2703         }
2704         if (wParam == VK_PAUSE) {      /* Break/Pause */
2705             *p++ = 26;
2706             *p++ = 0;
2707             return -2;
2708         }
2709         /* Control-2 to Control-8 are special */
2710         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2711             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2712             return p - output;
2713         }
2714         if (shift_state == 2 && wParam == 0xBD) {
2715             *p++ = 0x1F;
2716             return p - output;
2717         }
2718         if (shift_state == 2 && wParam == 0xDF) {
2719             *p++ = 0x1C;
2720             return p - output;
2721         }
2722         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2723             *p++ = '\r';
2724             *p++ = '\n';
2725             return p - output;
2726         }
2727
2728         /*
2729          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2730          * for integer decimal nn.)
2731          *
2732          * We also deal with the weird ones here. Linux VCs replace F1
2733          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2734          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2735          * respectively.
2736          */
2737         code = 0;
2738         switch (wParam) {
2739           case VK_F1:
2740             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2741             break;
2742           case VK_F2:
2743             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2744             break;
2745           case VK_F3:
2746             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2747             break;
2748           case VK_F4:
2749             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2750             break;
2751           case VK_F5:
2752             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2753             break;
2754           case VK_F6:
2755             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2756             break;
2757           case VK_F7:
2758             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2759             break;
2760           case VK_F8:
2761             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2762             break;
2763           case VK_F9:
2764             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2765             break;
2766           case VK_F10:
2767             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2768             break;
2769           case VK_F11:
2770             code = 23;
2771             break;
2772           case VK_F12:
2773             code = 24;
2774             break;
2775           case VK_F13:
2776             code = 25;
2777             break;
2778           case VK_F14:
2779             code = 26;
2780             break;
2781           case VK_F15:
2782             code = 28;
2783             break;
2784           case VK_F16:
2785             code = 29;
2786             break;
2787           case VK_F17:
2788             code = 31;
2789             break;
2790           case VK_F18:
2791             code = 32;
2792             break;
2793           case VK_F19:
2794             code = 33;
2795             break;
2796           case VK_F20:
2797             code = 34;
2798             break;
2799           case VK_HOME:
2800             code = 1;
2801             break;
2802           case VK_INSERT:
2803             code = 2;
2804             break;
2805           case VK_DELETE:
2806             code = 3;
2807             break;
2808           case VK_END:
2809             code = 4;
2810             break;
2811           case VK_PRIOR:
2812             code = 5;
2813             break;
2814           case VK_NEXT:
2815             code = 6;
2816             break;
2817         }
2818         /* Reorder edit keys to physical order */
2819         if (cfg.funky_type == 3 && code <= 6)
2820             code = "\0\2\1\4\5\3\6"[code];
2821
2822         if (vt52_mode && code > 0 && code <= 6) {
2823             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2824             return p - output;
2825         }
2826
2827         if (cfg.funky_type == 5 &&     /* SCO function keys */
2828             code >= 11 && code <= 34) {
2829             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2830             int index = 0;
2831             switch (wParam) {
2832               case VK_F1: index = 0; break;
2833               case VK_F2: index = 1; break;
2834               case VK_F3: index = 2; break;
2835               case VK_F4: index = 3; break;
2836               case VK_F5: index = 4; break;
2837               case VK_F6: index = 5; break;
2838               case VK_F7: index = 6; break;
2839               case VK_F8: index = 7; break;
2840               case VK_F9: index = 8; break;
2841               case VK_F10: index = 9; break;
2842               case VK_F11: index = 10; break;
2843               case VK_F12: index = 11; break;
2844             }
2845             if (keystate[VK_SHIFT] & 0x80) index += 12;
2846             if (keystate[VK_CONTROL] & 0x80) index += 24;
2847             p += sprintf((char *) p, "\x1B[%c", codes[index]);
2848             return p - output;
2849         }
2850         if (cfg.funky_type == 5 &&     /* SCO small keypad */
2851             code >= 1 && code <= 6) {
2852             char codes[] = "HL.FIG";
2853             if (code == 3) {
2854                 *p++ = '\x7F';
2855             } else {
2856                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2857             }
2858             return p - output;
2859         }
2860         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2861             int offt = 0;
2862             if (code > 15)
2863                 offt++;
2864             if (code > 21)
2865                 offt++;
2866             if (vt52_mode)
2867                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2868             else
2869                 p +=
2870                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2871             return p - output;
2872         }
2873         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2874             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2875             return p - output;
2876         }
2877         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2878             if (vt52_mode)
2879                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2880             else
2881                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2882             return p - output;
2883         }
2884         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2885             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2886             return p - output;
2887         }
2888         if (code) {
2889             p += sprintf((char *) p, "\x1B[%d~", code);
2890             return p - output;
2891         }
2892
2893         /*
2894          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2895          * some reason seems to send VK_CLEAR to Windows...).
2896          */
2897         {
2898             char xkey = 0;
2899             switch (wParam) {
2900               case VK_UP:
2901                 xkey = 'A';
2902                 break;
2903               case VK_DOWN:
2904                 xkey = 'B';
2905                 break;
2906               case VK_RIGHT:
2907                 xkey = 'C';
2908                 break;
2909               case VK_LEFT:
2910                 xkey = 'D';
2911                 break;
2912               case VK_CLEAR:
2913                 xkey = 'G';
2914                 break;
2915             }
2916             if (xkey) {
2917                 if (vt52_mode)
2918                     p += sprintf((char *) p, "\x1B%c", xkey);
2919                 else {
2920                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2921                     /* VT100 & VT102 manuals both state the app cursor keys
2922                      * only work if the app keypad is on.
2923                      */
2924                     if (!app_keypad_keys)
2925                         app_flg = 0;
2926                     /* Useful mapping of Ctrl-arrows */
2927                     if (shift_state == 2)
2928                         app_flg = !app_flg;
2929
2930                     if (app_flg)
2931                         p += sprintf((char *) p, "\x1BO%c", xkey);
2932                     else
2933                         p += sprintf((char *) p, "\x1B[%c", xkey);
2934                 }
2935                 return p - output;
2936             }
2937         }
2938
2939         /*
2940          * Finally, deal with Return ourselves. (Win95 seems to
2941          * foul it up when Alt is pressed, for some reason.)
2942          */
2943         if (wParam == VK_RETURN) {     /* Return */
2944             *p++ = 0x0D;
2945             *p++ = 0;
2946             return -2;
2947         }
2948
2949         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2950             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2951         else
2952             alt_sum = 0;
2953     }
2954
2955     /* Okay we've done everything interesting; let windows deal with 
2956      * the boring stuff */
2957     {
2958         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2959 #ifdef SHOW_TOASCII_RESULT
2960         if (r == 1 && !key_down) {
2961             if (alt_sum) {
2962                 if (in_utf || dbcs_screenfont)
2963                     debug((", (U+%04x)", alt_sum));
2964                 else
2965                     debug((", LCH(%d)", alt_sum));
2966             } else {
2967                 debug((", ACH(%d)", keys[0]));
2968             }
2969         } else if (r > 0) {
2970             int r1;
2971             debug((", ASC("));
2972             for (r1 = 0; r1 < r; r1++) {
2973                 debug(("%s%d", r1 ? "," : "", keys[r1]));
2974             }
2975             debug((")"));
2976         }
2977 #endif
2978         if (r > 0) {
2979             WCHAR keybuf;
2980             p = output;
2981             for (i = 0; i < r; i++) {
2982                 unsigned char ch = (unsigned char) keys[i];
2983
2984                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2985                     compose_char = ch;
2986                     compose_state++;
2987                     continue;
2988                 }
2989                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2990                     int nc;
2991                     compose_state = 0;
2992
2993                     if ((nc = check_compose(compose_char, ch)) == -1) {
2994                         MessageBeep(MB_ICONHAND);
2995                         return 0;
2996                     }
2997                     keybuf = nc;
2998                     luni_send(&keybuf, 1);
2999                     continue;
3000                 }
3001
3002                 compose_state = 0;
3003
3004                 if (!key_down) {
3005                     if (alt_sum) {
3006                         if (in_utf || dbcs_screenfont) {
3007                             keybuf = alt_sum;
3008                             luni_send(&keybuf, 1);
3009                         } else {
3010                             ch = (char) alt_sum;
3011                             /*
3012                              * We need not bother about stdin
3013                              * backlogs here, because in GUI PuTTY
3014                              * we can't do anything about it
3015                              * anyway; there's no means of asking
3016                              * Windows to hold off on KEYDOWN
3017                              * messages. We _have_ to buffer
3018                              * everything we're sent.
3019                              */
3020                             ldisc_send(&ch, 1);
3021                         }
3022                         alt_sum = 0;
3023                     } else
3024                         lpage_send(kbd_codepage, &ch, 1);
3025                 } else {
3026                     static char cbuf[] = "\033 ";
3027                     cbuf[1] = ch;
3028                     lpage_send(kbd_codepage, cbuf + !left_alt,
3029                                1 + !!left_alt);
3030                 }
3031                 show_mouseptr(0);
3032             }
3033
3034             /* This is so the ALT-Numpad and dead keys work correctly. */
3035             keys[0] = 0;
3036
3037             return p - output;
3038         }
3039         /* If we're definitly not building up an ALT-54321 then clear it */
3040         if (!left_alt)
3041             keys[0] = 0;
3042         /* If we will be using alt_sum fix the 256s */
3043         else if (keys[0] && (in_utf || dbcs_screenfont))
3044             keys[0] = 10;
3045     }
3046
3047     /* ALT alone may or may not want to bring up the System menu */
3048     if (wParam == VK_MENU) {
3049         if (cfg.alt_only) {
3050             if (message == WM_SYSKEYDOWN)
3051                 alt_state = 1;
3052             else if (message == WM_SYSKEYUP && alt_state)
3053                 PostMessage(hwnd, WM_CHAR, ' ', 0);
3054             if (message == WM_SYSKEYUP)
3055                 alt_state = 0;
3056         } else
3057             return 0;
3058     } else
3059         alt_state = 0;
3060
3061     return -1;
3062 }
3063
3064 void set_title(char *title)
3065 {
3066     sfree(window_name);
3067     window_name = smalloc(1 + strlen(title));
3068     strcpy(window_name, title);
3069     if (cfg.win_name_always || !IsIconic(hwnd))
3070         SetWindowText(hwnd, title);
3071 }
3072
3073 void set_icon(char *title)
3074 {
3075     sfree(icon_name);
3076     icon_name = smalloc(1 + strlen(title));
3077     strcpy(icon_name, title);
3078     if (!cfg.win_name_always && IsIconic(hwnd))
3079         SetWindowText(hwnd, title);
3080 }
3081
3082 void set_sbar(int total, int start, int page)
3083 {
3084     SCROLLINFO si;
3085
3086     if (!cfg.scrollbar)
3087         return;
3088
3089     si.cbSize = sizeof(si);
3090     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3091     si.nMin = 0;
3092     si.nMax = total - 1;
3093     si.nPage = page;
3094     si.nPos = start;
3095     if (hwnd)
3096         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3097 }
3098
3099 Context get_ctx(void)
3100 {
3101     HDC hdc;
3102     if (hwnd) {
3103         hdc = GetDC(hwnd);
3104         if (hdc && pal)
3105             SelectPalette(hdc, pal, FALSE);
3106         return hdc;
3107     } else
3108         return NULL;
3109 }
3110
3111 void free_ctx(Context ctx)
3112 {
3113     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3114     ReleaseDC(hwnd, ctx);
3115 }
3116
3117 static void real_palette_set(int n, int r, int g, int b)
3118 {
3119     if (pal) {
3120         logpal->palPalEntry[n].peRed = r;
3121         logpal->palPalEntry[n].peGreen = g;
3122         logpal->palPalEntry[n].peBlue = b;
3123         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3124         colours[n] = PALETTERGB(r, g, b);
3125         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3126     } else
3127         colours[n] = RGB(r, g, b);
3128 }
3129
3130 void palette_set(int n, int r, int g, int b)
3131 {
3132     static const int first[21] = {
3133         0, 2, 4, 6, 8, 10, 12, 14,
3134         1, 3, 5, 7, 9, 11, 13, 15,
3135         16, 17, 18, 20, 22
3136     };
3137     real_palette_set(first[n], r, g, b);
3138     if (first[n] >= 18)
3139         real_palette_set(first[n] + 1, r, g, b);
3140     if (pal) {
3141         HDC hdc = get_ctx();
3142         UnrealizeObject(pal);
3143         RealizePalette(hdc);
3144         free_ctx(hdc);
3145     }
3146 }
3147
3148 void palette_reset(void)
3149 {
3150     int i;
3151
3152     for (i = 0; i < NCOLOURS; i++) {
3153         if (pal) {
3154             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3155             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3156             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3157             logpal->palPalEntry[i].peFlags = 0;
3158             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3159                                     defpal[i].rgbtGreen,
3160                                     defpal[i].rgbtBlue);
3161         } else
3162             colours[i] = RGB(defpal[i].rgbtRed,
3163                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3164     }
3165
3166     if (pal) {
3167         HDC hdc;
3168         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3169         hdc = get_ctx();
3170         RealizePalette(hdc);
3171         free_ctx(hdc);
3172     }
3173 }
3174
3175 void write_aclip(char *data, int len, int must_deselect)
3176 {
3177     HGLOBAL clipdata;
3178     void *lock;
3179
3180     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3181     if (!clipdata)
3182         return;
3183     lock = GlobalLock(clipdata);
3184     if (!lock)
3185         return;
3186     memcpy(lock, data, len);
3187     ((unsigned char *) lock)[len] = 0;
3188     GlobalUnlock(clipdata);
3189
3190     if (!must_deselect)
3191         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3192
3193     if (OpenClipboard(hwnd)) {
3194         EmptyClipboard();
3195         SetClipboardData(CF_TEXT, clipdata);
3196         CloseClipboard();
3197     } else
3198         GlobalFree(clipdata);
3199
3200     if (!must_deselect)
3201         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3202 }
3203
3204 /*
3205  * Note: unlike write_aclip() this will not append a nul.
3206  */
3207 void write_clip(wchar_t * data, int len, int must_deselect)
3208 {
3209     HGLOBAL clipdata;
3210     HGLOBAL clipdata2;
3211     int len2;
3212     void *lock, *lock2;
3213
3214     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3215
3216     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3217                            len * sizeof(wchar_t));
3218     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3219
3220     if (!clipdata || !clipdata2) {
3221         if (clipdata)
3222             GlobalFree(clipdata);
3223         if (clipdata2)
3224             GlobalFree(clipdata2);
3225         return;
3226     }
3227     if (!(lock = GlobalLock(clipdata)))
3228         return;
3229     if (!(lock2 = GlobalLock(clipdata2)))
3230         return;
3231
3232     memcpy(lock, data, len * sizeof(wchar_t));
3233     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3234
3235     GlobalUnlock(clipdata);
3236     GlobalUnlock(clipdata2);
3237
3238     if (!must_deselect)
3239         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3240
3241     if (OpenClipboard(hwnd)) {
3242         EmptyClipboard();
3243         SetClipboardData(CF_UNICODETEXT, clipdata);
3244         SetClipboardData(CF_TEXT, clipdata2);
3245         CloseClipboard();
3246     } else {
3247         GlobalFree(clipdata);
3248         GlobalFree(clipdata2);
3249     }
3250
3251     if (!must_deselect)
3252         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3253 }
3254
3255 void get_clip(wchar_t ** p, int *len)
3256 {
3257     static HGLOBAL clipdata = NULL;
3258     static wchar_t *converted = 0;
3259     wchar_t *p2;
3260
3261     if (converted) {
3262         sfree(converted);
3263         converted = 0;
3264     }
3265     if (!p) {
3266         if (clipdata)
3267             GlobalUnlock(clipdata);
3268         clipdata = NULL;
3269         return;
3270     } else if (OpenClipboard(NULL)) {
3271         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3272             CloseClipboard();
3273             *p = GlobalLock(clipdata);
3274             if (*p) {
3275                 for (p2 = *p; *p2; p2++);
3276                 *len = p2 - *p;
3277                 return;
3278             }
3279         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3280             char *s;
3281             int i;
3282             CloseClipboard();
3283             s = GlobalLock(clipdata);
3284             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3285             *p = converted = smalloc(i * sizeof(wchar_t));
3286             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3287             *len = i - 1;
3288             return;
3289         } else
3290             CloseClipboard();
3291     }
3292
3293     *p = NULL;
3294     *len = 0;
3295 }
3296
3297 #if 0
3298 /*
3299  * Move `lines' lines from position `from' to position `to' in the
3300  * window.
3301  */
3302 void optimised_move(int to, int from, int lines)
3303 {
3304     RECT r;
3305     int min, max;
3306
3307     min = (to < from ? to : from);
3308     max = to + from - min;
3309
3310     r.left = 0;
3311     r.right = cols * font_width;
3312     r.top = min * font_height;
3313     r.bottom = (max + lines) * font_height;
3314     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3315 }
3316 #endif
3317
3318 /*
3319  * Print a message box and perform a fatal exit.
3320  */
3321 void fatalbox(char *fmt, ...)
3322 {
3323     va_list ap;
3324     char stuff[200];
3325
3326     va_start(ap, fmt);
3327     vsprintf(stuff, fmt, ap);
3328     va_end(ap);
3329     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3330     exit(1);
3331 }
3332
3333 /*
3334  * Manage window caption / taskbar flashing, if enabled.
3335  * 0 = stop, 1 = maintain, 2 = start
3336  */
3337 static void flash_window(int mode)
3338 {
3339     static long last_flash = 0;
3340     static int flashing = 0;
3341     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3342         /* stop */
3343         if (flashing) {
3344             FlashWindow(hwnd, FALSE);
3345             flashing = 0;
3346         }
3347
3348     } else if (mode == 2) {
3349         /* start */
3350         if (!flashing) {
3351             last_flash = GetTickCount();
3352             flashing = 1;
3353             FlashWindow(hwnd, TRUE);
3354         }
3355
3356     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3357         /* maintain */
3358         if (flashing) {
3359             long now = GetTickCount();
3360             long fdiff = now - last_flash;
3361             if (fdiff < 0 || fdiff > 450) {
3362                 last_flash = now;
3363                 FlashWindow(hwnd, TRUE);        /* toggle */
3364             }
3365         }
3366     }
3367 }
3368
3369 /*
3370  * Beep.
3371  */
3372 void beep(int mode)
3373 {
3374     if (mode == BELL_DEFAULT) {
3375         /*
3376          * For MessageBeep style bells, we want to be careful of
3377          * timing, because they don't have the nice property of
3378          * PlaySound bells that each one cancels the previous
3379          * active one. So we limit the rate to one per 50ms or so.
3380          */
3381         static long lastbeep = 0;
3382         long beepdiff;
3383
3384         beepdiff = GetTickCount() - lastbeep;
3385         if (beepdiff >= 0 && beepdiff < 50)
3386             return;
3387         MessageBeep(MB_OK);
3388         /*
3389          * The above MessageBeep call takes time, so we record the
3390          * time _after_ it finishes rather than before it starts.
3391          */
3392         lastbeep = GetTickCount();
3393     } else if (mode == BELL_WAVEFILE) {
3394         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3395             char buf[sizeof(cfg.bell_wavefile) + 80];
3396             sprintf(buf, "Unable to play sound file\n%s\n"
3397                     "Using default sound instead", cfg.bell_wavefile);
3398             MessageBox(hwnd, buf, "PuTTY Sound Error",
3399                        MB_OK | MB_ICONEXCLAMATION);
3400             cfg.beep = BELL_DEFAULT;
3401         }
3402     }
3403     /* Otherwise, either visual bell or disabled; do nothing here */
3404     if (!has_focus) {
3405         flash_window(2);               /* start */
3406     }
3407 }