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