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