]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
7ffa4d9c08627331d7df7f7276590f538eb7d27c
[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_CLIP (WM_XUSER + 2)
53
54 /* Needed for Chinese support and apparently not always defined. */
55 #ifndef VK_PROCESSKEY
56 #define VK_PROCESSKEY 0xE5
57 #endif
58
59 /* Needed for mouse wheel support and not defined in earlier SDKs. */
60 #ifndef WM_MOUSEWHEEL
61 #define WM_MOUSEWHEEL 0x020A
62 #endif
63
64 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
65 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
66                         unsigned char *output);
67 static void cfgtopalette(void);
68 static void init_palette(void);
69 static void init_fonts(int, int);
70 static void another_font(int);
71 static void deinit_fonts(void);
72
73 /* Window layout information */
74 static void reset_window(int);
75 static int full_screen = 0, extra_width, extra_height;
76 static int font_width, font_height, font_dualwidth;
77 static int offset_width, offset_height;
78 static int was_zoomed = 0;
79 static int prev_rows, prev_cols;
80   
81 static LONG old_wind_style;
82 static WINDOWPLACEMENT old_wind_placement;
83
84 static int pending_netevent = 0;
85 static WPARAM pend_netevent_wParam = 0;
86 static LPARAM pend_netevent_lParam = 0;
87 static void enact_pending_netevent(void);
88 static void flash_window(int mode);
89 static void flip_full_screen(void);
90
91 static time_t last_movement = 0;
92
93 #define FONT_NORMAL 0
94 #define FONT_BOLD 1
95 #define FONT_UNDERLINE 2
96 #define FONT_BOLDUND 3
97 #define FONT_WIDE       0x04
98 #define FONT_HIGH       0x08
99 #define FONT_NARROW     0x10
100
101 #define FONT_OEM        0x20
102 #define FONT_OEMBOLD    0x21
103 #define FONT_OEMUND     0x22
104 #define FONT_OEMBOLDUND 0x23
105
106 #define FONT_MAXNO      0x2F
107 #define FONT_SHIFT      5
108 static HFONT fonts[FONT_MAXNO];
109 static int fontflag[FONT_MAXNO];
110 static enum {
111     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
112 } bold_mode;
113 static enum {
114     UND_LINE, UND_FONT
115 } und_mode;
116 static int descent;
117
118 #define NCOLOURS 24
119 static COLORREF colours[NCOLOURS];
120 static HPALETTE pal;
121 static LPLOGPALETTE logpal;
122 static RGBTRIPLE defpal[NCOLOURS];
123
124 static HWND hwnd;
125
126 static HBITMAP caretbm;
127
128 static int dbltime, lasttime, lastact;
129 static Mouse_Button lastbtn;
130
131 /* this allows xterm-style mouse handling. */
132 static int send_raw_mouse = 0;
133 static int wheel_accumulator = 0;
134
135 static char *window_name, *icon_name;
136
137 static int compose_state = 0;
138
139 static OSVERSIONINFO osVersion;
140
141 /* Dummy routine, only required in plink. */
142 void ldisc_update(int echo, int edit)
143 {
144 }
145
146 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
147 {
148     static char appname[] = "PuTTY";
149     WORD winsock_ver;
150     WSADATA wsadata;
151     WNDCLASS wndclass;
152     MSG msg;
153     int guess_width, guess_height;
154
155     hinst = inst;
156     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
157
158     winsock_ver = MAKEWORD(1, 1);
159     if (WSAStartup(winsock_ver, &wsadata)) {
160         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
161                    MB_OK | MB_ICONEXCLAMATION);
162         return 1;
163     }
164     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
165         MessageBox(NULL, "WinSock version is incompatible with 1.1",
166                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
167         WSACleanup();
168         return 1;
169     }
170     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
171     sk_init();
172
173     InitCommonControls();
174
175     /* Ensure a Maximize setting in Explorer doesn't maximise the
176      * config box. */
177     defuse_showwindow();
178
179     {
180         ZeroMemory(&osVersion, sizeof(osVersion));
181         osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
182         if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
183             MessageBox(NULL, "Windows refuses to report a version",
184                        "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
185             return 1;
186         }
187     }
188
189     /*
190      * Process the command line.
191      */
192     {
193         char *p;
194
195         default_protocol = DEFAULT_PROTOCOL;
196         default_port = DEFAULT_PORT;
197         cfg.logtype = LGTYP_NONE;
198
199         do_defaults(NULL, &cfg);
200
201         p = cmdline;
202         while (*p && isspace(*p))
203             p++;
204
205         /*
206          * Process command line options first. Yes, this can be
207          * done better, and it will be as soon as I have the
208          * energy...
209          */
210         while (*p == '-') {
211             char *q = p + strcspn(p, " \t");
212             p++;
213             if (q == p + 3 &&
214                 tolower(p[0]) == 's' &&
215                 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
216                 default_protocol = cfg.protocol = PROT_SSH;
217                 default_port = cfg.port = 22;
218             } else if (q == p + 7 &&
219                        tolower(p[0]) == 'c' &&
220                        tolower(p[1]) == 'l' &&
221                        tolower(p[2]) == 'e' &&
222                        tolower(p[3]) == 'a' &&
223                        tolower(p[4]) == 'n' &&
224                        tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
225                 /*
226                  * `putty -cleanup'. Remove all registry entries
227                  * associated with PuTTY, and also find and delete
228                  * the random seed file.
229                  */
230                 if (MessageBox(NULL,
231                                "This procedure will remove ALL Registry\n"
232                                "entries associated with PuTTY, and will\n"
233                                "also remove the PuTTY random seed file.\n"
234                                "\n"
235                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
236                                "SESSIONS. Are you really sure you want\n"
237                                "to continue?",
238                                "PuTTY Warning",
239                                MB_YESNO | MB_ICONWARNING) == IDYES) {
240                     cleanup_all();
241                 }
242                 exit(0);
243             }
244             p = q + strspn(q, " \t");
245         }
246
247         /*
248          * An initial @ means to activate a saved session.
249          */
250         if (*p == '@') {
251             int i = strlen(p);
252             while (i > 1 && isspace(p[i - 1]))
253                 i--;
254             p[i] = '\0';
255             do_defaults(p + 1, &cfg);
256             if (!*cfg.host && !do_config()) {
257                 WSACleanup();
258                 return 0;
259             }
260         } else if (*p == '&') {
261             /*
262              * An initial & means we've been given a command line
263              * containing the hex value of a HANDLE for a file
264              * mapping object, which we must then extract as a
265              * config.
266              */
267             HANDLE filemap;
268             Config *cp;
269             if (sscanf(p + 1, "%p", &filemap) == 1 &&
270                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
271                                     0, 0, sizeof(Config))) != NULL) {
272                 cfg = *cp;
273                 UnmapViewOfFile(cp);
274                 CloseHandle(filemap);
275             } else if (!do_config()) {
276                 WSACleanup();
277                 return 0;
278             }
279         } else if (*p) {
280             char *q = p;
281             /*
282              * If the hostname starts with "telnet:", set the
283              * protocol to Telnet and process the string as a
284              * Telnet URL.
285              */
286             if (!strncmp(q, "telnet:", 7)) {
287                 char c;
288
289                 q += 7;
290                 if (q[0] == '/' && q[1] == '/')
291                     q += 2;
292                 cfg.protocol = PROT_TELNET;
293                 p = q;
294                 while (*p && *p != ':' && *p != '/')
295                     p++;
296                 c = *p;
297                 if (*p)
298                     *p++ = '\0';
299                 if (c == ':')
300                     cfg.port = atoi(p);
301                 else
302                     cfg.port = -1;
303                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
304                 cfg.host[sizeof(cfg.host) - 1] = '\0';
305             } else {
306                 while (*p && !isspace(*p))
307                     p++;
308                 if (*p)
309                     *p++ = '\0';
310                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
311                 cfg.host[sizeof(cfg.host) - 1] = '\0';
312                 while (*p && isspace(*p))
313                     p++;
314                 if (*p)
315                     cfg.port = atoi(p);
316                 else
317                     cfg.port = -1;
318             }
319         } else {
320             if (!do_config()) {
321                 WSACleanup();
322                 return 0;
323             }
324         }
325
326         /*
327          * Trim leading whitespace off the hostname if it's there.
328          */
329         {
330             int space = strspn(cfg.host, " \t");
331             memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
332         }
333
334         /* See if host is of the form user@host */
335         if (cfg.host[0] != '\0') {
336             char *atsign = strchr(cfg.host, '@');
337             /* Make sure we're not overflowing the user field */
338             if (atsign) {
339                 if (atsign - cfg.host < sizeof cfg.username) {
340                     strncpy(cfg.username, cfg.host, atsign - cfg.host);
341                     cfg.username[atsign - cfg.host] = '\0';
342                 }
343                 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
344             }
345         }
346
347         /*
348          * Trim a colon suffix off the hostname if it's there.
349          */
350         cfg.host[strcspn(cfg.host, ":")] = '\0';
351     }
352
353     /*
354      * Select protocol. This is farmed out into a table in a
355      * separate file to enable an ssh-free variant.
356      */
357     {
358         int i;
359         back = NULL;
360         for (i = 0; backends[i].backend != NULL; i++)
361             if (backends[i].protocol == cfg.protocol) {
362                 back = backends[i].backend;
363                 break;
364             }
365         if (back == NULL) {
366             MessageBox(NULL, "Unsupported protocol number found",
367                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
368             WSACleanup();
369             return 1;
370         }
371     }
372
373     /* Check for invalid Port number (i.e. zero) */
374     if (cfg.port == 0) {
375         MessageBox(NULL, "Invalid Port Number",
376                    "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
377         WSACleanup();
378         return 1;
379     }
380
381     if (!prev) {
382         wndclass.style = 0;
383         wndclass.lpfnWndProc = WndProc;
384         wndclass.cbClsExtra = 0;
385         wndclass.cbWndExtra = 0;
386         wndclass.hInstance = inst;
387         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
388         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
389         wndclass.hbrBackground = NULL;
390         wndclass.lpszMenuName = NULL;
391         wndclass.lpszClassName = appname;
392
393         RegisterClass(&wndclass);
394     }
395
396     hwnd = NULL;
397
398     savelines = cfg.savelines;
399     term_init();
400
401     cfgtopalette();
402
403     /*
404      * Guess some defaults for the window size. This all gets
405      * updated later, so we don't really care too much. However, we
406      * do want the font width/height guesses to correspond to a
407      * large font rather than a small one...
408      */
409
410     font_width = 10;
411     font_height = 20;
412     extra_width = 25;
413     extra_height = 28;
414     term_size(cfg.height, cfg.width, cfg.savelines);
415     guess_width = extra_width + font_width * cols;
416     guess_height = extra_height + font_height * rows;
417     {
418         RECT r;
419         HWND w = GetDesktopWindow();
420         GetWindowRect(w, &r);
421         if (guess_width > r.right - r.left)
422             guess_width = r.right - r.left;
423         if (guess_height > r.bottom - r.top)
424             guess_height = r.bottom - r.top;
425     }
426
427     {
428         int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
429         int exwinmode = 0;
430         if (!cfg.scrollbar)
431             winmode &= ~(WS_VSCROLL);
432         if (cfg.locksize && cfg.lockfont)
433             winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
434         if (cfg.alwaysontop)
435             exwinmode |= WS_EX_TOPMOST;
436         if (cfg.sunken_edge)
437             exwinmode |= WS_EX_CLIENTEDGE;
438         hwnd = CreateWindowEx(exwinmode, appname, appname,
439                               winmode, CW_USEDEFAULT, CW_USEDEFAULT,
440                               guess_width, guess_height,
441                               NULL, NULL, inst, NULL);
442     }
443
444     /*
445      * Initialise the fonts, simultaneously correcting the guesses
446      * for font_{width,height}.
447      */
448     init_fonts(0,0);
449
450     /*
451      * Correct the guesses for extra_{width,height}.
452      */
453     {
454         RECT cr, wr;
455         GetWindowRect(hwnd, &wr);
456         GetClientRect(hwnd, &cr);
457         offset_width = offset_height = cfg.window_border;
458         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
459         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
460     }
461
462     /*
463      * Resize the window, now we know what size we _really_ want it
464      * to be.
465      */
466     guess_width = extra_width + font_width * cols;
467     guess_height = extra_height + font_height * rows;
468     SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
469                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
470
471     /*
472      * Set up a caret bitmap, with no content.
473      */
474     {
475         char *bits;
476         int size = (font_width + 15) / 16 * 2 * font_height;
477         bits = smalloc(size);
478         memset(bits, 0, size);
479         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
480         sfree(bits);
481     }
482     CreateCaret(hwnd, caretbm, font_width, font_height);
483
484     /*
485      * Initialise the scroll bar.
486      */
487     {
488         SCROLLINFO si;
489
490         si.cbSize = sizeof(si);
491         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
492         si.nMin = 0;
493         si.nMax = rows - 1;
494         si.nPage = rows;
495         si.nPos = 0;
496         SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
497     }
498
499     /*
500      * Start up the telnet connection.
501      */
502     {
503         char *error;
504         char msg[1024], *title;
505         char *realhost;
506
507         error = back->init(cfg.host, cfg.port, &realhost);
508         if (error) {
509             sprintf(msg, "Unable to open connection to\n"
510                     "%.800s\n" "%s", cfg.host, error);
511             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
512             return 0;
513         }
514         window_name = icon_name = NULL;
515         if (*cfg.wintitle) {
516             title = cfg.wintitle;
517         } else {
518             sprintf(msg, "%s - PuTTY", realhost);
519             title = msg;
520         }
521         sfree(realhost);
522         set_title(title);
523         set_icon(title);
524     }
525
526     session_closed = FALSE;
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             term_out();
658             term_update();
659             ShowCaret(hwnd);
660
661             flash_window(1);           /* maintain */
662
663             if (in_vbell)
664                 /* Hmm, term_update didn't want to do an update too soon ... */
665                 timer_id = SetTimer(hwnd, 1, 50, NULL);
666             else if (!has_focus)
667                 timer_id = SetTimer(hwnd, 1, 500, NULL);
668             else
669                 timer_id = SetTimer(hwnd, 1, 100, NULL);
670             long_timer = 1;
671
672             /* There's no point rescanning everything in the message queue
673              * so we do an apparently unnecessary wait here
674              */
675             WaitMessage();
676             if (GetMessage(&msg, NULL, 0, 0) != 1)
677                 break;
678         }
679     }
680
681     /*
682      * Clean up.
683      */
684     deinit_fonts();
685     sfree(logpal);
686     if (pal)
687         DeleteObject(pal);
688     WSACleanup();
689
690     if (cfg.protocol == PROT_SSH) {
691         random_save_seed();
692 #ifdef MSCRYPTOAPI
693         crypto_wrapup();
694 #endif
695     }
696
697     return msg.wParam;
698 }
699
700 /*
701  * Set up, or shut down, an AsyncSelect. Called from winnet.c.
702  */
703 char *do_select(SOCKET skt, int startup)
704 {
705     int msg, events;
706     if (startup) {
707         msg = WM_NETEVENT;
708         events = (FD_CONNECT | FD_READ | FD_WRITE |
709                   FD_OOB | FD_CLOSE | FD_ACCEPT);
710     } else {
711         msg = events = 0;
712     }
713     if (!hwnd)
714         return "do_select(): internal error (hwnd==NULL)";
715     if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
716         switch (WSAGetLastError()) {
717           case WSAENETDOWN:
718             return "Network is down";
719           default:
720             return "WSAAsyncSelect(): unknown error";
721         }
722     }
723     return NULL;
724 }
725
726 /*
727  * set or clear the "raw mouse message" mode
728  */
729 void set_raw_mouse_mode(int activate)
730 {
731     send_raw_mouse = activate;
732     SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
733 }
734
735 /*
736  * Print a message box and close the connection.
737  */
738 void connection_fatal(char *fmt, ...)
739 {
740     va_list ap;
741     char stuff[200];
742
743     va_start(ap, fmt);
744     vsprintf(stuff, fmt, ap);
745     va_end(ap);
746     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
747     if (cfg.close_on_exit == COE_ALWAYS)
748         PostQuitMessage(1);
749     else {
750         session_closed = TRUE;
751         SetWindowText(hwnd, "PuTTY (inactive)");
752     }
753 }
754
755 /*
756  * Actually do the job requested by a WM_NETEVENT
757  */
758 static void enact_pending_netevent(void)
759 {
760     static int reentering = 0;
761     extern int select_result(WPARAM, LPARAM);
762     int ret;
763
764     if (reentering)
765         return;                        /* don't unpend the pending */
766
767     pending_netevent = FALSE;
768
769     reentering = 1;
770     ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
771     reentering = 0;
772
773     if (ret == 0 && !session_closed) {
774         /* Abnormal exits will already have set session_closed and taken
775          * appropriate action. */
776         if (cfg.close_on_exit == COE_ALWAYS ||
777             cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
778         else {
779             session_closed = TRUE;
780             SetWindowText(hwnd, "PuTTY (inactive)");
781             MessageBox(hwnd, "Connection closed by remote host",
782                        "PuTTY", MB_OK | MB_ICONINFORMATION);
783         }
784     }
785 }
786
787 /*
788  * Copy the colour palette from the configuration data into defpal.
789  * This is non-trivial because the colour indices are different.
790  */
791 static void cfgtopalette(void)
792 {
793     int i;
794     static const int ww[] = {
795         6, 7, 8, 9, 10, 11, 12, 13,
796         14, 15, 16, 17, 18, 19, 20, 21,
797         0, 1, 2, 3, 4, 4, 5, 5
798     };
799
800     for (i = 0; i < 24; i++) {
801         int w = ww[i];
802         defpal[i].rgbtRed = cfg.colours[w][0];
803         defpal[i].rgbtGreen = cfg.colours[w][1];
804         defpal[i].rgbtBlue = cfg.colours[w][2];
805     }
806 }
807
808 /*
809  * Set up the colour palette.
810  */
811 static void init_palette(void)
812 {
813     int i;
814     HDC hdc = GetDC(hwnd);
815     if (hdc) {
816         if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
817             logpal = smalloc(sizeof(*logpal)
818                              - sizeof(logpal->palPalEntry)
819                              + NCOLOURS * sizeof(PALETTEENTRY));
820             logpal->palVersion = 0x300;
821             logpal->palNumEntries = NCOLOURS;
822             for (i = 0; i < NCOLOURS; i++) {
823                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
824                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
825                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
826                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
827             }
828             pal = CreatePalette(logpal);
829             if (pal) {
830                 SelectPalette(hdc, pal, FALSE);
831                 RealizePalette(hdc);
832                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
833             }
834         }
835         ReleaseDC(hwnd, hdc);
836     }
837     if (pal)
838         for (i = 0; i < NCOLOURS; i++)
839             colours[i] = PALETTERGB(defpal[i].rgbtRed,
840                                     defpal[i].rgbtGreen,
841                                     defpal[i].rgbtBlue);
842     else
843         for (i = 0; i < NCOLOURS; i++)
844             colours[i] = RGB(defpal[i].rgbtRed,
845                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
846 }
847
848 /*
849  * Initialise all the fonts we will need initially. There may be as many as
850  * three or as few as one.  The other (poentially) twentyone fonts are done
851  * if/when they are needed.
852  *
853  * We also:
854  *
855  * - check the font width and height, correcting our guesses if
856  *   necessary.
857  *
858  * - verify that the bold font is the same width as the ordinary
859  *   one, and engage shadow bolding if not.
860  * 
861  * - verify that the underlined font is the same width as the
862  *   ordinary one (manual underlining by means of line drawing can
863  *   be done in a pinch).
864  */
865 static void init_fonts(int pick_width, int pick_height)
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     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
878     und_mode = UND_FONT;
879
880     if (cfg.fontisbold) {
881         fw_dontcare = FW_BOLD;
882         fw_bold = FW_HEAVY;
883     } else {
884         fw_dontcare = FW_DONTCARE;
885         fw_bold = FW_BOLD;
886     }
887
888     hdc = GetDC(hwnd);
889
890     if (pick_height)
891         font_height = pick_height;
892     else {
893         font_height = cfg.fontheight;
894         if (font_height > 0) {
895             font_height =
896                 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
897         }
898     }
899     font_width = pick_width;
900
901 #define f(i,c,w,u) \
902     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
903                            c, OUT_DEFAULT_PRECIS, \
904                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
905                            FIXED_PITCH | FF_DONTCARE, cfg.font)
906
907     f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
908
909     SelectObject(hdc, fonts[FONT_NORMAL]);
910     GetTextMetrics(hdc, &tm);
911
912     if (pick_width == 0 || pick_height == 0) {
913         font_height = tm.tmHeight;
914         font_width = tm.tmAveCharWidth;
915     }
916     font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
917
918 #ifdef RDB_DEBUG_PATCH
919     debug(23, "Primary font H=%d, AW=%d, MW=%d",
920             tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
921 #endif
922
923     {
924         CHARSETINFO info;
925         DWORD cset = tm.tmCharSet;
926         memset(&info, 0xFF, sizeof(info));
927
928         /* !!! Yes the next line is right */
929         if (cset == OEM_CHARSET)
930             font_codepage = GetOEMCP();
931         else
932             if (TranslateCharsetInfo
933                 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
934                 info.ciACP;
935         else
936             font_codepage = -1;
937
938         GetCPInfo(font_codepage, &cpinfo);
939         dbcs_screenfont = (cpinfo.MaxCharSize > 1);
940     }
941
942     f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
943
944     /*
945      * Some fonts, e.g. 9-pt Courier, draw their underlines
946      * outside their character cell. We successfully prevent
947      * screen corruption by clipping the text output, but then
948      * we lose the underline completely. Here we try to work
949      * out whether this is such a font, and if it is, we set a
950      * flag that causes underlines to be drawn by hand.
951      *
952      * Having tried other more sophisticated approaches (such
953      * as examining the TEXTMETRIC structure or requesting the
954      * height of a string), I think we'll do this the brute
955      * force way: we create a small bitmap, draw an underlined
956      * space on it, and test to see whether any pixels are
957      * foreground-coloured. (Since we expect the underline to
958      * go all the way across the character cell, we only search
959      * down a single column of the bitmap, half way across.)
960      */
961     {
962         HDC und_dc;
963         HBITMAP und_bm, und_oldbm;
964         int i, gotit;
965         COLORREF c;
966
967         und_dc = CreateCompatibleDC(hdc);
968         und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
969         und_oldbm = SelectObject(und_dc, und_bm);
970         SelectObject(und_dc, fonts[FONT_UNDERLINE]);
971         SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
972         SetTextColor(und_dc, RGB(255, 255, 255));
973         SetBkColor(und_dc, RGB(0, 0, 0));
974         SetBkMode(und_dc, OPAQUE);
975         ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
976         gotit = FALSE;
977         for (i = 0; i < font_height; i++) {
978             c = GetPixel(und_dc, font_width / 2, i);
979             if (c != RGB(0, 0, 0))
980                 gotit = TRUE;
981         }
982         SelectObject(und_dc, und_oldbm);
983         DeleteObject(und_bm);
984         DeleteDC(und_dc);
985         if (!gotit) {
986             und_mode = UND_LINE;
987             DeleteObject(fonts[FONT_UNDERLINE]);
988             fonts[FONT_UNDERLINE] = 0;
989         }
990     }
991
992     if (bold_mode == BOLD_FONT) {
993         f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
994     }
995 #undef f
996
997     descent = tm.tmAscent + 1;
998     if (descent >= font_height)
999         descent = font_height - 1;
1000
1001     for (i = 0; i < 3; i++) {
1002         if (fonts[i]) {
1003             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1004                 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1005             else
1006                 fontsize[i] = -i;
1007         } else
1008             fontsize[i] = -i;
1009     }
1010
1011     ReleaseDC(hwnd, hdc);
1012
1013     if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1014         und_mode = UND_LINE;
1015         DeleteObject(fonts[FONT_UNDERLINE]);
1016         fonts[FONT_UNDERLINE] = 0;
1017     }
1018
1019     if (bold_mode == BOLD_FONT &&
1020         fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1021         bold_mode = BOLD_SHADOW;
1022         DeleteObject(fonts[FONT_BOLD]);
1023         fonts[FONT_BOLD] = 0;
1024     }
1025     fontflag[0] = fontflag[1] = fontflag[2] = 1;
1026
1027     init_ucs_tables();
1028 }
1029
1030 static void another_font(int fontno)
1031 {
1032     int basefont;
1033     int fw_dontcare, fw_bold;
1034     int c, u, w, x;
1035     char *s;
1036
1037     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1038         return;
1039
1040     basefont = (fontno & ~(FONT_BOLDUND));
1041     if (basefont != fontno && !fontflag[basefont])
1042         another_font(basefont);
1043
1044     if (cfg.fontisbold) {
1045         fw_dontcare = FW_BOLD;
1046         fw_bold = FW_HEAVY;
1047     } else {
1048         fw_dontcare = FW_DONTCARE;
1049         fw_bold = FW_BOLD;
1050     }
1051
1052     c = cfg.fontcharset;
1053     w = fw_dontcare;
1054     u = FALSE;
1055     s = cfg.font;
1056     x = font_width;
1057
1058     if (fontno & FONT_WIDE)
1059         x *= 2;
1060     if (fontno & FONT_NARROW)
1061         x = (x+1)/2;
1062     if (fontno & FONT_OEM)
1063         c = OEM_CHARSET;
1064     if (fontno & FONT_BOLD)
1065         w = fw_bold;
1066     if (fontno & FONT_UNDERLINE)
1067         u = TRUE;
1068
1069     fonts[fontno] =
1070         CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1071                    FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1072                    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1073                    FIXED_PITCH | FF_DONTCARE, s);
1074
1075     fontflag[fontno] = 1;
1076 }
1077
1078 static void deinit_fonts(void)
1079 {
1080     int i;
1081     for (i = 0; i < FONT_MAXNO; i++) {
1082         if (fonts[i])
1083             DeleteObject(fonts[i]);
1084         fonts[i] = 0;
1085         fontflag[i] = 0;
1086     }
1087 }
1088
1089 void request_resize(int w, int h)
1090 {
1091     int width, height;
1092
1093     /* If the window is maximized supress resizing attempts */
1094     if (IsZoomed(hwnd)) {
1095         if (cfg.lockfont)
1096             return;
1097     }
1098
1099     if (cfg.lockfont && cfg.locksize) return;
1100     if (h == rows && w == cols) return;
1101
1102     /* Sanity checks ... */
1103     {
1104         static int first_time = 1;
1105         static RECT ss;
1106
1107         switch (first_time) {
1108           case 1:
1109             /* Get the size of the screen */
1110             if (GetClientRect(GetDesktopWindow(), &ss))
1111                 /* first_time = 0 */ ;
1112             else {
1113                 first_time = 2;
1114                 break;
1115             }
1116           case 0:
1117             /* Make sure the values are sane */
1118             width = (ss.right - ss.left - extra_width) / 4;
1119             height = (ss.bottom - ss.top - extra_height) / 6;
1120
1121             if (w > width || h > height)
1122                 return;
1123             if (w < 15)
1124                 w = 15;
1125             if (h < 1)
1126                 h = 1;
1127         }
1128     }
1129
1130     term_size(h, w, cfg.savelines);
1131
1132     if (cfg.lockfont) {
1133         width = extra_width + font_width * w;
1134         height = extra_height + font_height * h;
1135
1136         SetWindowPos(hwnd, NULL, 0, 0, width, height,
1137             SWP_NOACTIVATE | SWP_NOCOPYBITS |
1138             SWP_NOMOVE | SWP_NOZORDER);
1139     } else
1140         reset_window(0);
1141
1142     InvalidateRect(hwnd, NULL, TRUE);
1143 }
1144
1145 static void reset_window(int reinit) {
1146     /*
1147      * This function decides how to resize or redraw when the 
1148      * user changes something. 
1149      *
1150      * This function doesn't like to change the terminal size but if the
1151      * font size is locked that may be it's only soluion.
1152      */
1153     int win_width, win_height;
1154     RECT cr, wr;
1155
1156 #ifdef RDB_DEBUG_PATCH
1157     debug((27, "reset_window()"));
1158 #endif
1159
1160     /* Current window sizes ... */
1161     GetWindowRect(hwnd, &wr);
1162     GetClientRect(hwnd, &cr);
1163
1164     win_width  = cr.right - cr.left;
1165     win_height = cr.bottom - cr.top;
1166
1167     /* Are we being forced to reload the fonts ? */
1168     if (reinit>1) {
1169 #ifdef RDB_DEBUG_PATCH
1170         debug((27, "reset_window() -- Forced deinit"));
1171 #endif
1172         deinit_fonts();
1173         init_fonts(0,0);
1174     }
1175
1176     /* Oh, looks like we're minimised */
1177     if (win_width == 0 || win_height == 0)
1178         return;
1179
1180     /* Is the window out of position ? */
1181     if ( !reinit && 
1182             (offset_width != (win_width-font_width*cols)/2 ||
1183              offset_height != (win_height-font_height*rows)/2) ){
1184         offset_width = (win_width-font_width*cols)/2;
1185         offset_height = (win_height-font_height*rows)/2;
1186         InvalidateRect(hwnd, NULL, TRUE);
1187 #ifdef RDB_DEBUG_PATCH
1188         debug((27, "reset_window() -> Reposition terminal"));
1189 #endif
1190     }
1191
1192     if (IsZoomed(hwnd)) {
1193         /* We're fullscreen, this means we must not change the size of
1194          * the window so it's the font size or the terminal itself.
1195          */
1196
1197         extra_width = wr.right - wr.left - cr.right + cr.left;
1198         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1199
1200         if (!cfg.lockfont) {
1201             if (  font_width != win_width/cols || 
1202                   font_height != win_height/rows) {
1203                 deinit_fonts();
1204                 init_fonts(win_width/cols, win_height/rows);
1205                 offset_width = (win_width-font_width*cols)/2;
1206                 offset_height = (win_height-font_height*rows)/2;
1207                 InvalidateRect(hwnd, NULL, TRUE);
1208 #ifdef RDB_DEBUG_PATCH
1209                 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1210                         font_width, font_height));
1211 #endif
1212             }
1213         } else {
1214             if (  font_width != win_width/cols || 
1215                   font_height != win_height/rows) {
1216                 /* Our only choice at this point is to change the 
1217                  * size of the terminal; Oh well.
1218                  */
1219                 term_size( win_height/font_height, win_width/font_width,
1220                            cfg.savelines);
1221                 offset_width = (win_width-font_width*cols)/2;
1222                 offset_height = (win_height-font_height*rows)/2;
1223                 InvalidateRect(hwnd, NULL, TRUE);
1224 #ifdef RDB_DEBUG_PATCH
1225                 debug((27, "reset_window() -> Zoomed term_size"));
1226 #endif
1227             }
1228         }
1229         return;
1230     }
1231
1232     /* Hmm, a force re-init means we should ignore the current window
1233      * so we resize to the default font size.
1234      */
1235     if (reinit>0) {
1236 #ifdef RDB_DEBUG_PATCH
1237         debug((27, "reset_window() -> Forced re-init"));
1238 #endif
1239
1240         offset_width = offset_height = cfg.window_border;
1241         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1242         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1243
1244         if (win_width != font_width*cols + offset_width*2 ||
1245             win_height != font_height*rows + offset_height*2) {
1246
1247             /* If this is too large windows will resize it to the maximum
1248              * allowed window size, we will then be back in here and resize
1249              * the font or terminal to fit.
1250              */
1251             SetWindowPos(hwnd, NULL, 0, 0, 
1252                          font_width*cols + extra_width, 
1253                          font_height*rows + extra_height,
1254                          SWP_NOMOVE | SWP_NOZORDER);
1255         }
1256         return;
1257     }
1258
1259     /* Okay the user doesn't want us to change the font so we try the 
1260      * window. But that may be too big for the screen which forces us
1261      * to change the terminal.
1262      */
1263     if ((cfg.lockfont && reinit==0) || reinit>0) {
1264         offset_width = offset_height = cfg.window_border;
1265         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1266         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1267
1268         if (win_width != font_width*cols + offset_width*2 ||
1269             win_height != font_height*rows + offset_height*2) {
1270
1271             static RECT ss;
1272             int width, height;
1273
1274             GetClientRect(GetDesktopWindow(), &ss);
1275             width = (ss.right - ss.left - extra_width) / font_width;
1276             height = (ss.bottom - ss.top - extra_height) / font_height;
1277
1278             /* Grrr too big */
1279             if ( rows > height || cols > width ) {
1280                 if ( height > rows ) height = rows;
1281                 if ( width > cols )  width = cols;
1282                 term_size(height, width, cfg.savelines);
1283 #ifdef RDB_DEBUG_PATCH
1284                 debug((27, "reset_window() -> term resize to (%d,%d)",
1285                            height, width));
1286 #endif
1287             }
1288             
1289             SetWindowPos(hwnd, NULL, 0, 0, 
1290                          font_width*cols + extra_width, 
1291                          font_height*rows + extra_height,
1292                          SWP_NOMOVE | SWP_NOZORDER);
1293
1294             InvalidateRect(hwnd, NULL, TRUE);
1295 #ifdef RDB_DEBUG_PATCH
1296             debug((27, "reset_window() -> window resize to (%d,%d)",
1297                         font_width*cols + extra_width,
1298                         font_height*rows + extra_height));
1299 #endif
1300         }
1301         return;
1302     }
1303
1304     /* We're allowed to or must change the font but do we want to ?  */
1305
1306     if (font_width != (win_width-cfg.window_border*2)/cols || 
1307         font_height != (win_height-cfg.window_border*2)/rows) {
1308
1309         deinit_fonts();
1310         init_fonts((win_width-cfg.window_border*2)/cols, 
1311                    (win_height-cfg.window_border*2)/rows);
1312         offset_width = (win_width-font_width*cols)/2;
1313         offset_height = (win_height-font_height*rows)/2;
1314
1315         extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1316         extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1317
1318         InvalidateRect(hwnd, NULL, TRUE);
1319 #ifdef RDB_DEBUG_PATCH
1320         debug((25, "reset_window() -> font resize to (%d,%d)", 
1321                    font_width, font_height));
1322 #endif
1323     }
1324 }
1325
1326 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1327 {
1328     int thistime = GetMessageTime();
1329
1330     if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1331         lastbtn = MBT_NOTHING;
1332         term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1333         return;
1334     }
1335
1336     if (lastbtn == b && thistime - lasttime < dbltime) {
1337         lastact = (lastact == MA_CLICK ? MA_2CLK :
1338                    lastact == MA_2CLK ? MA_3CLK :
1339                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1340     } else {
1341         lastbtn = b;
1342         lastact = MA_CLICK;
1343     }
1344     if (lastact != MA_NOTHING)
1345         term_mouse(b, lastact, x, y, shift, ctrl);
1346     lasttime = thistime;
1347 }
1348
1349 /*
1350  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1351  * into a cooked one (SELECT, EXTEND, PASTE).
1352  */
1353 Mouse_Button translate_button(Mouse_Button button)
1354 {
1355     if (button == MBT_LEFT)
1356         return MBT_SELECT;
1357     if (button == MBT_MIDDLE)
1358         return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1359     if (button == MBT_RIGHT)
1360         return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1361     return 0;                          /* shouldn't happen */
1362 }
1363
1364 static void show_mouseptr(int show)
1365 {
1366     static int cursor_visible = 1;
1367     if (!cfg.hide_mouseptr)            /* override if this feature disabled */
1368         show = 1;
1369     if (cursor_visible && !show)
1370         ShowCursor(FALSE);
1371     else if (!cursor_visible && show)
1372         ShowCursor(TRUE);
1373     cursor_visible = show;
1374 }
1375
1376 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1377                                 WPARAM wParam, LPARAM lParam)
1378 {
1379     HDC hdc;
1380     static int ignore_clip = FALSE;
1381     static int resizing = FALSE;
1382     static int need_backend_resize = FALSE;
1383
1384     switch (message) {
1385       case WM_TIMER:
1386         if (pending_netevent)
1387             enact_pending_netevent();
1388         term_out();
1389         noise_regular();
1390         HideCaret(hwnd);
1391         term_update();
1392         ShowCaret(hwnd);
1393         if (cfg.ping_interval > 0) {
1394             time_t now;
1395             time(&now);
1396             if (now - last_movement > cfg.ping_interval) {
1397                 back->special(TS_PING);
1398                 last_movement = now;
1399             }
1400         }
1401         return 0;
1402       case WM_CREATE:
1403         break;
1404       case WM_CLOSE:
1405         show_mouseptr(1);
1406         if (!cfg.warn_on_close || session_closed ||
1407             MessageBox(hwnd,
1408                        "Are you sure you want to close this session?",
1409                        "PuTTY Exit Confirmation",
1410                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1411             DestroyWindow(hwnd);
1412         return 0;
1413       case WM_DESTROY:
1414         show_mouseptr(1);
1415         PostQuitMessage(0);
1416         return 0;
1417       case WM_SYSCOMMAND:
1418         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1419           case IDM_SHOWLOG:
1420             showeventlog(hwnd);
1421             break;
1422           case IDM_NEWSESS:
1423           case IDM_DUPSESS:
1424           case IDM_SAVEDSESS:
1425             {
1426                 char b[2048];
1427                 char c[30], *cl;
1428                 int freecl = FALSE;
1429                 STARTUPINFO si;
1430                 PROCESS_INFORMATION pi;
1431                 HANDLE filemap = NULL;
1432
1433                 if (wParam == IDM_DUPSESS) {
1434                     /*
1435                      * Allocate a file-mapping memory chunk for the
1436                      * config structure.
1437                      */
1438                     SECURITY_ATTRIBUTES sa;
1439                     Config *p;
1440
1441                     sa.nLength = sizeof(sa);
1442                     sa.lpSecurityDescriptor = NULL;
1443                     sa.bInheritHandle = TRUE;
1444                     filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1445                                                 &sa,
1446                                                 PAGE_READWRITE,
1447                                                 0, sizeof(Config), NULL);
1448                     if (filemap) {
1449                         p = (Config *) MapViewOfFile(filemap,
1450                                                      FILE_MAP_WRITE,
1451                                                      0, 0, sizeof(Config));
1452                         if (p) {
1453                             *p = cfg;  /* structure copy */
1454                             UnmapViewOfFile(p);
1455                         }
1456                     }
1457                     sprintf(c, "putty &%p", filemap);
1458                     cl = c;
1459                 } else if (wParam == IDM_SAVEDSESS) {
1460                     char *session =
1461                         sessions[(lParam - IDM_SAVED_MIN) / 16];
1462                     cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1463                     if (!cl)
1464                         cl = NULL;     /* not a very important failure mode */
1465                     else {
1466                         sprintf(cl, "putty @%s", session);
1467                         freecl = TRUE;
1468                     }
1469                 } else
1470                     cl = NULL;
1471
1472                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1473                 si.cb = sizeof(si);
1474                 si.lpReserved = NULL;
1475                 si.lpDesktop = NULL;
1476                 si.lpTitle = NULL;
1477                 si.dwFlags = 0;
1478                 si.cbReserved2 = 0;
1479                 si.lpReserved2 = NULL;
1480                 CreateProcess(b, cl, NULL, NULL, TRUE,
1481                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1482
1483                 if (filemap)
1484                     CloseHandle(filemap);
1485                 if (freecl)
1486                     sfree(cl);
1487             }
1488             break;
1489           case IDM_RECONF:
1490             {
1491                 Config prev_cfg;
1492                 int init_lvl = 1;
1493
1494                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1495                 prev_cfg = cfg;
1496
1497                 if (!do_reconfig(hwnd))
1498                     break;
1499
1500                 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1501                     prev_cfg.logtype != cfg.logtype) {
1502                     logfclose();       /* reset logging */
1503                     logfopen();
1504                 }
1505
1506                 sfree(logpal);
1507                 /*
1508                  * Flush the line discipline's edit buffer in the
1509                  * case where local editing has just been disabled.
1510                  */
1511                 ldisc_send(NULL, 0, 0);
1512                 if (pal)
1513                     DeleteObject(pal);
1514                 logpal = NULL;
1515                 pal = NULL;
1516                 cfgtopalette();
1517                 init_palette();
1518
1519                 /* Screen size changed ? */
1520                 if (cfg.height != prev_cfg.height ||
1521                     cfg.width != prev_cfg.width ||
1522                     cfg.savelines != prev_cfg.savelines ||
1523                     cfg.locksize )
1524                     term_size(cfg.height, cfg.width, cfg.savelines);
1525
1526                 /* Enable or disable the scroll bar, etc */
1527                 {
1528                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1529                     LONG nexflag, exflag =
1530                         GetWindowLong(hwnd, GWL_EXSTYLE);
1531
1532                     nexflag = exflag;
1533                     if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1534                         if (cfg.alwaysontop) {
1535                             nexflag |= WS_EX_TOPMOST;
1536                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1537                                          SWP_NOMOVE | SWP_NOSIZE);
1538                         } else {
1539                             nexflag &= ~(WS_EX_TOPMOST);
1540                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1541                                          SWP_NOMOVE | SWP_NOSIZE);
1542                         }
1543                     }
1544                     if (cfg.sunken_edge)
1545                         nexflag |= WS_EX_CLIENTEDGE;
1546                     else
1547                         nexflag &= ~(WS_EX_CLIENTEDGE);
1548
1549                     nflg = flag;
1550                     if (cfg.scrollbar)
1551                         nflg |= WS_VSCROLL;
1552                     else
1553                         nflg &= ~WS_VSCROLL;
1554                     if (cfg.locksize && cfg.lockfont)
1555                         nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1556                     else
1557                         nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1558
1559                     if (nflg != flag || nexflag != exflag) {
1560                         if (nflg != flag)
1561                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1562                         if (nexflag != exflag)
1563                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1564
1565                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1566                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1567                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1568                                      SWP_FRAMECHANGED);
1569
1570                         init_lvl = 2;
1571                     }
1572                 }
1573
1574                 /* Oops */
1575                 if (cfg.locksize && cfg.lockfont && IsZoomed(hwnd)) {
1576                     force_normal(hwnd);
1577                     init_lvl = 2;
1578                 }
1579
1580                 set_title(cfg.wintitle);
1581                 if (IsIconic(hwnd)) {
1582                     SetWindowText(hwnd,
1583                                   cfg.win_name_always ? window_name :
1584                                   icon_name);
1585                 }
1586
1587                 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1588                     strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1589                     cfg.fontisbold != prev_cfg.fontisbold ||
1590                     cfg.fontheight != prev_cfg.fontheight ||
1591                     cfg.fontcharset != prev_cfg.fontcharset ||
1592                     cfg.vtmode != prev_cfg.vtmode ||
1593                     cfg.bold_colour != prev_cfg.bold_colour ||
1594                     (cfg.lockfont && !prev_cfg.lockfont))
1595                     init_lvl = 2;
1596
1597                 InvalidateRect(hwnd, NULL, TRUE);
1598                 reset_window(init_lvl);
1599             }
1600             break;
1601           case IDM_COPYALL:
1602             term_copyall();
1603             break;
1604           case IDM_CLRSB:
1605             term_clrsb();
1606             break;
1607           case IDM_RESET:
1608             term_pwron();
1609             break;
1610           case IDM_TEL_AYT:
1611             back->special(TS_AYT);
1612             break;
1613           case IDM_TEL_BRK:
1614             back->special(TS_BRK);
1615             break;
1616           case IDM_TEL_SYNCH:
1617             back->special(TS_SYNCH);
1618             break;
1619           case IDM_TEL_EC:
1620             back->special(TS_EC);
1621             break;
1622           case IDM_TEL_EL:
1623             back->special(TS_EL);
1624             break;
1625           case IDM_TEL_GA:
1626             back->special(TS_GA);
1627             break;
1628           case IDM_TEL_NOP:
1629             back->special(TS_NOP);
1630             break;
1631           case IDM_TEL_ABORT:
1632             back->special(TS_ABORT);
1633             break;
1634           case IDM_TEL_AO:
1635             back->special(TS_AO);
1636             break;
1637           case IDM_TEL_IP:
1638             back->special(TS_IP);
1639             break;
1640           case IDM_TEL_SUSP:
1641             back->special(TS_SUSP);
1642             break;
1643           case IDM_TEL_EOR:
1644             back->special(TS_EOR);
1645             break;
1646           case IDM_TEL_EOF:
1647             back->special(TS_EOF);
1648             break;
1649           case IDM_ABOUT:
1650             showabout(hwnd);
1651             break;
1652           case SC_KEYMENU:
1653             /*
1654              * We get this if the System menu has been activated.
1655              * This might happen from within TranslateKey, in which
1656              * case it really wants to be followed by a `space'
1657              * character to actually _bring the menu up_ rather
1658              * than just sitting there in `ready to appear' state.
1659              */
1660             if( lParam == 0 )
1661                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1662             break;
1663           default:
1664             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1665                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1666             }
1667         }
1668         break;
1669
1670 #define X_POS(l) ((int)(short)LOWORD(l))
1671 #define Y_POS(l) ((int)(short)HIWORD(l))
1672
1673 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1674 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1675 #define WHEEL_DELTA 120
1676       case WM_MOUSEWHEEL:
1677         {
1678             wheel_accumulator += (short) HIWORD(wParam);
1679             wParam = LOWORD(wParam);
1680
1681             /* process events when the threshold is reached */
1682             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1683                 int b;
1684
1685                 /* reduce amount for next time */
1686                 if (wheel_accumulator > 0) {
1687                     b = MBT_WHEEL_UP;
1688                     wheel_accumulator -= WHEEL_DELTA;
1689                 } else if (wheel_accumulator < 0) {
1690                     b = MBT_WHEEL_DOWN;
1691                     wheel_accumulator += WHEEL_DELTA;
1692                 } else
1693                     break;
1694
1695                 if (send_raw_mouse) {
1696                     /* send a mouse-down followed by a mouse up */
1697                     term_mouse(b,
1698                                MA_CLICK,
1699                                TO_CHR_X(X_POS(lParam)),
1700                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1701                                wParam & MK_CONTROL);
1702                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1703                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1704                                wParam & MK_CONTROL);
1705                 } else {
1706                     /* trigger a scroll */
1707                     term_scroll(0,
1708                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1709                 }
1710             }
1711             return 0;
1712         }
1713       case WM_LBUTTONDOWN:
1714       case WM_MBUTTONDOWN:
1715       case WM_RBUTTONDOWN:
1716       case WM_LBUTTONUP:
1717       case WM_MBUTTONUP:
1718       case WM_RBUTTONUP:
1719         {
1720             int button, press;
1721             switch (message) {
1722               case WM_LBUTTONDOWN:
1723                 button = MBT_LEFT;
1724                 press = 1;
1725                 break;
1726               case WM_MBUTTONDOWN:
1727                 button = MBT_MIDDLE;
1728                 press = 1;
1729                 break;
1730               case WM_RBUTTONDOWN:
1731                 button = MBT_RIGHT;
1732                 press = 1;
1733                 break;
1734               case WM_LBUTTONUP:
1735                 button = MBT_LEFT;
1736                 press = 0;
1737                 break;
1738               case WM_MBUTTONUP:
1739                 button = MBT_MIDDLE;
1740                 press = 0;
1741                 break;
1742               case WM_RBUTTONUP:
1743                 button = MBT_RIGHT;
1744                 press = 0;
1745                 break;
1746               default:
1747                 button = press = 0;    /* shouldn't happen */
1748             }
1749             show_mouseptr(1);
1750             if (press) {
1751                 click(button,
1752                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1753                       wParam & MK_SHIFT, wParam & MK_CONTROL);
1754                 SetCapture(hwnd);
1755             } else {
1756                 term_mouse(button, MA_RELEASE,
1757                            TO_CHR_X(X_POS(lParam)),
1758                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1759                            wParam & MK_CONTROL);
1760                 ReleaseCapture();
1761             }
1762         }
1763         return 0;
1764       case WM_MOUSEMOVE:
1765         show_mouseptr(1);
1766         /*
1767          * Add the mouse position and message time to the random
1768          * number noise.
1769          */
1770         noise_ultralight(lParam);
1771
1772         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1773             Mouse_Button b;
1774             if (wParam & MK_LBUTTON)
1775                 b = MBT_LEFT;
1776             else if (wParam & MK_MBUTTON)
1777                 b = MBT_MIDDLE;
1778             else
1779                 b = MBT_RIGHT;
1780             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1781                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1782                        wParam & MK_CONTROL);
1783         }
1784         return 0;
1785       case WM_NCMOUSEMOVE:
1786         show_mouseptr(1);
1787         noise_ultralight(lParam);
1788         return 0;
1789       case WM_IGNORE_CLIP:
1790         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1791         break;
1792       case WM_DESTROYCLIPBOARD:
1793         if (!ignore_clip)
1794             term_deselect();
1795         ignore_clip = FALSE;
1796         return 0;
1797       case WM_PAINT:
1798         {
1799             PAINTSTRUCT p;
1800             HideCaret(hwnd);
1801             hdc = BeginPaint(hwnd, &p);
1802             if (pal) {
1803                 SelectPalette(hdc, pal, TRUE);
1804                 RealizePalette(hdc);
1805             }
1806             term_paint(hdc, 
1807                        (p.rcPaint.left-offset_width)/font_width,
1808                        (p.rcPaint.top-offset_height)/font_height,
1809                        (p.rcPaint.right-offset_width-1)/font_width,
1810                        (p.rcPaint.bottom-offset_height-1)/font_height);
1811
1812             if (p.fErase ||
1813                 p.rcPaint.left  < offset_width  ||
1814                 p.rcPaint.top   < offset_height ||
1815                 p.rcPaint.right >= offset_width + font_width*cols ||
1816                 p.rcPaint.bottom>= offset_height + font_height*rows)
1817             {
1818                 HBRUSH fillcolour, oldbrush;
1819                 HPEN   edge, oldpen;
1820                 fillcolour = CreateSolidBrush (
1821                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1822                 oldbrush = SelectObject(hdc, fillcolour);
1823                 edge = CreatePen(PS_SOLID, 0, 
1824                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1825                 oldpen = SelectObject(hdc, edge);
1826
1827                 ExcludeClipRect(hdc, 
1828                         offset_width, offset_height,
1829                         offset_width+font_width*cols,
1830                         offset_height+font_height*rows);
1831
1832                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
1833                           p.rcPaint.right, p.rcPaint.bottom);
1834
1835                 // SelectClipRgn(hdc, NULL);
1836
1837                 SelectObject(hdc, oldbrush);
1838                 DeleteObject(fillcolour);
1839                 SelectObject(hdc, oldpen);
1840                 DeleteObject(edge);
1841             }
1842             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1843             SelectObject(hdc, GetStockObject(WHITE_PEN));
1844             EndPaint(hwnd, &p);
1845             ShowCaret(hwnd);
1846         }
1847         return 0;
1848       case WM_NETEVENT:
1849         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1850          * but the only one that's likely to try to overload us is FD_READ.
1851          * This means buffering just one is fine.
1852          */
1853         if (pending_netevent)
1854             enact_pending_netevent();
1855
1856         pending_netevent = TRUE;
1857         pend_netevent_wParam = wParam;
1858         pend_netevent_lParam = lParam;
1859         if (WSAGETSELECTEVENT(lParam) != FD_READ)
1860             enact_pending_netevent();
1861
1862         time(&last_movement);
1863         return 0;
1864       case WM_SETFOCUS:
1865         has_focus = TRUE;
1866         CreateCaret(hwnd, caretbm, font_width, font_height);
1867         ShowCaret(hwnd);
1868         flash_window(0);               /* stop */
1869         compose_state = 0;
1870         term_out();
1871         term_update();
1872         break;
1873       case WM_KILLFOCUS:
1874         show_mouseptr(1);
1875         has_focus = FALSE;
1876         DestroyCaret();
1877         term_out();
1878         term_update();
1879         break;
1880       case WM_ENTERSIZEMOVE:
1881 #ifdef RDB_DEBUG_PATCH
1882         debug((27, "WM_ENTERSIZEMOVE"));
1883 #endif
1884         EnableSizeTip(1);
1885         resizing = TRUE;
1886         need_backend_resize = FALSE;
1887         break;
1888       case WM_EXITSIZEMOVE:
1889         EnableSizeTip(0);
1890         resizing = FALSE;
1891 #ifdef RDB_DEBUG_PATCH
1892         debug((27, "WM_EXITSIZEMOVE"));
1893 #endif
1894         if (need_backend_resize) {
1895             term_size(cfg.height, cfg.width, cfg.savelines);
1896             InvalidateRect(hwnd, NULL, TRUE);
1897         }
1898         break;
1899       case WM_SIZING:
1900         /*
1901          * This does two jobs:
1902          * 1) Keep the sizetip uptodate
1903          * 2) Make sure the window size is _stepped_ in units of the font size.
1904          */
1905         if (!cfg.locksize && !alt_pressed) {
1906             int width, height, w, h, ew, eh;
1907             LPRECT r = (LPRECT) lParam;
1908
1909             if ( !need_backend_resize && 
1910                     (cfg.height != rows || cfg.width != cols )) {
1911                 /* 
1912                  * Great! It seems the host has been changing the terminal
1913                  * size, well the user is now grabbing so this is probably
1914                  * the least confusing solution in the long run even though
1915                  * it a is suprise. Unfortunatly the only way to prevent 
1916                  * this seems to be to let the host change the window size 
1917                  * and as that's a user option we're still right back here.
1918                  */
1919                 term_size(cfg.height, cfg.width, cfg.savelines);
1920                 reset_window(2);
1921                 InvalidateRect(hwnd, NULL, TRUE);
1922                 need_backend_resize = TRUE;
1923             }
1924
1925             width = r->right - r->left - extra_width;
1926             height = r->bottom - r->top - extra_height;
1927             w = (width + font_width / 2) / font_width;
1928             if (w < 1)
1929                 w = 1;
1930             h = (height + font_height / 2) / font_height;
1931             if (h < 1)
1932                 h = 1;
1933             UpdateSizeTip(hwnd, w, h);
1934             ew = width - w * font_width;
1935             eh = height - h * font_height;
1936             if (ew != 0) {
1937                 if (wParam == WMSZ_LEFT ||
1938                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1939                     r->left += ew;
1940                 else
1941                     r->right -= ew;
1942             }
1943             if (eh != 0) {
1944                 if (wParam == WMSZ_TOP ||
1945                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1946                     r->top += eh;
1947                 else
1948                     r->bottom -= eh;
1949             }
1950             if (ew || eh)
1951                 return 1;
1952             else
1953                 return 0;
1954         } else {
1955             int width, height, w, h, rv = 0;
1956             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1957             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1958             LPRECT r = (LPRECT) lParam;
1959
1960             width = r->right - r->left - ex_width;
1961             height = r->bottom - r->top - ex_height;
1962
1963             w = (width + cols/2)/cols;
1964             h = (height + rows/2)/rows;
1965             if ( r->right != r->left + w*cols + ex_width) 
1966                 rv = 1;
1967
1968             if (wParam == WMSZ_LEFT ||
1969                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1970                 r->left = r->right - w*cols - ex_width;
1971             else
1972                 r->right = r->left + w*cols + ex_width;
1973
1974             if (r->bottom != r->top + h*rows + ex_height)
1975                 rv = 1;
1976
1977             if (wParam == WMSZ_TOP ||
1978                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1979                 r->top = r->bottom - h*rows - ex_height;
1980             else
1981                 r->bottom = r->top + h*rows + ex_height;
1982
1983             return rv;
1984         }
1985         break;
1986         /* break;  (never reached) */
1987       case WM_SIZE:
1988 #ifdef RDB_DEBUG_PATCH
1989         debug((27, "WM_SIZE %s (%d,%d)",
1990                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
1991                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
1992                 (wParam == SIZE_RESTORED && resizing) ? "to":
1993                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
1994                 "...",
1995             LOWORD(lParam), HIWORD(lParam)));
1996 #endif
1997         if (wParam == SIZE_MINIMIZED) {
1998             SetWindowText(hwnd,
1999                           cfg.win_name_always ? window_name : icon_name);
2000             break;
2001         }
2002         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2003             SetWindowText(hwnd, window_name);
2004
2005         if (cfg.lockfont && cfg.locksize) {
2006             /* A resize, well it better be a minimize. */
2007             reset_window(-1);
2008         } else {
2009
2010             int width, height, w, h;
2011
2012             width = LOWORD(lParam);
2013             height = HIWORD(lParam);
2014
2015             if (!resizing) {
2016                 if (wParam == SIZE_MAXIMIZED) {
2017                     was_zoomed = 1;
2018                     prev_rows = rows;
2019                     prev_cols = cols;
2020                     if (cfg.lockfont) {
2021                         w = width / font_width;
2022                         if (w < 1) w = 1;
2023                         h = height / font_height;
2024                         if (h < 1) h = 1;
2025
2026                         term_size(h, w, cfg.savelines);
2027                     }
2028                     reset_window(0);
2029                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2030                     was_zoomed = 0;
2031                     if (cfg.lockfont)
2032                         term_size(prev_rows, prev_cols, cfg.savelines);
2033                     reset_window(0);
2034                 }
2035                 /* This is an unexpected resize, these will normally happen
2036                  * if the window is too large. Probably either the user
2037                  * selected a huge font or the screen size has changed.
2038                  *
2039                  * This is also called with minimize.
2040                  */
2041                 else reset_window(-1);
2042             }
2043
2044             /*
2045              * Don't call back->size in mid-resize. (To prevent
2046              * massive numbers of resize events getting sent
2047              * down the connection during an NT opaque drag.)
2048              */
2049             if (resizing) {
2050                 if (!cfg.locksize && !alt_pressed) {
2051                     need_backend_resize = TRUE;
2052                     w = (width-cfg.window_border*2) / font_width;
2053                     if (w < 1) w = 1;
2054                     h = (height-cfg.window_border*2) / font_height;
2055                     if (h < 1) h = 1;
2056
2057                     cfg.height = h;
2058                     cfg.width = w;
2059                 } else 
2060                     reset_window(0);
2061             }
2062         }
2063         return 0;
2064       case WM_VSCROLL:
2065         switch (LOWORD(wParam)) {
2066           case SB_BOTTOM:
2067             term_scroll(-1, 0);
2068             break;
2069           case SB_TOP:
2070             term_scroll(+1, 0);
2071             break;
2072           case SB_LINEDOWN:
2073             term_scroll(0, +1);
2074             break;
2075           case SB_LINEUP:
2076             term_scroll(0, -1);
2077             break;
2078           case SB_PAGEDOWN:
2079             term_scroll(0, +rows / 2);
2080             break;
2081           case SB_PAGEUP:
2082             term_scroll(0, -rows / 2);
2083             break;
2084           case SB_THUMBPOSITION:
2085           case SB_THUMBTRACK:
2086             term_scroll(1, HIWORD(wParam));
2087             break;
2088         }
2089         break;
2090       case WM_PALETTECHANGED:
2091         if ((HWND) wParam != hwnd && pal != NULL) {
2092             HDC hdc = get_ctx();
2093             if (hdc) {
2094                 if (RealizePalette(hdc) > 0)
2095                     UpdateColors(hdc);
2096                 free_ctx(hdc);
2097             }
2098         }
2099         break;
2100       case WM_QUERYNEWPALETTE:
2101         if (pal != NULL) {
2102             HDC hdc = get_ctx();
2103             if (hdc) {
2104                 if (RealizePalette(hdc) > 0)
2105                     UpdateColors(hdc);
2106                 free_ctx(hdc);
2107                 return TRUE;
2108             }
2109         }
2110         return FALSE;
2111       case WM_KEYDOWN:
2112       case WM_SYSKEYDOWN:
2113       case WM_KEYUP:
2114       case WM_SYSKEYUP:
2115         /*
2116          * Add the scan code and keypress timing to the random
2117          * number noise.
2118          */
2119         noise_ultralight(lParam);
2120
2121         /*
2122          * We don't do TranslateMessage since it disassociates the
2123          * resulting CHAR message from the KEYDOWN that sparked it,
2124          * which we occasionally don't want. Instead, we process
2125          * KEYDOWN, and call the Win32 translator functions so that
2126          * we get the translations under _our_ control.
2127          */
2128         {
2129             unsigned char buf[20];
2130             int len;
2131
2132             if (wParam == VK_PROCESSKEY) {
2133                 MSG m;
2134                 m.hwnd = hwnd;
2135                 m.message = WM_KEYDOWN;
2136                 m.wParam = wParam;
2137                 m.lParam = lParam & 0xdfff;
2138                 TranslateMessage(&m);
2139             } else {
2140                 len = TranslateKey(message, wParam, lParam, buf);
2141                 if (len == -1)
2142                     return DefWindowProc(hwnd, message, wParam, lParam);
2143
2144                 if (len != 0) {
2145                     /*
2146                      * We need not bother about stdin backlogs
2147                      * here, because in GUI PuTTY we can't do
2148                      * anything about it anyway; there's no means
2149                      * of asking Windows to hold off on KEYDOWN
2150                      * messages. We _have_ to buffer everything
2151                      * we're sent.
2152                      */
2153                     ldisc_send(buf, len, 1);
2154                     show_mouseptr(0);
2155                 }
2156             }
2157         }
2158         return 0;
2159       case WM_INPUTLANGCHANGE:
2160         {
2161             /* wParam == Font number */
2162             /* lParam == Locale */
2163             char lbuf[20];
2164             HKL NewInputLocale = (HKL) lParam;
2165
2166             // lParam == GetKeyboardLayout(0);
2167
2168             GetLocaleInfo(LOWORD(NewInputLocale),
2169                           LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2170
2171             kbd_codepage = atoi(lbuf);
2172         }
2173         break;
2174       case WM_IME_COMPOSITION:
2175         {
2176             HIMC hIMC;
2177             int n;
2178             char *buff;
2179    
2180             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2181                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2182
2183             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2184                 break; /* fall back to DefWindowProc */
2185
2186             hIMC = ImmGetContext(hwnd);
2187             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2188
2189             if (n > 0) {
2190                 buff = (char*) smalloc(n);
2191                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2192                 luni_send((unsigned short *)buff, n / 2, 1);
2193                 free(buff);
2194             }
2195             ImmReleaseContext(hwnd, hIMC);
2196             return 1;
2197         }
2198
2199       case WM_IME_CHAR:
2200         if (wParam & 0xFF00) {
2201             unsigned char buf[2];
2202
2203             buf[1] = wParam;
2204             buf[0] = wParam >> 8;
2205             lpage_send(kbd_codepage, buf, 2, 1);
2206         } else {
2207             char c = (unsigned char) wParam;
2208             lpage_send(kbd_codepage, &c, 1, 1);
2209         }
2210         return (0);
2211       case WM_CHAR:
2212       case WM_SYSCHAR:
2213         /*
2214          * Nevertheless, we are prepared to deal with WM_CHAR
2215          * messages, should they crop up. So if someone wants to
2216          * post the things to us as part of a macro manoeuvre,
2217          * we're ready to cope.
2218          */
2219         {
2220             char c = (unsigned char)wParam;
2221             lpage_send(CP_ACP, &c, 1, 1);
2222         }
2223         return 0;
2224       case WM_SETCURSOR:
2225         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2226             SetCursor(LoadCursor(NULL, IDC_ARROW));
2227             return TRUE;
2228         }
2229     }
2230
2231     return DefWindowProc(hwnd, message, wParam, lParam);
2232 }
2233
2234 /*
2235  * Move the system caret. (We maintain one, even though it's
2236  * invisible, for the benefit of blind people: apparently some
2237  * helper software tracks the system caret, so we should arrange to
2238  * have one.)
2239  */
2240 void sys_cursor(int x, int y)
2241 {
2242     COMPOSITIONFORM cf;
2243     HIMC hIMC;
2244
2245     if (!has_focus) return;
2246     
2247     SetCaretPos(x * font_width + offset_width,
2248                 y * font_height + offset_height);
2249
2250     /* IMM calls on Win98 and beyond only */
2251     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2252     
2253     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2254             osVersion.dwMinorVersion == 0) return; /* 95 */
2255
2256     /* we should have the IMM functions */
2257     hIMC = ImmGetContext(hwnd);
2258     cf.dwStyle = CFS_POINT;
2259     cf.ptCurrentPos.x = x * font_width;
2260     cf.ptCurrentPos.y = y * font_height;
2261     ImmSetCompositionWindow(hIMC, &cf);
2262
2263     ImmReleaseContext(hwnd, hIMC);
2264 }
2265
2266 /*
2267  * Draw a line of text in the window, at given character
2268  * coordinates, in given attributes.
2269  *
2270  * We are allowed to fiddle with the contents of `text'.
2271  */
2272 void do_text(Context ctx, int x, int y, char *text, int len,
2273              unsigned long attr, int lattr)
2274 {
2275     COLORREF fg, bg, t;
2276     int nfg, nbg, nfont;
2277     HDC hdc = ctx;
2278     RECT line_box;
2279     int force_manual_underline = 0;
2280     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2281     int char_width = fnt_width;
2282     int text_adjust = 0;
2283     static int *IpDx = 0, IpDxLEN = 0;
2284
2285     if (attr & ATTR_WIDE)
2286         char_width *= 2;
2287
2288     if (len > IpDxLEN || IpDx[0] != char_width) {
2289         int i;
2290         if (len > IpDxLEN) {
2291             sfree(IpDx);
2292             IpDx = smalloc((len + 16) * sizeof(int));
2293             IpDxLEN = (len + 16);
2294         }
2295         for (i = 0; i < IpDxLEN; i++)
2296             IpDx[i] = char_width;
2297     }
2298
2299     /* Only want the left half of double width lines */
2300     if (lattr != LATTR_NORM && x*2 >= cols)
2301         return;
2302
2303     x *= fnt_width;
2304     y *= font_height;
2305     x += offset_width;
2306     y += offset_height;
2307
2308     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2309         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2310         attr ^= ATTR_CUR_XOR;
2311     }
2312
2313     nfont = 0;
2314     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2315         /* Assume a poorman font is borken in other ways too. */
2316         lattr = LATTR_WIDE;
2317     } else
2318         switch (lattr) {
2319           case LATTR_NORM:
2320             break;
2321           case LATTR_WIDE:
2322             nfont |= FONT_WIDE;
2323             break;
2324           default:
2325             nfont |= FONT_WIDE + FONT_HIGH;
2326             break;
2327         }
2328     if (attr & ATTR_NARROW)
2329         nfont |= FONT_NARROW;
2330
2331     /* Special hack for the VT100 linedraw glyphs. */
2332     if ((attr & CSET_MASK) == 0x2300) {
2333         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2334             switch ((unsigned char) (text[0])) {
2335               case 0xBA:
2336                 text_adjust = -2 * font_height / 5;
2337                 break;
2338               case 0xBB:
2339                 text_adjust = -1 * font_height / 5;
2340                 break;
2341               case 0xBC:
2342                 text_adjust = font_height / 5;
2343                 break;
2344               case 0xBD:
2345                 text_adjust = 2 * font_height / 5;
2346                 break;
2347             }
2348             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2349                 text_adjust *= 2;
2350             attr &= ~CSET_MASK;
2351             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2352             attr |= (unitab_xterm['q'] & CSET_MASK);
2353             if (attr & ATTR_UNDER) {
2354                 attr &= ~ATTR_UNDER;
2355                 force_manual_underline = 1;
2356             }
2357         }
2358     }
2359
2360     /* Anything left as an original character set is unprintable. */
2361     if (DIRECT_CHAR(attr)) {
2362         attr &= ~CSET_MASK;
2363         attr |= 0xFF00;
2364         memset(text, 0xFD, len);
2365     }
2366
2367     /* OEM CP */
2368     if ((attr & CSET_MASK) == ATTR_OEMCP)
2369         nfont |= FONT_OEM;
2370
2371     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2372     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2373     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2374         nfont |= FONT_BOLD;
2375     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2376         nfont |= FONT_UNDERLINE;
2377     another_font(nfont);
2378     if (!fonts[nfont]) {
2379         if (nfont & FONT_UNDERLINE)
2380             force_manual_underline = 1;
2381         /* Don't do the same for manual bold, it could be bad news. */
2382
2383         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2384     }
2385     another_font(nfont);
2386     if (!fonts[nfont])
2387         nfont = FONT_NORMAL;
2388     if (attr & ATTR_REVERSE) {
2389         t = nfg;
2390         nfg = nbg;
2391         nbg = t;
2392     }
2393     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2394         nfg++;
2395     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2396         nbg++;
2397     fg = colours[nfg];
2398     bg = colours[nbg];
2399     SelectObject(hdc, fonts[nfont]);
2400     SetTextColor(hdc, fg);
2401     SetBkColor(hdc, bg);
2402     SetBkMode(hdc, OPAQUE);
2403     line_box.left = x;
2404     line_box.top = y;
2405     line_box.right = x + char_width * len;
2406     line_box.bottom = y + font_height;
2407
2408     /* Only want the left half of double width lines */
2409     if (line_box.right > font_width*cols+offset_width)
2410         line_box.right = font_width*cols+offset_width;
2411
2412     /* We're using a private area for direct to font. (512 chars.) */
2413     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2414         /* Ho Hum, dbcs fonts are a PITA! */
2415         /* To display on W9x I have to convert to UCS */
2416         static wchar_t *uni_buf = 0;
2417         static int uni_len = 0;
2418         int nlen, mptr;
2419         if (len > uni_len) {
2420             sfree(uni_buf);
2421             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2422         }
2423
2424         for(nlen = mptr = 0; mptr<len; mptr++) {
2425             uni_buf[nlen] = 0xFFFD;
2426             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2427                 IpDx[nlen] += char_width;
2428                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2429                                    text+mptr, 2, uni_buf+nlen, 1);
2430                 mptr++;
2431             }
2432             else
2433             {
2434                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2435                                    text+mptr, 1, uni_buf+nlen, 1);
2436             }
2437             nlen++;
2438         }
2439         if (nlen <= 0)
2440             return;                    /* Eeek! */
2441
2442         ExtTextOutW(hdc, x,
2443                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2444                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2445         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2446             SetBkMode(hdc, TRANSPARENT);
2447             ExtTextOutW(hdc, x - 1,
2448                         y - font_height * (lattr ==
2449                                            LATTR_BOT) + text_adjust,
2450                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2451         }
2452
2453         IpDx[0] = -1;
2454     } else if (DIRECT_FONT(attr)) {
2455         ExtTextOut(hdc, x,
2456                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2457                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2458         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2459             SetBkMode(hdc, TRANSPARENT);
2460
2461             /* GRR: This draws the character outside it's box and can leave
2462              * 'droppings' even with the clip box! I suppose I could loop it
2463              * one character at a time ... yuk. 
2464              * 
2465              * Or ... I could do a test print with "W", and use +1 or -1 for this
2466              * shift depending on if the leftmost column is blank...
2467              */
2468             ExtTextOut(hdc, x - 1,
2469                        y - font_height * (lattr ==
2470                                           LATTR_BOT) + text_adjust,
2471                        ETO_CLIPPED, &line_box, text, len, IpDx);
2472         }
2473     } else {
2474         /* And 'normal' unicode characters */
2475         static WCHAR *wbuf = NULL;
2476         static int wlen = 0;
2477         int i;
2478         if (wlen < len) {
2479             sfree(wbuf);
2480             wlen = len;
2481             wbuf = smalloc(wlen * sizeof(WCHAR));
2482         }
2483         for (i = 0; i < len; i++)
2484             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2485
2486         ExtTextOutW(hdc, x,
2487                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2488                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2489
2490         /* And the shadow bold hack. */
2491         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2492             SetBkMode(hdc, TRANSPARENT);
2493             ExtTextOutW(hdc, x - 1,
2494                         y - font_height * (lattr ==
2495                                            LATTR_BOT) + text_adjust,
2496                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2497         }
2498     }
2499     if (lattr != LATTR_TOP && (force_manual_underline ||
2500                                (und_mode == UND_LINE
2501                                 && (attr & ATTR_UNDER)))) {
2502         HPEN oldpen;
2503         int dec = descent;
2504         if (lattr == LATTR_BOT)
2505             dec = dec * 2 - font_height;
2506
2507         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2508         MoveToEx(hdc, x, y + dec, NULL);
2509         LineTo(hdc, x + len * char_width, y + dec);
2510         oldpen = SelectObject(hdc, oldpen);
2511         DeleteObject(oldpen);
2512     }
2513 }
2514
2515 void do_cursor(Context ctx, int x, int y, char *text, int len,
2516                unsigned long attr, int lattr)
2517 {
2518
2519     int fnt_width;
2520     int char_width;
2521     HDC hdc = ctx;
2522     int ctype = cfg.cursor_type;
2523
2524     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2525         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2526             do_text(ctx, x, y, text, len, attr, lattr);
2527             return;
2528         }
2529         ctype = 2;
2530         attr |= TATTR_RIGHTCURS;
2531     }
2532
2533     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2534     if (attr & ATTR_WIDE)
2535         char_width *= 2;
2536     x *= fnt_width;
2537     y *= font_height;
2538     x += offset_width;
2539     y += offset_height;
2540
2541     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2542         POINT pts[5];
2543         HPEN oldpen;
2544         pts[0].x = pts[1].x = pts[4].x = x;
2545         pts[2].x = pts[3].x = x + char_width - 1;
2546         pts[0].y = pts[3].y = pts[4].y = y;
2547         pts[1].y = pts[2].y = y + font_height - 1;
2548         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2549         Polyline(hdc, pts, 5);
2550         oldpen = SelectObject(hdc, oldpen);
2551         DeleteObject(oldpen);
2552     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2553         int startx, starty, dx, dy, length, i;
2554         if (ctype == 1) {
2555             startx = x;
2556             starty = y + descent;
2557             dx = 1;
2558             dy = 0;
2559             length = char_width;
2560         } else {
2561             int xadjust = 0;
2562             if (attr & TATTR_RIGHTCURS)
2563                 xadjust = char_width - 1;
2564             startx = x + xadjust;
2565             starty = y;
2566             dx = 0;
2567             dy = 1;
2568             length = font_height;
2569         }
2570         if (attr & TATTR_ACTCURS) {
2571             HPEN oldpen;
2572             oldpen =
2573                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2574             MoveToEx(hdc, startx, starty, NULL);
2575             LineTo(hdc, startx + dx * length, starty + dy * length);
2576             oldpen = SelectObject(hdc, oldpen);
2577             DeleteObject(oldpen);
2578         } else {
2579             for (i = 0; i < length; i++) {
2580                 if (i % 2 == 0) {
2581                     SetPixel(hdc, startx, starty, colours[23]);
2582                 }
2583                 startx += dx;
2584                 starty += dy;
2585             }
2586         }
2587     }
2588 }
2589
2590 /* This function gets the actual width of a character in the normal font.
2591  */
2592 int CharWidth(Context ctx, int uc) {
2593     HDC hdc = ctx;
2594     int ibuf = 0;
2595
2596     /* If the font max is the same as the font ave width then this
2597      * function is a no-op.
2598      */
2599     if (!font_dualwidth) return 1;
2600
2601     switch (uc & CSET_MASK) {
2602       case ATTR_ASCII:
2603         uc = unitab_line[uc & 0xFF];
2604         break;
2605       case ATTR_LINEDRW:
2606         uc = unitab_xterm[uc & 0xFF];
2607         break;
2608       case ATTR_SCOACS:
2609         uc = unitab_scoacs[uc & 0xFF];
2610         break;
2611     }
2612     if (DIRECT_FONT(uc)) {
2613         if (dbcs_screenfont) return 1;
2614
2615         /* Speedup, I know of no font where ascii is the wrong width */
2616         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2617             return 1;
2618
2619         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2620             SelectObject(hdc, fonts[FONT_NORMAL]);
2621         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2622             another_font(FONT_OEM);
2623             if (!fonts[FONT_OEM]) return 0;
2624
2625             SelectObject(hdc, fonts[FONT_OEM]);
2626         } else
2627             return 0;
2628
2629         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2630              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2631             return 0;
2632     } else {
2633         /* Speedup, I know of no font where ascii is the wrong width */
2634         if (uc >= ' ' && uc <= '~') return 1;
2635
2636         SelectObject(hdc, fonts[FONT_NORMAL]);
2637         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2638             /* Okay that one worked */ ;
2639         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2640             /* This should work on 9x too, but it's "less accurate" */ ;
2641         else
2642             return 0;
2643     }
2644
2645     ibuf += font_width / 2 -1;
2646     ibuf /= font_width;
2647
2648     return ibuf;
2649 }
2650
2651 /*
2652  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2653  * codes. Returns number of bytes used or zero to drop the message
2654  * or -1 to forward the message to windows.
2655  */
2656 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2657                         unsigned char *output)
2658 {
2659     BYTE keystate[256];
2660     int scan, left_alt = 0, key_down, shift_state;
2661     int r, i, code;
2662     unsigned char *p = output;
2663     static int alt_sum = 0;
2664
2665     HKL kbd_layout = GetKeyboardLayout(0);
2666
2667     static WORD keys[3];
2668     static int compose_char = 0;
2669     static WPARAM compose_key = 0;
2670
2671     r = GetKeyboardState(keystate);
2672     if (!r)
2673         memset(keystate, 0, sizeof(keystate));
2674     else {
2675 #if 0
2676 #define SHOW_TOASCII_RESULT
2677         {                              /* Tell us all about key events */
2678             static BYTE oldstate[256];
2679             static int first = 1;
2680             static int scan;
2681             int ch;
2682             if (first)
2683                 memcpy(oldstate, keystate, sizeof(oldstate));
2684             first = 0;
2685
2686             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2687                 debug(("+"));
2688             } else if ((HIWORD(lParam) & KF_UP)
2689                        && scan == (HIWORD(lParam) & 0xFF)) {
2690                 debug((". U"));
2691             } else {
2692                 debug((".\n"));
2693                 if (wParam >= VK_F1 && wParam <= VK_F20)
2694                     debug(("K_F%d", wParam + 1 - VK_F1));
2695                 else
2696                     switch (wParam) {
2697                       case VK_SHIFT:
2698                         debug(("SHIFT"));
2699                         break;
2700                       case VK_CONTROL:
2701                         debug(("CTRL"));
2702                         break;
2703                       case VK_MENU:
2704                         debug(("ALT"));
2705                         break;
2706                       default:
2707                         debug(("VK_%02x", wParam));
2708                     }
2709                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2710                     debug(("*"));
2711                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2712
2713                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2714                 if (ch >= ' ' && ch <= '~')
2715                     debug((", '%c'", ch));
2716                 else if (ch)
2717                     debug((", $%02x", ch));
2718
2719                 if (keys[0])
2720                     debug((", KB0=%02x", keys[0]));
2721                 if (keys[1])
2722                     debug((", KB1=%02x", keys[1]));
2723                 if (keys[2])
2724                     debug((", KB2=%02x", keys[2]));
2725
2726                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2727                     debug((", S"));
2728                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2729                     debug((", C"));
2730                 if ((HIWORD(lParam) & KF_EXTENDED))
2731                     debug((", E"));
2732                 if ((HIWORD(lParam) & KF_UP))
2733                     debug((", U"));
2734             }
2735
2736             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2737             else if ((HIWORD(lParam) & KF_UP))
2738                 oldstate[wParam & 0xFF] ^= 0x80;
2739             else
2740                 oldstate[wParam & 0xFF] ^= 0x81;
2741
2742             for (ch = 0; ch < 256; ch++)
2743                 if (oldstate[ch] != keystate[ch])
2744                     debug((", M%02x=%02x", ch, keystate[ch]));
2745
2746             memcpy(oldstate, keystate, sizeof(oldstate));
2747         }
2748 #endif
2749
2750         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2751             keystate[VK_RMENU] = keystate[VK_MENU];
2752         }
2753
2754
2755         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2756         if ((cfg.funky_type == 3 ||
2757              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2758             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2759
2760             wParam = VK_EXECUTE;
2761
2762             /* UnToggle NUMLock */
2763             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2764                 keystate[VK_NUMLOCK] ^= 1;
2765         }
2766
2767         /* And write back the 'adjusted' state */
2768         SetKeyboardState(keystate);
2769     }
2770
2771     /* Disable Auto repeat if required */
2772     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2773         return 0;
2774
2775     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2776         left_alt = 1;
2777
2778     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2779
2780     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2781     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2782         if (cfg.ctrlaltkeys)
2783             keystate[VK_MENU] = 0;
2784         else {
2785             keystate[VK_RMENU] = 0x80;
2786             left_alt = 0;
2787         }
2788     }
2789
2790     alt_pressed = (left_alt && key_down);
2791
2792     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2793     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2794         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2795
2796     /* Note if AltGr was pressed and if it was used as a compose key */
2797     if (!compose_state) {
2798         compose_key = 0x100;
2799         if (cfg.compose_key) {
2800             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2801                 compose_key = wParam;
2802         }
2803         if (wParam == VK_APPS)
2804             compose_key = wParam;
2805     }
2806
2807     if (wParam == compose_key) {
2808         if (compose_state == 0
2809             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2810                 1;
2811         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2812             compose_state = 2;
2813         else
2814             compose_state = 0;
2815     } else if (compose_state == 1 && wParam != VK_CONTROL)
2816         compose_state = 0;
2817
2818     /* 
2819      * Record that we pressed key so the scroll window can be reset, but
2820      * be careful to avoid Shift-UP/Down
2821      */
2822     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2823         seen_key_event = 1;
2824     }
2825
2826     /* Make sure we're not pasting */
2827     if (key_down)
2828         term_nopaste();
2829
2830     if (compose_state > 1 && left_alt)
2831         compose_state = 0;
2832
2833     /* Sanitize the number pad if not using a PC NumPad */
2834     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2835                      && cfg.funky_type != 2)
2836         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2837         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2838             int nParam = 0;
2839             switch (wParam) {
2840               case VK_INSERT:
2841                 nParam = VK_NUMPAD0;
2842                 break;
2843               case VK_END:
2844                 nParam = VK_NUMPAD1;
2845                 break;
2846               case VK_DOWN:
2847                 nParam = VK_NUMPAD2;
2848                 break;
2849               case VK_NEXT:
2850                 nParam = VK_NUMPAD3;
2851                 break;
2852               case VK_LEFT:
2853                 nParam = VK_NUMPAD4;
2854                 break;
2855               case VK_CLEAR:
2856                 nParam = VK_NUMPAD5;
2857                 break;
2858               case VK_RIGHT:
2859                 nParam = VK_NUMPAD6;
2860                 break;
2861               case VK_HOME:
2862                 nParam = VK_NUMPAD7;
2863                 break;
2864               case VK_UP:
2865                 nParam = VK_NUMPAD8;
2866                 break;
2867               case VK_PRIOR:
2868                 nParam = VK_NUMPAD9;
2869                 break;
2870               case VK_DELETE:
2871                 nParam = VK_DECIMAL;
2872                 break;
2873             }
2874             if (nParam) {
2875                 if (keystate[VK_NUMLOCK] & 1)
2876                     shift_state |= 1;
2877                 wParam = nParam;
2878             }
2879         }
2880     }
2881
2882     /* If a key is pressed and AltGr is not active */
2883     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2884         /* Okay, prepare for most alts then ... */
2885         if (left_alt)
2886             *p++ = '\033';
2887
2888         /* Lets see if it's a pattern we know all about ... */
2889         if (wParam == VK_PRIOR && shift_state == 1) {
2890             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2891             return 0;
2892         }
2893         if (wParam == VK_NEXT && shift_state == 1) {
2894             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2895             return 0;
2896         }
2897         if (wParam == VK_INSERT && shift_state == 1) {
2898             term_do_paste();
2899             return 0;
2900         }
2901         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2902             return -1;
2903         }
2904         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2905             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2906             return -1;
2907         }
2908         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2909             flip_full_screen();
2910             return -1;
2911         }
2912         /* Control-Numlock for app-keypad mode switch */
2913         if (wParam == VK_PAUSE && shift_state == 2) {
2914             app_keypad_keys ^= 1;
2915             return 0;
2916         }
2917
2918         /* Nethack keypad */
2919         if (cfg.nethack_keypad && !left_alt) {
2920             switch (wParam) {
2921               case VK_NUMPAD1:
2922                 *p++ = shift_state ? 'B' : 'b';
2923                 return p - output;
2924               case VK_NUMPAD2:
2925                 *p++ = shift_state ? 'J' : 'j';
2926                 return p - output;
2927               case VK_NUMPAD3:
2928                 *p++ = shift_state ? 'N' : 'n';
2929                 return p - output;
2930               case VK_NUMPAD4:
2931                 *p++ = shift_state ? 'H' : 'h';
2932                 return p - output;
2933               case VK_NUMPAD5:
2934                 *p++ = shift_state ? '.' : '.';
2935                 return p - output;
2936               case VK_NUMPAD6:
2937                 *p++ = shift_state ? 'L' : 'l';
2938                 return p - output;
2939               case VK_NUMPAD7:
2940                 *p++ = shift_state ? 'Y' : 'y';
2941                 return p - output;
2942               case VK_NUMPAD8:
2943                 *p++ = shift_state ? 'K' : 'k';
2944                 return p - output;
2945               case VK_NUMPAD9:
2946                 *p++ = shift_state ? 'U' : 'u';
2947                 return p - output;
2948             }
2949         }
2950
2951         /* Application Keypad */
2952         if (!left_alt) {
2953             int xkey = 0;
2954
2955             if (cfg.funky_type == 3 ||
2956                 (cfg.funky_type <= 1 &&
2957                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2958                   case VK_EXECUTE:
2959                     xkey = 'P';
2960                     break;
2961                   case VK_DIVIDE:
2962                     xkey = 'Q';
2963                     break;
2964                   case VK_MULTIPLY:
2965                     xkey = 'R';
2966                     break;
2967                   case VK_SUBTRACT:
2968                     xkey = 'S';
2969                     break;
2970                 }
2971             if (app_keypad_keys && !cfg.no_applic_k)
2972                 switch (wParam) {
2973                   case VK_NUMPAD0:
2974                     xkey = 'p';
2975                     break;
2976                   case VK_NUMPAD1:
2977                     xkey = 'q';
2978                     break;
2979                   case VK_NUMPAD2:
2980                     xkey = 'r';
2981                     break;
2982                   case VK_NUMPAD3:
2983                     xkey = 's';
2984                     break;
2985                   case VK_NUMPAD4:
2986                     xkey = 't';
2987                     break;
2988                   case VK_NUMPAD5:
2989                     xkey = 'u';
2990                     break;
2991                   case VK_NUMPAD6:
2992                     xkey = 'v';
2993                     break;
2994                   case VK_NUMPAD7:
2995                     xkey = 'w';
2996                     break;
2997                   case VK_NUMPAD8:
2998                     xkey = 'x';
2999                     break;
3000                   case VK_NUMPAD9:
3001                     xkey = 'y';
3002                     break;
3003
3004                   case VK_DECIMAL:
3005                     xkey = 'n';
3006                     break;
3007                   case VK_ADD:
3008                     if (cfg.funky_type == 2) {
3009                         if (shift_state)
3010                             xkey = 'l';
3011                         else
3012                             xkey = 'k';
3013                     } else if (shift_state)
3014                         xkey = 'm';
3015                     else
3016                         xkey = 'l';
3017                     break;
3018
3019                   case VK_DIVIDE:
3020                     if (cfg.funky_type == 2)
3021                         xkey = 'o';
3022                     break;
3023                   case VK_MULTIPLY:
3024                     if (cfg.funky_type == 2)
3025                         xkey = 'j';
3026                     break;
3027                   case VK_SUBTRACT:
3028                     if (cfg.funky_type == 2)
3029                         xkey = 'm';
3030                     break;
3031
3032                   case VK_RETURN:
3033                     if (HIWORD(lParam) & KF_EXTENDED)
3034                         xkey = 'M';
3035                     break;
3036                 }
3037             if (xkey) {
3038                 if (vt52_mode) {
3039                     if (xkey >= 'P' && xkey <= 'S')
3040                         p += sprintf((char *) p, "\x1B%c", xkey);
3041                     else
3042                         p += sprintf((char *) p, "\x1B?%c", xkey);
3043                 } else
3044                     p += sprintf((char *) p, "\x1BO%c", xkey);
3045                 return p - output;
3046             }
3047         }
3048
3049         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3050             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3051             *p++ = 0;
3052             return -2;
3053         }
3054         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3055             *p++ = 0x1B;
3056             *p++ = '[';
3057             *p++ = 'Z';
3058             return p - output;
3059         }
3060         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3061             *p++ = 0;
3062             return p - output;
3063         }
3064         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3065             *p++ = 160;
3066             return p - output;
3067         }
3068         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3069             *p++ = 3;
3070             *p++ = 0;
3071             return -2;
3072         }
3073         if (wParam == VK_PAUSE) {      /* Break/Pause */
3074             *p++ = 26;
3075             *p++ = 0;
3076             return -2;
3077         }
3078         /* Control-2 to Control-8 are special */
3079         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3080             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3081             return p - output;
3082         }
3083         if (shift_state == 2 && wParam == 0xBD) {
3084             *p++ = 0x1F;
3085             return p - output;
3086         }
3087         if (shift_state == 2 && wParam == 0xDF) {
3088             *p++ = 0x1C;
3089             return p - output;
3090         }
3091         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3092             *p++ = '\r';
3093             *p++ = '\n';
3094             return p - output;
3095         }
3096
3097         /*
3098          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3099          * for integer decimal nn.)
3100          *
3101          * We also deal with the weird ones here. Linux VCs replace F1
3102          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3103          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3104          * respectively.
3105          */
3106         code = 0;
3107         switch (wParam) {
3108           case VK_F1:
3109             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3110             break;
3111           case VK_F2:
3112             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3113             break;
3114           case VK_F3:
3115             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3116             break;
3117           case VK_F4:
3118             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3119             break;
3120           case VK_F5:
3121             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3122             break;
3123           case VK_F6:
3124             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3125             break;
3126           case VK_F7:
3127             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3128             break;
3129           case VK_F8:
3130             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3131             break;
3132           case VK_F9:
3133             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3134             break;
3135           case VK_F10:
3136             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3137             break;
3138           case VK_F11:
3139             code = 23;
3140             break;
3141           case VK_F12:
3142             code = 24;
3143             break;
3144           case VK_F13:
3145             code = 25;
3146             break;
3147           case VK_F14:
3148             code = 26;
3149             break;
3150           case VK_F15:
3151             code = 28;
3152             break;
3153           case VK_F16:
3154             code = 29;
3155             break;
3156           case VK_F17:
3157             code = 31;
3158             break;
3159           case VK_F18:
3160             code = 32;
3161             break;
3162           case VK_F19:
3163             code = 33;
3164             break;
3165           case VK_F20:
3166             code = 34;
3167             break;
3168         }
3169         if ((shift_state&2) == 0) switch (wParam) {
3170           case VK_HOME:
3171             code = 1;
3172             break;
3173           case VK_INSERT:
3174             code = 2;
3175             break;
3176           case VK_DELETE:
3177             code = 3;
3178             break;
3179           case VK_END:
3180             code = 4;
3181             break;
3182           case VK_PRIOR:
3183             code = 5;
3184             break;
3185           case VK_NEXT:
3186             code = 6;
3187             break;
3188         }
3189         /* Reorder edit keys to physical order */
3190         if (cfg.funky_type == 3 && code <= 6)
3191             code = "\0\2\1\4\5\3\6"[code];
3192
3193         if (vt52_mode && code > 0 && code <= 6) {
3194             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3195             return p - output;
3196         }
3197
3198         if (cfg.funky_type == 5 &&     /* SCO function keys */
3199             code >= 11 && code <= 34) {
3200             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3201             int index = 0;
3202             switch (wParam) {
3203               case VK_F1: index = 0; break;
3204               case VK_F2: index = 1; break;
3205               case VK_F3: index = 2; break;
3206               case VK_F4: index = 3; break;
3207               case VK_F5: index = 4; break;
3208               case VK_F6: index = 5; break;
3209               case VK_F7: index = 6; break;
3210               case VK_F8: index = 7; break;
3211               case VK_F9: index = 8; break;
3212               case VK_F10: index = 9; break;
3213               case VK_F11: index = 10; break;
3214               case VK_F12: index = 11; break;
3215             }
3216             if (keystate[VK_SHIFT] & 0x80) index += 12;
3217             if (keystate[VK_CONTROL] & 0x80) index += 24;
3218             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3219             return p - output;
3220         }
3221         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3222             code >= 1 && code <= 6) {
3223             char codes[] = "HL.FIG";
3224             if (code == 3) {
3225                 *p++ = '\x7F';
3226             } else {
3227                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3228             }
3229             return p - output;
3230         }
3231         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3232             int offt = 0;
3233             if (code > 15)
3234                 offt++;
3235             if (code > 21)
3236                 offt++;
3237             if (vt52_mode)
3238                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3239             else
3240                 p +=
3241                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3242             return p - output;
3243         }
3244         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3245             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3246             return p - output;
3247         }
3248         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3249             if (vt52_mode)
3250                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3251             else
3252                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3253             return p - output;
3254         }
3255         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3256             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3257             return p - output;
3258         }
3259         if (code) {
3260             p += sprintf((char *) p, "\x1B[%d~", code);
3261             return p - output;
3262         }
3263
3264         /*
3265          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3266          * some reason seems to send VK_CLEAR to Windows...).
3267          */
3268         {
3269             char xkey = 0;
3270             switch (wParam) {
3271               case VK_UP:
3272                 xkey = 'A';
3273                 break;
3274               case VK_DOWN:
3275                 xkey = 'B';
3276                 break;
3277               case VK_RIGHT:
3278                 xkey = 'C';
3279                 break;
3280               case VK_LEFT:
3281                 xkey = 'D';
3282                 break;
3283               case VK_CLEAR:
3284                 xkey = 'G';
3285                 break;
3286             }
3287             if (xkey) {
3288                 if (vt52_mode)
3289                     p += sprintf((char *) p, "\x1B%c", xkey);
3290                 else {
3291                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3292                     /* VT100 & VT102 manuals both state the app cursor keys
3293                      * only work if the app keypad is on.
3294                      */
3295                     if (!app_keypad_keys)
3296                         app_flg = 0;
3297                     /* Useful mapping of Ctrl-arrows */
3298                     if (shift_state == 2)
3299                         app_flg = !app_flg;
3300
3301                     if (app_flg)
3302                         p += sprintf((char *) p, "\x1BO%c", xkey);
3303                     else
3304                         p += sprintf((char *) p, "\x1B[%c", xkey);
3305                 }
3306                 return p - output;
3307             }
3308         }
3309
3310         /*
3311          * Finally, deal with Return ourselves. (Win95 seems to
3312          * foul it up when Alt is pressed, for some reason.)
3313          */
3314         if (wParam == VK_RETURN) {     /* Return */
3315             *p++ = 0x0D;
3316             *p++ = 0;
3317             return -2;
3318         }
3319
3320         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3321             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3322         else
3323             alt_sum = 0;
3324     }
3325
3326     /* Okay we've done everything interesting; let windows deal with 
3327      * the boring stuff */
3328     {
3329         BOOL capsOn=0;
3330
3331         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3332         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3333             capsOn= !left_alt;
3334             keystate[VK_CAPITAL] = 0;
3335         }
3336
3337         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3338 #ifdef SHOW_TOASCII_RESULT
3339         if (r == 1 && !key_down) {
3340             if (alt_sum) {
3341                 if (in_utf || dbcs_screenfont)
3342                     debug((", (U+%04x)", alt_sum));
3343                 else
3344                     debug((", LCH(%d)", alt_sum));
3345             } else {
3346                 debug((", ACH(%d)", keys[0]));
3347             }
3348         } else if (r > 0) {
3349             int r1;
3350             debug((", ASC("));
3351             for (r1 = 0; r1 < r; r1++) {
3352                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3353             }
3354             debug((")"));
3355         }
3356 #endif
3357         if (r > 0) {
3358             WCHAR keybuf;
3359             p = output;
3360             for (i = 0; i < r; i++) {
3361                 unsigned char ch = (unsigned char) keys[i];
3362
3363                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3364                     compose_char = ch;
3365                     compose_state++;
3366                     continue;
3367                 }
3368                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3369                     int nc;
3370                     compose_state = 0;
3371
3372                     if ((nc = check_compose(compose_char, ch)) == -1) {
3373                         MessageBeep(MB_ICONHAND);
3374                         return 0;
3375                     }
3376                     keybuf = nc;
3377                     luni_send(&keybuf, 1, 1);
3378                     continue;
3379                 }
3380
3381                 compose_state = 0;
3382
3383                 if (!key_down) {
3384                     if (alt_sum) {
3385                         if (in_utf || dbcs_screenfont) {
3386                             keybuf = alt_sum;
3387                             luni_send(&keybuf, 1, 1);
3388                         } else {
3389                             ch = (char) alt_sum;
3390                             /*
3391                              * We need not bother about stdin
3392                              * backlogs here, because in GUI PuTTY
3393                              * we can't do anything about it
3394                              * anyway; there's no means of asking
3395                              * Windows to hold off on KEYDOWN
3396                              * messages. We _have_ to buffer
3397                              * everything we're sent.
3398                              */
3399                             ldisc_send(&ch, 1, 1);
3400                         }
3401                         alt_sum = 0;
3402                     } else
3403                         lpage_send(kbd_codepage, &ch, 1, 1);
3404                 } else {
3405                     if(capsOn && ch < 0x80) {
3406                         WCHAR cbuf[2];
3407                         cbuf[0] = 27;
3408                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3409                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3410                     } else {
3411                         char cbuf[2];
3412                         cbuf[0] = '\033';
3413                         cbuf[1] = ch;
3414                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3415                     }
3416                 }
3417                 show_mouseptr(0);
3418             }
3419
3420             /* This is so the ALT-Numpad and dead keys work correctly. */
3421             keys[0] = 0;
3422
3423             return p - output;
3424         }
3425         /* If we're definitly not building up an ALT-54321 then clear it */
3426         if (!left_alt)
3427             keys[0] = 0;
3428         /* If we will be using alt_sum fix the 256s */
3429         else if (keys[0] && (in_utf || dbcs_screenfont))
3430             keys[0] = 10;
3431     }
3432
3433     /*
3434      * ALT alone may or may not want to bring up the System menu.
3435      * If it's not meant to, we return 0 on presses or releases of
3436      * ALT, to show that we've swallowed the keystroke. Otherwise
3437      * we return -1, which means Windows will give the keystroke
3438      * its default handling (i.e. bring up the System menu).
3439      */
3440     if (wParam == VK_MENU && !cfg.alt_only)
3441         return 0;
3442
3443     return -1;
3444 }
3445
3446 void set_title(char *title)
3447 {
3448     sfree(window_name);
3449     window_name = smalloc(1 + strlen(title));
3450     strcpy(window_name, title);
3451     if (cfg.win_name_always || !IsIconic(hwnd))
3452         SetWindowText(hwnd, title);
3453 }
3454
3455 void set_icon(char *title)
3456 {
3457     sfree(icon_name);
3458     icon_name = smalloc(1 + strlen(title));
3459     strcpy(icon_name, title);
3460     if (!cfg.win_name_always && IsIconic(hwnd))
3461         SetWindowText(hwnd, title);
3462 }
3463
3464 void set_sbar(int total, int start, int page)
3465 {
3466     SCROLLINFO si;
3467
3468     if (!cfg.scrollbar)
3469         return;
3470
3471     si.cbSize = sizeof(si);
3472     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3473     si.nMin = 0;
3474     si.nMax = total - 1;
3475     si.nPage = page;
3476     si.nPos = start;
3477     if (hwnd)
3478         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3479 }
3480
3481 Context get_ctx(void)
3482 {
3483     HDC hdc;
3484     if (hwnd) {
3485         hdc = GetDC(hwnd);
3486         if (hdc && pal)
3487             SelectPalette(hdc, pal, FALSE);
3488         return hdc;
3489     } else
3490         return NULL;
3491 }
3492
3493 void free_ctx(Context ctx)
3494 {
3495     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3496     ReleaseDC(hwnd, ctx);
3497 }
3498
3499 static void real_palette_set(int n, int r, int g, int b)
3500 {
3501     if (pal) {
3502         logpal->palPalEntry[n].peRed = r;
3503         logpal->palPalEntry[n].peGreen = g;
3504         logpal->palPalEntry[n].peBlue = b;
3505         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3506         colours[n] = PALETTERGB(r, g, b);
3507         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3508     } else
3509         colours[n] = RGB(r, g, b);
3510 }
3511
3512 void palette_set(int n, int r, int g, int b)
3513 {
3514     static const int first[21] = {
3515         0, 2, 4, 6, 8, 10, 12, 14,
3516         1, 3, 5, 7, 9, 11, 13, 15,
3517         16, 17, 18, 20, 22
3518     };
3519     real_palette_set(first[n], r, g, b);
3520     if (first[n] >= 18)
3521         real_palette_set(first[n] + 1, r, g, b);
3522     if (pal) {
3523         HDC hdc = get_ctx();
3524         UnrealizeObject(pal);
3525         RealizePalette(hdc);
3526         free_ctx(hdc);
3527     }
3528 }
3529
3530 void palette_reset(void)
3531 {
3532     int i;
3533
3534     for (i = 0; i < NCOLOURS; i++) {
3535         if (pal) {
3536             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3537             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3538             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3539             logpal->palPalEntry[i].peFlags = 0;
3540             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3541                                     defpal[i].rgbtGreen,
3542                                     defpal[i].rgbtBlue);
3543         } else
3544             colours[i] = RGB(defpal[i].rgbtRed,
3545                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3546     }
3547
3548     if (pal) {
3549         HDC hdc;
3550         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3551         hdc = get_ctx();
3552         RealizePalette(hdc);
3553         free_ctx(hdc);
3554     }
3555 }
3556
3557 void write_aclip(char *data, int len, int must_deselect)
3558 {
3559     HGLOBAL clipdata;
3560     void *lock;
3561
3562     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3563     if (!clipdata)
3564         return;
3565     lock = GlobalLock(clipdata);
3566     if (!lock)
3567         return;
3568     memcpy(lock, data, len);
3569     ((unsigned char *) lock)[len] = 0;
3570     GlobalUnlock(clipdata);
3571
3572     if (!must_deselect)
3573         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3574
3575     if (OpenClipboard(hwnd)) {
3576         EmptyClipboard();
3577         SetClipboardData(CF_TEXT, clipdata);
3578         CloseClipboard();
3579     } else
3580         GlobalFree(clipdata);
3581
3582     if (!must_deselect)
3583         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3584 }
3585
3586 /*
3587  * Note: unlike write_aclip() this will not append a nul.
3588  */
3589 void write_clip(wchar_t * data, int len, int must_deselect)
3590 {
3591     HGLOBAL clipdata;
3592     HGLOBAL clipdata2;
3593     int len2;
3594     void *lock, *lock2;
3595
3596     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3597
3598     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3599                            len * sizeof(wchar_t));
3600     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3601
3602     if (!clipdata || !clipdata2) {
3603         if (clipdata)
3604             GlobalFree(clipdata);
3605         if (clipdata2)
3606             GlobalFree(clipdata2);
3607         return;
3608     }
3609     if (!(lock = GlobalLock(clipdata)))
3610         return;
3611     if (!(lock2 = GlobalLock(clipdata2)))
3612         return;
3613
3614     memcpy(lock, data, len * sizeof(wchar_t));
3615     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3616
3617     GlobalUnlock(clipdata);
3618     GlobalUnlock(clipdata2);
3619
3620     if (!must_deselect)
3621         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3622
3623     if (OpenClipboard(hwnd)) {
3624         EmptyClipboard();
3625         SetClipboardData(CF_UNICODETEXT, clipdata);
3626         SetClipboardData(CF_TEXT, clipdata2);
3627         CloseClipboard();
3628     } else {
3629         GlobalFree(clipdata);
3630         GlobalFree(clipdata2);
3631     }
3632
3633     if (!must_deselect)
3634         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3635 }
3636
3637 void get_clip(wchar_t ** p, int *len)
3638 {
3639     static HGLOBAL clipdata = NULL;
3640     static wchar_t *converted = 0;
3641     wchar_t *p2;
3642
3643     if (converted) {
3644         sfree(converted);
3645         converted = 0;
3646     }
3647     if (!p) {
3648         if (clipdata)
3649             GlobalUnlock(clipdata);
3650         clipdata = NULL;
3651         return;
3652     } else if (OpenClipboard(NULL)) {
3653         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3654             CloseClipboard();
3655             *p = GlobalLock(clipdata);
3656             if (*p) {
3657                 for (p2 = *p; *p2; p2++);
3658                 *len = p2 - *p;
3659                 return;
3660             }
3661         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3662             char *s;
3663             int i;
3664             CloseClipboard();
3665             s = GlobalLock(clipdata);
3666             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3667             *p = converted = smalloc(i * sizeof(wchar_t));
3668             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3669             *len = i - 1;
3670             return;
3671         } else
3672             CloseClipboard();
3673     }
3674
3675     *p = NULL;
3676     *len = 0;
3677 }
3678
3679 #if 0
3680 /*
3681  * Move `lines' lines from position `from' to position `to' in the
3682  * window.
3683  */
3684 void optimised_move(int to, int from, int lines)
3685 {
3686     RECT r;
3687     int min, max;
3688
3689     min = (to < from ? to : from);
3690     max = to + from - min;
3691
3692     r.left = offset_width;
3693     r.right = offset_width + cols * font_width;
3694     r.top = offset_height + min * font_height;
3695     r.bottom = offset_height + (max + lines) * font_height;
3696     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3697 }
3698 #endif
3699
3700 /*
3701  * Print a message box and perform a fatal exit.
3702  */
3703 void fatalbox(char *fmt, ...)
3704 {
3705     va_list ap;
3706     char stuff[200];
3707
3708     va_start(ap, fmt);
3709     vsprintf(stuff, fmt, ap);
3710     va_end(ap);
3711     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3712     exit(1);
3713 }
3714
3715 /*
3716  * Manage window caption / taskbar flashing, if enabled.
3717  * 0 = stop, 1 = maintain, 2 = start
3718  */
3719 static void flash_window(int mode)
3720 {
3721     static long last_flash = 0;
3722     static int flashing = 0;
3723     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3724         /* stop */
3725         if (flashing) {
3726             FlashWindow(hwnd, FALSE);
3727             flashing = 0;
3728         }
3729
3730     } else if (mode == 2) {
3731         /* start */
3732         if (!flashing) {
3733             last_flash = GetTickCount();
3734             flashing = 1;
3735             FlashWindow(hwnd, TRUE);
3736         }
3737
3738     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3739         /* maintain */
3740         if (flashing) {
3741             long now = GetTickCount();
3742             long fdiff = now - last_flash;
3743             if (fdiff < 0 || fdiff > 450) {
3744                 last_flash = now;
3745                 FlashWindow(hwnd, TRUE);        /* toggle */
3746             }
3747         }
3748     }
3749 }
3750
3751 /*
3752  * Beep.
3753  */
3754 void beep(int mode)
3755 {
3756     if (mode == BELL_DEFAULT) {
3757         /*
3758          * For MessageBeep style bells, we want to be careful of
3759          * timing, because they don't have the nice property of
3760          * PlaySound bells that each one cancels the previous
3761          * active one. So we limit the rate to one per 50ms or so.
3762          */
3763         static long lastbeep = 0;
3764         long beepdiff;
3765
3766         beepdiff = GetTickCount() - lastbeep;
3767         if (beepdiff >= 0 && beepdiff < 50)
3768             return;
3769         MessageBeep(MB_OK);
3770         /*
3771          * The above MessageBeep call takes time, so we record the
3772          * time _after_ it finishes rather than before it starts.
3773          */
3774         lastbeep = GetTickCount();
3775     } else if (mode == BELL_WAVEFILE) {
3776         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3777             char buf[sizeof(cfg.bell_wavefile) + 80];
3778             sprintf(buf, "Unable to play sound file\n%s\n"
3779                     "Using default sound instead", cfg.bell_wavefile);
3780             MessageBox(hwnd, buf, "PuTTY Sound Error",
3781                        MB_OK | MB_ICONEXCLAMATION);
3782             cfg.beep = BELL_DEFAULT;
3783         }
3784     }
3785     /* Otherwise, either visual bell or disabled; do nothing here */
3786     if (!has_focus) {
3787         flash_window(2);               /* start */
3788     }
3789 }
3790
3791 /*
3792  * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3793  * implementation.
3794  */
3795 static void flip_full_screen(void)
3796 {
3797     if (!full_screen) {
3798         int cx, cy;
3799
3800         cx = GetSystemMetrics(SM_CXSCREEN);
3801         cy = GetSystemMetrics(SM_CYSCREEN);
3802         GetWindowPlacement(hwnd, &old_wind_placement);
3803         old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3804         SetWindowLong(hwnd, GWL_STYLE,
3805                       old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3806         SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3807         full_screen = 1;
3808     } else {
3809         SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3810         SetWindowPlacement(hwnd,&old_wind_placement);
3811         full_screen = 0;
3812     }
3813 }