]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Revamp the window-resize behaviour UI so there are only three states
[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.resize_action == RESIZE_DISABLED)
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.resize_action != RESIZE_FONT)
1096             return;
1097     }
1098
1099     if (cfg.resize_action == RESIZE_DISABLED) 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.resize_action != RESIZE_FONT) {
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.resize_action == RESIZE_FONT) {
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.resize_action != RESIZE_FONT  && 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.resize_action != RESIZE_TERM)
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.resize_action == RESIZE_DISABLED)
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.resize_action == RESIZE_DISABLED && 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.resize_action != RESIZE_FONT &&
1595                      prev_cfg.resize_action == RESIZE_FONT))
1596                     init_lvl = 2;
1597
1598                 InvalidateRect(hwnd, NULL, TRUE);
1599                 reset_window(init_lvl);
1600             }
1601             break;
1602           case IDM_COPYALL:
1603             term_copyall();
1604             break;
1605           case IDM_CLRSB:
1606             term_clrsb();
1607             break;
1608           case IDM_RESET:
1609             term_pwron();
1610             break;
1611           case IDM_TEL_AYT:
1612             back->special(TS_AYT);
1613             break;
1614           case IDM_TEL_BRK:
1615             back->special(TS_BRK);
1616             break;
1617           case IDM_TEL_SYNCH:
1618             back->special(TS_SYNCH);
1619             break;
1620           case IDM_TEL_EC:
1621             back->special(TS_EC);
1622             break;
1623           case IDM_TEL_EL:
1624             back->special(TS_EL);
1625             break;
1626           case IDM_TEL_GA:
1627             back->special(TS_GA);
1628             break;
1629           case IDM_TEL_NOP:
1630             back->special(TS_NOP);
1631             break;
1632           case IDM_TEL_ABORT:
1633             back->special(TS_ABORT);
1634             break;
1635           case IDM_TEL_AO:
1636             back->special(TS_AO);
1637             break;
1638           case IDM_TEL_IP:
1639             back->special(TS_IP);
1640             break;
1641           case IDM_TEL_SUSP:
1642             back->special(TS_SUSP);
1643             break;
1644           case IDM_TEL_EOR:
1645             back->special(TS_EOR);
1646             break;
1647           case IDM_TEL_EOF:
1648             back->special(TS_EOF);
1649             break;
1650           case IDM_ABOUT:
1651             showabout(hwnd);
1652             break;
1653           case SC_KEYMENU:
1654             /*
1655              * We get this if the System menu has been activated.
1656              * This might happen from within TranslateKey, in which
1657              * case it really wants to be followed by a `space'
1658              * character to actually _bring the menu up_ rather
1659              * than just sitting there in `ready to appear' state.
1660              */
1661             if( lParam == 0 )
1662                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1663             break;
1664           default:
1665             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1666                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1667             }
1668         }
1669         break;
1670
1671 #define X_POS(l) ((int)(short)LOWORD(l))
1672 #define Y_POS(l) ((int)(short)HIWORD(l))
1673
1674 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1675 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1676 #define WHEEL_DELTA 120
1677       case WM_MOUSEWHEEL:
1678         {
1679             wheel_accumulator += (short) HIWORD(wParam);
1680             wParam = LOWORD(wParam);
1681
1682             /* process events when the threshold is reached */
1683             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1684                 int b;
1685
1686                 /* reduce amount for next time */
1687                 if (wheel_accumulator > 0) {
1688                     b = MBT_WHEEL_UP;
1689                     wheel_accumulator -= WHEEL_DELTA;
1690                 } else if (wheel_accumulator < 0) {
1691                     b = MBT_WHEEL_DOWN;
1692                     wheel_accumulator += WHEEL_DELTA;
1693                 } else
1694                     break;
1695
1696                 if (send_raw_mouse) {
1697                     /* send a mouse-down followed by a mouse up */
1698                     term_mouse(b,
1699                                MA_CLICK,
1700                                TO_CHR_X(X_POS(lParam)),
1701                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1702                                wParam & MK_CONTROL);
1703                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1704                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1705                                wParam & MK_CONTROL);
1706                 } else {
1707                     /* trigger a scroll */
1708                     term_scroll(0,
1709                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1710                 }
1711             }
1712             return 0;
1713         }
1714       case WM_LBUTTONDOWN:
1715       case WM_MBUTTONDOWN:
1716       case WM_RBUTTONDOWN:
1717       case WM_LBUTTONUP:
1718       case WM_MBUTTONUP:
1719       case WM_RBUTTONUP:
1720         {
1721             int button, press;
1722             switch (message) {
1723               case WM_LBUTTONDOWN:
1724                 button = MBT_LEFT;
1725                 press = 1;
1726                 break;
1727               case WM_MBUTTONDOWN:
1728                 button = MBT_MIDDLE;
1729                 press = 1;
1730                 break;
1731               case WM_RBUTTONDOWN:
1732                 button = MBT_RIGHT;
1733                 press = 1;
1734                 break;
1735               case WM_LBUTTONUP:
1736                 button = MBT_LEFT;
1737                 press = 0;
1738                 break;
1739               case WM_MBUTTONUP:
1740                 button = MBT_MIDDLE;
1741                 press = 0;
1742                 break;
1743               case WM_RBUTTONUP:
1744                 button = MBT_RIGHT;
1745                 press = 0;
1746                 break;
1747               default:
1748                 button = press = 0;    /* shouldn't happen */
1749             }
1750             show_mouseptr(1);
1751             if (press) {
1752                 click(button,
1753                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1754                       wParam & MK_SHIFT, wParam & MK_CONTROL);
1755                 SetCapture(hwnd);
1756             } else {
1757                 term_mouse(button, MA_RELEASE,
1758                            TO_CHR_X(X_POS(lParam)),
1759                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1760                            wParam & MK_CONTROL);
1761                 ReleaseCapture();
1762             }
1763         }
1764         return 0;
1765       case WM_MOUSEMOVE:
1766         show_mouseptr(1);
1767         /*
1768          * Add the mouse position and message time to the random
1769          * number noise.
1770          */
1771         noise_ultralight(lParam);
1772
1773         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1774             Mouse_Button b;
1775             if (wParam & MK_LBUTTON)
1776                 b = MBT_LEFT;
1777             else if (wParam & MK_MBUTTON)
1778                 b = MBT_MIDDLE;
1779             else
1780                 b = MBT_RIGHT;
1781             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1782                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1783                        wParam & MK_CONTROL);
1784         }
1785         return 0;
1786       case WM_NCMOUSEMOVE:
1787         show_mouseptr(1);
1788         noise_ultralight(lParam);
1789         return 0;
1790       case WM_IGNORE_CLIP:
1791         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1792         break;
1793       case WM_DESTROYCLIPBOARD:
1794         if (!ignore_clip)
1795             term_deselect();
1796         ignore_clip = FALSE;
1797         return 0;
1798       case WM_PAINT:
1799         {
1800             PAINTSTRUCT p;
1801             HideCaret(hwnd);
1802             hdc = BeginPaint(hwnd, &p);
1803             if (pal) {
1804                 SelectPalette(hdc, pal, TRUE);
1805                 RealizePalette(hdc);
1806             }
1807             term_paint(hdc, 
1808                        (p.rcPaint.left-offset_width)/font_width,
1809                        (p.rcPaint.top-offset_height)/font_height,
1810                        (p.rcPaint.right-offset_width-1)/font_width,
1811                        (p.rcPaint.bottom-offset_height-1)/font_height);
1812
1813             if (p.fErase ||
1814                 p.rcPaint.left  < offset_width  ||
1815                 p.rcPaint.top   < offset_height ||
1816                 p.rcPaint.right >= offset_width + font_width*cols ||
1817                 p.rcPaint.bottom>= offset_height + font_height*rows)
1818             {
1819                 HBRUSH fillcolour, oldbrush;
1820                 HPEN   edge, oldpen;
1821                 fillcolour = CreateSolidBrush (
1822                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1823                 oldbrush = SelectObject(hdc, fillcolour);
1824                 edge = CreatePen(PS_SOLID, 0, 
1825                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1826                 oldpen = SelectObject(hdc, edge);
1827
1828                 ExcludeClipRect(hdc, 
1829                         offset_width, offset_height,
1830                         offset_width+font_width*cols,
1831                         offset_height+font_height*rows);
1832
1833                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
1834                           p.rcPaint.right, p.rcPaint.bottom);
1835
1836                 // SelectClipRgn(hdc, NULL);
1837
1838                 SelectObject(hdc, oldbrush);
1839                 DeleteObject(fillcolour);
1840                 SelectObject(hdc, oldpen);
1841                 DeleteObject(edge);
1842             }
1843             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1844             SelectObject(hdc, GetStockObject(WHITE_PEN));
1845             EndPaint(hwnd, &p);
1846             ShowCaret(hwnd);
1847         }
1848         return 0;
1849       case WM_NETEVENT:
1850         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1851          * but the only one that's likely to try to overload us is FD_READ.
1852          * This means buffering just one is fine.
1853          */
1854         if (pending_netevent)
1855             enact_pending_netevent();
1856
1857         pending_netevent = TRUE;
1858         pend_netevent_wParam = wParam;
1859         pend_netevent_lParam = lParam;
1860         if (WSAGETSELECTEVENT(lParam) != FD_READ)
1861             enact_pending_netevent();
1862
1863         time(&last_movement);
1864         return 0;
1865       case WM_SETFOCUS:
1866         has_focus = TRUE;
1867         CreateCaret(hwnd, caretbm, font_width, font_height);
1868         ShowCaret(hwnd);
1869         flash_window(0);               /* stop */
1870         compose_state = 0;
1871         term_out();
1872         term_update();
1873         break;
1874       case WM_KILLFOCUS:
1875         show_mouseptr(1);
1876         has_focus = FALSE;
1877         DestroyCaret();
1878         term_out();
1879         term_update();
1880         break;
1881       case WM_ENTERSIZEMOVE:
1882 #ifdef RDB_DEBUG_PATCH
1883         debug((27, "WM_ENTERSIZEMOVE"));
1884 #endif
1885         EnableSizeTip(1);
1886         resizing = TRUE;
1887         need_backend_resize = FALSE;
1888         break;
1889       case WM_EXITSIZEMOVE:
1890         EnableSizeTip(0);
1891         resizing = FALSE;
1892 #ifdef RDB_DEBUG_PATCH
1893         debug((27, "WM_EXITSIZEMOVE"));
1894 #endif
1895         if (need_backend_resize) {
1896             term_size(cfg.height, cfg.width, cfg.savelines);
1897             InvalidateRect(hwnd, NULL, TRUE);
1898         }
1899         break;
1900       case WM_SIZING:
1901         /*
1902          * This does two jobs:
1903          * 1) Keep the sizetip uptodate
1904          * 2) Make sure the window size is _stepped_ in units of the font size.
1905          */
1906         if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1907             int width, height, w, h, ew, eh;
1908             LPRECT r = (LPRECT) lParam;
1909
1910             if ( !need_backend_resize && 
1911                     (cfg.height != rows || cfg.width != cols )) {
1912                 /* 
1913                  * Great! It seems the host has been changing the terminal
1914                  * size, well the user is now grabbing so this is probably
1915                  * the least confusing solution in the long run even though
1916                  * it a is suprise. Unfortunatly the only way to prevent 
1917                  * this seems to be to let the host change the window size 
1918                  * and as that's a user option we're still right back here.
1919                  */
1920                 term_size(cfg.height, cfg.width, cfg.savelines);
1921                 reset_window(2);
1922                 InvalidateRect(hwnd, NULL, TRUE);
1923                 need_backend_resize = TRUE;
1924             }
1925
1926             width = r->right - r->left - extra_width;
1927             height = r->bottom - r->top - extra_height;
1928             w = (width + font_width / 2) / font_width;
1929             if (w < 1)
1930                 w = 1;
1931             h = (height + font_height / 2) / font_height;
1932             if (h < 1)
1933                 h = 1;
1934             UpdateSizeTip(hwnd, w, h);
1935             ew = width - w * font_width;
1936             eh = height - h * font_height;
1937             if (ew != 0) {
1938                 if (wParam == WMSZ_LEFT ||
1939                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1940                     r->left += ew;
1941                 else
1942                     r->right -= ew;
1943             }
1944             if (eh != 0) {
1945                 if (wParam == WMSZ_TOP ||
1946                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1947                     r->top += eh;
1948                 else
1949                     r->bottom -= eh;
1950             }
1951             if (ew || eh)
1952                 return 1;
1953             else
1954                 return 0;
1955         } else {
1956             int width, height, w, h, rv = 0;
1957             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1958             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1959             LPRECT r = (LPRECT) lParam;
1960
1961             width = r->right - r->left - ex_width;
1962             height = r->bottom - r->top - ex_height;
1963
1964             w = (width + cols/2)/cols;
1965             h = (height + rows/2)/rows;
1966             if ( r->right != r->left + w*cols + ex_width) 
1967                 rv = 1;
1968
1969             if (wParam == WMSZ_LEFT ||
1970                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1971                 r->left = r->right - w*cols - ex_width;
1972             else
1973                 r->right = r->left + w*cols + ex_width;
1974
1975             if (r->bottom != r->top + h*rows + ex_height)
1976                 rv = 1;
1977
1978             if (wParam == WMSZ_TOP ||
1979                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1980                 r->top = r->bottom - h*rows - ex_height;
1981             else
1982                 r->bottom = r->top + h*rows + ex_height;
1983
1984             return rv;
1985         }
1986         break;
1987         /* break;  (never reached) */
1988       case WM_SIZE:
1989 #ifdef RDB_DEBUG_PATCH
1990         debug((27, "WM_SIZE %s (%d,%d)",
1991                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
1992                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
1993                 (wParam == SIZE_RESTORED && resizing) ? "to":
1994                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
1995                 "...",
1996             LOWORD(lParam), HIWORD(lParam)));
1997 #endif
1998         if (wParam == SIZE_MINIMIZED) {
1999             SetWindowText(hwnd,
2000                           cfg.win_name_always ? window_name : icon_name);
2001             break;
2002         }
2003         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2004             SetWindowText(hwnd, window_name);
2005
2006         if (cfg.resize_action == RESIZE_DISABLED) {
2007             /* A resize, well it better be a minimize. */
2008             reset_window(-1);
2009         } else {
2010
2011             int width, height, w, h;
2012
2013             width = LOWORD(lParam);
2014             height = HIWORD(lParam);
2015
2016             if (!resizing) {
2017                 if (wParam == SIZE_MAXIMIZED) {
2018                     was_zoomed = 1;
2019                     prev_rows = rows;
2020                     prev_cols = cols;
2021                     if (cfg.resize_action != RESIZE_FONT) {
2022                         w = width / font_width;
2023                         if (w < 1) w = 1;
2024                         h = height / font_height;
2025                         if (h < 1) h = 1;
2026
2027                         term_size(h, w, cfg.savelines);
2028                     }
2029                     reset_window(0);
2030                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2031                     was_zoomed = 0;
2032                     if (cfg.resize_action != RESIZE_FONT)
2033                         term_size(prev_rows, prev_cols, cfg.savelines);
2034                     reset_window(0);
2035                 }
2036                 /* This is an unexpected resize, these will normally happen
2037                  * if the window is too large. Probably either the user
2038                  * selected a huge font or the screen size has changed.
2039                  *
2040                  * This is also called with minimize.
2041                  */
2042                 else reset_window(-1);
2043             }
2044
2045             /*
2046              * Don't call back->size in mid-resize. (To prevent
2047              * massive numbers of resize events getting sent
2048              * down the connection during an NT opaque drag.)
2049              */
2050             if (resizing) {
2051                 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2052                     need_backend_resize = TRUE;
2053                     w = (width-cfg.window_border*2) / font_width;
2054                     if (w < 1) w = 1;
2055                     h = (height-cfg.window_border*2) / font_height;
2056                     if (h < 1) h = 1;
2057
2058                     cfg.height = h;
2059                     cfg.width = w;
2060                 } else 
2061                     reset_window(0);
2062             }
2063         }
2064         return 0;
2065       case WM_VSCROLL:
2066         switch (LOWORD(wParam)) {
2067           case SB_BOTTOM:
2068             term_scroll(-1, 0);
2069             break;
2070           case SB_TOP:
2071             term_scroll(+1, 0);
2072             break;
2073           case SB_LINEDOWN:
2074             term_scroll(0, +1);
2075             break;
2076           case SB_LINEUP:
2077             term_scroll(0, -1);
2078             break;
2079           case SB_PAGEDOWN:
2080             term_scroll(0, +rows / 2);
2081             break;
2082           case SB_PAGEUP:
2083             term_scroll(0, -rows / 2);
2084             break;
2085           case SB_THUMBPOSITION:
2086           case SB_THUMBTRACK:
2087             term_scroll(1, HIWORD(wParam));
2088             break;
2089         }
2090         break;
2091       case WM_PALETTECHANGED:
2092         if ((HWND) wParam != hwnd && pal != NULL) {
2093             HDC hdc = get_ctx();
2094             if (hdc) {
2095                 if (RealizePalette(hdc) > 0)
2096                     UpdateColors(hdc);
2097                 free_ctx(hdc);
2098             }
2099         }
2100         break;
2101       case WM_QUERYNEWPALETTE:
2102         if (pal != NULL) {
2103             HDC hdc = get_ctx();
2104             if (hdc) {
2105                 if (RealizePalette(hdc) > 0)
2106                     UpdateColors(hdc);
2107                 free_ctx(hdc);
2108                 return TRUE;
2109             }
2110         }
2111         return FALSE;
2112       case WM_KEYDOWN:
2113       case WM_SYSKEYDOWN:
2114       case WM_KEYUP:
2115       case WM_SYSKEYUP:
2116         /*
2117          * Add the scan code and keypress timing to the random
2118          * number noise.
2119          */
2120         noise_ultralight(lParam);
2121
2122         /*
2123          * We don't do TranslateMessage since it disassociates the
2124          * resulting CHAR message from the KEYDOWN that sparked it,
2125          * which we occasionally don't want. Instead, we process
2126          * KEYDOWN, and call the Win32 translator functions so that
2127          * we get the translations under _our_ control.
2128          */
2129         {
2130             unsigned char buf[20];
2131             int len;
2132
2133             if (wParam == VK_PROCESSKEY) {
2134                 MSG m;
2135                 m.hwnd = hwnd;
2136                 m.message = WM_KEYDOWN;
2137                 m.wParam = wParam;
2138                 m.lParam = lParam & 0xdfff;
2139                 TranslateMessage(&m);
2140             } else {
2141                 len = TranslateKey(message, wParam, lParam, buf);
2142                 if (len == -1)
2143                     return DefWindowProc(hwnd, message, wParam, lParam);
2144
2145                 if (len != 0) {
2146                     /*
2147                      * Interrupt an ongoing paste. I'm not sure
2148                      * this is sensible, but for the moment it's
2149                      * preferable to having to faff about buffering
2150                      * things.
2151                      */
2152                     term_nopaste();
2153
2154                     /*
2155                      * We need not bother about stdin backlogs
2156                      * here, because in GUI PuTTY we can't do
2157                      * anything about it anyway; there's no means
2158                      * of asking Windows to hold off on KEYDOWN
2159                      * messages. We _have_ to buffer everything
2160                      * we're sent.
2161                      */
2162                     ldisc_send(buf, len, 1);
2163                     show_mouseptr(0);
2164                 }
2165             }
2166         }
2167         return 0;
2168       case WM_INPUTLANGCHANGE:
2169         {
2170             /* wParam == Font number */
2171             /* lParam == Locale */
2172             char lbuf[20];
2173             HKL NewInputLocale = (HKL) lParam;
2174
2175             // lParam == GetKeyboardLayout(0);
2176
2177             GetLocaleInfo(LOWORD(NewInputLocale),
2178                           LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2179
2180             kbd_codepage = atoi(lbuf);
2181         }
2182         break;
2183       case WM_IME_COMPOSITION:
2184         {
2185             HIMC hIMC;
2186             int n;
2187             char *buff;
2188    
2189             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2190                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2191
2192             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2193                 break; /* fall back to DefWindowProc */
2194
2195             hIMC = ImmGetContext(hwnd);
2196             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2197
2198             if (n > 0) {
2199                 buff = (char*) smalloc(n);
2200                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2201                 luni_send((unsigned short *)buff, n / 2, 1);
2202                 free(buff);
2203             }
2204             ImmReleaseContext(hwnd, hIMC);
2205             return 1;
2206         }
2207
2208       case WM_IME_CHAR:
2209         if (wParam & 0xFF00) {
2210             unsigned char buf[2];
2211
2212             buf[1] = wParam;
2213             buf[0] = wParam >> 8;
2214             lpage_send(kbd_codepage, buf, 2, 1);
2215         } else {
2216             char c = (unsigned char) wParam;
2217             lpage_send(kbd_codepage, &c, 1, 1);
2218         }
2219         return (0);
2220       case WM_CHAR:
2221       case WM_SYSCHAR:
2222         /*
2223          * Nevertheless, we are prepared to deal with WM_CHAR
2224          * messages, should they crop up. So if someone wants to
2225          * post the things to us as part of a macro manoeuvre,
2226          * we're ready to cope.
2227          */
2228         {
2229             char c = (unsigned char)wParam;
2230             lpage_send(CP_ACP, &c, 1, 1);
2231         }
2232         return 0;
2233       case WM_SETCURSOR:
2234         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2235             SetCursor(LoadCursor(NULL, IDC_ARROW));
2236             return TRUE;
2237         }
2238     }
2239
2240     return DefWindowProc(hwnd, message, wParam, lParam);
2241 }
2242
2243 /*
2244  * Move the system caret. (We maintain one, even though it's
2245  * invisible, for the benefit of blind people: apparently some
2246  * helper software tracks the system caret, so we should arrange to
2247  * have one.)
2248  */
2249 void sys_cursor(int x, int y)
2250 {
2251     COMPOSITIONFORM cf;
2252     HIMC hIMC;
2253
2254     if (!has_focus) return;
2255     
2256     SetCaretPos(x * font_width + offset_width,
2257                 y * font_height + offset_height);
2258
2259     /* IMM calls on Win98 and beyond only */
2260     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2261     
2262     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2263             osVersion.dwMinorVersion == 0) return; /* 95 */
2264
2265     /* we should have the IMM functions */
2266     hIMC = ImmGetContext(hwnd);
2267     cf.dwStyle = CFS_POINT;
2268     cf.ptCurrentPos.x = x * font_width;
2269     cf.ptCurrentPos.y = y * font_height;
2270     ImmSetCompositionWindow(hIMC, &cf);
2271
2272     ImmReleaseContext(hwnd, hIMC);
2273 }
2274
2275 /*
2276  * Draw a line of text in the window, at given character
2277  * coordinates, in given attributes.
2278  *
2279  * We are allowed to fiddle with the contents of `text'.
2280  */
2281 void do_text(Context ctx, int x, int y, char *text, int len,
2282              unsigned long attr, int lattr)
2283 {
2284     COLORREF fg, bg, t;
2285     int nfg, nbg, nfont;
2286     HDC hdc = ctx;
2287     RECT line_box;
2288     int force_manual_underline = 0;
2289     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2290     int char_width = fnt_width;
2291     int text_adjust = 0;
2292     static int *IpDx = 0, IpDxLEN = 0;
2293
2294     if (attr & ATTR_WIDE)
2295         char_width *= 2;
2296
2297     if (len > IpDxLEN || IpDx[0] != char_width) {
2298         int i;
2299         if (len > IpDxLEN) {
2300             sfree(IpDx);
2301             IpDx = smalloc((len + 16) * sizeof(int));
2302             IpDxLEN = (len + 16);
2303         }
2304         for (i = 0; i < IpDxLEN; i++)
2305             IpDx[i] = char_width;
2306     }
2307
2308     /* Only want the left half of double width lines */
2309     if (lattr != LATTR_NORM && x*2 >= cols)
2310         return;
2311
2312     x *= fnt_width;
2313     y *= font_height;
2314     x += offset_width;
2315     y += offset_height;
2316
2317     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2318         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2319         attr ^= ATTR_CUR_XOR;
2320     }
2321
2322     nfont = 0;
2323     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2324         /* Assume a poorman font is borken in other ways too. */
2325         lattr = LATTR_WIDE;
2326     } else
2327         switch (lattr) {
2328           case LATTR_NORM:
2329             break;
2330           case LATTR_WIDE:
2331             nfont |= FONT_WIDE;
2332             break;
2333           default:
2334             nfont |= FONT_WIDE + FONT_HIGH;
2335             break;
2336         }
2337     if (attr & ATTR_NARROW)
2338         nfont |= FONT_NARROW;
2339
2340     /* Special hack for the VT100 linedraw glyphs. */
2341     if ((attr & CSET_MASK) == 0x2300) {
2342         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2343             switch ((unsigned char) (text[0])) {
2344               case 0xBA:
2345                 text_adjust = -2 * font_height / 5;
2346                 break;
2347               case 0xBB:
2348                 text_adjust = -1 * font_height / 5;
2349                 break;
2350               case 0xBC:
2351                 text_adjust = font_height / 5;
2352                 break;
2353               case 0xBD:
2354                 text_adjust = 2 * font_height / 5;
2355                 break;
2356             }
2357             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2358                 text_adjust *= 2;
2359             attr &= ~CSET_MASK;
2360             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2361             attr |= (unitab_xterm['q'] & CSET_MASK);
2362             if (attr & ATTR_UNDER) {
2363                 attr &= ~ATTR_UNDER;
2364                 force_manual_underline = 1;
2365             }
2366         }
2367     }
2368
2369     /* Anything left as an original character set is unprintable. */
2370     if (DIRECT_CHAR(attr)) {
2371         attr &= ~CSET_MASK;
2372         attr |= 0xFF00;
2373         memset(text, 0xFD, len);
2374     }
2375
2376     /* OEM CP */
2377     if ((attr & CSET_MASK) == ATTR_OEMCP)
2378         nfont |= FONT_OEM;
2379
2380     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2381     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2382     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2383         nfont |= FONT_BOLD;
2384     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2385         nfont |= FONT_UNDERLINE;
2386     another_font(nfont);
2387     if (!fonts[nfont]) {
2388         if (nfont & FONT_UNDERLINE)
2389             force_manual_underline = 1;
2390         /* Don't do the same for manual bold, it could be bad news. */
2391
2392         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2393     }
2394     another_font(nfont);
2395     if (!fonts[nfont])
2396         nfont = FONT_NORMAL;
2397     if (attr & ATTR_REVERSE) {
2398         t = nfg;
2399         nfg = nbg;
2400         nbg = t;
2401     }
2402     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2403         nfg++;
2404     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2405         nbg++;
2406     fg = colours[nfg];
2407     bg = colours[nbg];
2408     SelectObject(hdc, fonts[nfont]);
2409     SetTextColor(hdc, fg);
2410     SetBkColor(hdc, bg);
2411     SetBkMode(hdc, OPAQUE);
2412     line_box.left = x;
2413     line_box.top = y;
2414     line_box.right = x + char_width * len;
2415     line_box.bottom = y + font_height;
2416
2417     /* Only want the left half of double width lines */
2418     if (line_box.right > font_width*cols+offset_width)
2419         line_box.right = font_width*cols+offset_width;
2420
2421     /* We're using a private area for direct to font. (512 chars.) */
2422     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2423         /* Ho Hum, dbcs fonts are a PITA! */
2424         /* To display on W9x I have to convert to UCS */
2425         static wchar_t *uni_buf = 0;
2426         static int uni_len = 0;
2427         int nlen, mptr;
2428         if (len > uni_len) {
2429             sfree(uni_buf);
2430             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2431         }
2432
2433         for(nlen = mptr = 0; mptr<len; mptr++) {
2434             uni_buf[nlen] = 0xFFFD;
2435             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2436                 IpDx[nlen] += char_width;
2437                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2438                                    text+mptr, 2, uni_buf+nlen, 1);
2439                 mptr++;
2440             }
2441             else
2442             {
2443                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2444                                    text+mptr, 1, uni_buf+nlen, 1);
2445             }
2446             nlen++;
2447         }
2448         if (nlen <= 0)
2449             return;                    /* Eeek! */
2450
2451         ExtTextOutW(hdc, x,
2452                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2453                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2454         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2455             SetBkMode(hdc, TRANSPARENT);
2456             ExtTextOutW(hdc, x - 1,
2457                         y - font_height * (lattr ==
2458                                            LATTR_BOT) + text_adjust,
2459                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2460         }
2461
2462         IpDx[0] = -1;
2463     } else if (DIRECT_FONT(attr)) {
2464         ExtTextOut(hdc, x,
2465                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2466                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2467         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2468             SetBkMode(hdc, TRANSPARENT);
2469
2470             /* GRR: This draws the character outside it's box and can leave
2471              * 'droppings' even with the clip box! I suppose I could loop it
2472              * one character at a time ... yuk. 
2473              * 
2474              * Or ... I could do a test print with "W", and use +1 or -1 for this
2475              * shift depending on if the leftmost column is blank...
2476              */
2477             ExtTextOut(hdc, x - 1,
2478                        y - font_height * (lattr ==
2479                                           LATTR_BOT) + text_adjust,
2480                        ETO_CLIPPED, &line_box, text, len, IpDx);
2481         }
2482     } else {
2483         /* And 'normal' unicode characters */
2484         static WCHAR *wbuf = NULL;
2485         static int wlen = 0;
2486         int i;
2487         if (wlen < len) {
2488             sfree(wbuf);
2489             wlen = len;
2490             wbuf = smalloc(wlen * sizeof(WCHAR));
2491         }
2492         for (i = 0; i < len; i++)
2493             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2494
2495         ExtTextOutW(hdc, x,
2496                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2497                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2498
2499         /* And the shadow bold hack. */
2500         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2501             SetBkMode(hdc, TRANSPARENT);
2502             ExtTextOutW(hdc, x - 1,
2503                         y - font_height * (lattr ==
2504                                            LATTR_BOT) + text_adjust,
2505                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2506         }
2507     }
2508     if (lattr != LATTR_TOP && (force_manual_underline ||
2509                                (und_mode == UND_LINE
2510                                 && (attr & ATTR_UNDER)))) {
2511         HPEN oldpen;
2512         int dec = descent;
2513         if (lattr == LATTR_BOT)
2514             dec = dec * 2 - font_height;
2515
2516         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2517         MoveToEx(hdc, x, y + dec, NULL);
2518         LineTo(hdc, x + len * char_width, y + dec);
2519         oldpen = SelectObject(hdc, oldpen);
2520         DeleteObject(oldpen);
2521     }
2522 }
2523
2524 void do_cursor(Context ctx, int x, int y, char *text, int len,
2525                unsigned long attr, int lattr)
2526 {
2527
2528     int fnt_width;
2529     int char_width;
2530     HDC hdc = ctx;
2531     int ctype = cfg.cursor_type;
2532
2533     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2534         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2535             do_text(ctx, x, y, text, len, attr, lattr);
2536             return;
2537         }
2538         ctype = 2;
2539         attr |= TATTR_RIGHTCURS;
2540     }
2541
2542     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2543     if (attr & ATTR_WIDE)
2544         char_width *= 2;
2545     x *= fnt_width;
2546     y *= font_height;
2547     x += offset_width;
2548     y += offset_height;
2549
2550     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2551         POINT pts[5];
2552         HPEN oldpen;
2553         pts[0].x = pts[1].x = pts[4].x = x;
2554         pts[2].x = pts[3].x = x + char_width - 1;
2555         pts[0].y = pts[3].y = pts[4].y = y;
2556         pts[1].y = pts[2].y = y + font_height - 1;
2557         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2558         Polyline(hdc, pts, 5);
2559         oldpen = SelectObject(hdc, oldpen);
2560         DeleteObject(oldpen);
2561     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2562         int startx, starty, dx, dy, length, i;
2563         if (ctype == 1) {
2564             startx = x;
2565             starty = y + descent;
2566             dx = 1;
2567             dy = 0;
2568             length = char_width;
2569         } else {
2570             int xadjust = 0;
2571             if (attr & TATTR_RIGHTCURS)
2572                 xadjust = char_width - 1;
2573             startx = x + xadjust;
2574             starty = y;
2575             dx = 0;
2576             dy = 1;
2577             length = font_height;
2578         }
2579         if (attr & TATTR_ACTCURS) {
2580             HPEN oldpen;
2581             oldpen =
2582                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2583             MoveToEx(hdc, startx, starty, NULL);
2584             LineTo(hdc, startx + dx * length, starty + dy * length);
2585             oldpen = SelectObject(hdc, oldpen);
2586             DeleteObject(oldpen);
2587         } else {
2588             for (i = 0; i < length; i++) {
2589                 if (i % 2 == 0) {
2590                     SetPixel(hdc, startx, starty, colours[23]);
2591                 }
2592                 startx += dx;
2593                 starty += dy;
2594             }
2595         }
2596     }
2597 }
2598
2599 /* This function gets the actual width of a character in the normal font.
2600  */
2601 int CharWidth(Context ctx, int uc) {
2602     HDC hdc = ctx;
2603     int ibuf = 0;
2604
2605     /* If the font max is the same as the font ave width then this
2606      * function is a no-op.
2607      */
2608     if (!font_dualwidth) return 1;
2609
2610     switch (uc & CSET_MASK) {
2611       case ATTR_ASCII:
2612         uc = unitab_line[uc & 0xFF];
2613         break;
2614       case ATTR_LINEDRW:
2615         uc = unitab_xterm[uc & 0xFF];
2616         break;
2617       case ATTR_SCOACS:
2618         uc = unitab_scoacs[uc & 0xFF];
2619         break;
2620     }
2621     if (DIRECT_FONT(uc)) {
2622         if (dbcs_screenfont) return 1;
2623
2624         /* Speedup, I know of no font where ascii is the wrong width */
2625         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2626             return 1;
2627
2628         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2629             SelectObject(hdc, fonts[FONT_NORMAL]);
2630         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2631             another_font(FONT_OEM);
2632             if (!fonts[FONT_OEM]) return 0;
2633
2634             SelectObject(hdc, fonts[FONT_OEM]);
2635         } else
2636             return 0;
2637
2638         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2639              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2640             return 0;
2641     } else {
2642         /* Speedup, I know of no font where ascii is the wrong width */
2643         if (uc >= ' ' && uc <= '~') return 1;
2644
2645         SelectObject(hdc, fonts[FONT_NORMAL]);
2646         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2647             /* Okay that one worked */ ;
2648         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2649             /* This should work on 9x too, but it's "less accurate" */ ;
2650         else
2651             return 0;
2652     }
2653
2654     ibuf += font_width / 2 -1;
2655     ibuf /= font_width;
2656
2657     return ibuf;
2658 }
2659
2660 /*
2661  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2662  * codes. Returns number of bytes used or zero to drop the message
2663  * or -1 to forward the message to windows.
2664  */
2665 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2666                         unsigned char *output)
2667 {
2668     BYTE keystate[256];
2669     int scan, left_alt = 0, key_down, shift_state;
2670     int r, i, code;
2671     unsigned char *p = output;
2672     static int alt_sum = 0;
2673
2674     HKL kbd_layout = GetKeyboardLayout(0);
2675
2676     static WORD keys[3];
2677     static int compose_char = 0;
2678     static WPARAM compose_key = 0;
2679
2680     r = GetKeyboardState(keystate);
2681     if (!r)
2682         memset(keystate, 0, sizeof(keystate));
2683     else {
2684 #if 0
2685 #define SHOW_TOASCII_RESULT
2686         {                              /* Tell us all about key events */
2687             static BYTE oldstate[256];
2688             static int first = 1;
2689             static int scan;
2690             int ch;
2691             if (first)
2692                 memcpy(oldstate, keystate, sizeof(oldstate));
2693             first = 0;
2694
2695             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2696                 debug(("+"));
2697             } else if ((HIWORD(lParam) & KF_UP)
2698                        && scan == (HIWORD(lParam) & 0xFF)) {
2699                 debug((". U"));
2700             } else {
2701                 debug((".\n"));
2702                 if (wParam >= VK_F1 && wParam <= VK_F20)
2703                     debug(("K_F%d", wParam + 1 - VK_F1));
2704                 else
2705                     switch (wParam) {
2706                       case VK_SHIFT:
2707                         debug(("SHIFT"));
2708                         break;
2709                       case VK_CONTROL:
2710                         debug(("CTRL"));
2711                         break;
2712                       case VK_MENU:
2713                         debug(("ALT"));
2714                         break;
2715                       default:
2716                         debug(("VK_%02x", wParam));
2717                     }
2718                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2719                     debug(("*"));
2720                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2721
2722                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2723                 if (ch >= ' ' && ch <= '~')
2724                     debug((", '%c'", ch));
2725                 else if (ch)
2726                     debug((", $%02x", ch));
2727
2728                 if (keys[0])
2729                     debug((", KB0=%02x", keys[0]));
2730                 if (keys[1])
2731                     debug((", KB1=%02x", keys[1]));
2732                 if (keys[2])
2733                     debug((", KB2=%02x", keys[2]));
2734
2735                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2736                     debug((", S"));
2737                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2738                     debug((", C"));
2739                 if ((HIWORD(lParam) & KF_EXTENDED))
2740                     debug((", E"));
2741                 if ((HIWORD(lParam) & KF_UP))
2742                     debug((", U"));
2743             }
2744
2745             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2746             else if ((HIWORD(lParam) & KF_UP))
2747                 oldstate[wParam & 0xFF] ^= 0x80;
2748             else
2749                 oldstate[wParam & 0xFF] ^= 0x81;
2750
2751             for (ch = 0; ch < 256; ch++)
2752                 if (oldstate[ch] != keystate[ch])
2753                     debug((", M%02x=%02x", ch, keystate[ch]));
2754
2755             memcpy(oldstate, keystate, sizeof(oldstate));
2756         }
2757 #endif
2758
2759         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2760             keystate[VK_RMENU] = keystate[VK_MENU];
2761         }
2762
2763
2764         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2765         if ((cfg.funky_type == 3 ||
2766              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2767             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2768
2769             wParam = VK_EXECUTE;
2770
2771             /* UnToggle NUMLock */
2772             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2773                 keystate[VK_NUMLOCK] ^= 1;
2774         }
2775
2776         /* And write back the 'adjusted' state */
2777         SetKeyboardState(keystate);
2778     }
2779
2780     /* Disable Auto repeat if required */
2781     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2782         return 0;
2783
2784     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2785         left_alt = 1;
2786
2787     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2788
2789     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2790     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2791         if (cfg.ctrlaltkeys)
2792             keystate[VK_MENU] = 0;
2793         else {
2794             keystate[VK_RMENU] = 0x80;
2795             left_alt = 0;
2796         }
2797     }
2798
2799     alt_pressed = (left_alt && key_down);
2800
2801     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2802     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2803         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2804
2805     /* Note if AltGr was pressed and if it was used as a compose key */
2806     if (!compose_state) {
2807         compose_key = 0x100;
2808         if (cfg.compose_key) {
2809             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2810                 compose_key = wParam;
2811         }
2812         if (wParam == VK_APPS)
2813             compose_key = wParam;
2814     }
2815
2816     if (wParam == compose_key) {
2817         if (compose_state == 0
2818             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2819                 1;
2820         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2821             compose_state = 2;
2822         else
2823             compose_state = 0;
2824     } else if (compose_state == 1 && wParam != VK_CONTROL)
2825         compose_state = 0;
2826
2827     /* 
2828      * Record that we pressed key so the scroll window can be reset, but
2829      * be careful to avoid Shift-UP/Down
2830      */
2831     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2832         seen_key_event = 1;
2833     }
2834
2835     if (compose_state > 1 && left_alt)
2836         compose_state = 0;
2837
2838     /* Sanitize the number pad if not using a PC NumPad */
2839     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2840                      && cfg.funky_type != 2)
2841         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2842         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2843             int nParam = 0;
2844             switch (wParam) {
2845               case VK_INSERT:
2846                 nParam = VK_NUMPAD0;
2847                 break;
2848               case VK_END:
2849                 nParam = VK_NUMPAD1;
2850                 break;
2851               case VK_DOWN:
2852                 nParam = VK_NUMPAD2;
2853                 break;
2854               case VK_NEXT:
2855                 nParam = VK_NUMPAD3;
2856                 break;
2857               case VK_LEFT:
2858                 nParam = VK_NUMPAD4;
2859                 break;
2860               case VK_CLEAR:
2861                 nParam = VK_NUMPAD5;
2862                 break;
2863               case VK_RIGHT:
2864                 nParam = VK_NUMPAD6;
2865                 break;
2866               case VK_HOME:
2867                 nParam = VK_NUMPAD7;
2868                 break;
2869               case VK_UP:
2870                 nParam = VK_NUMPAD8;
2871                 break;
2872               case VK_PRIOR:
2873                 nParam = VK_NUMPAD9;
2874                 break;
2875               case VK_DELETE:
2876                 nParam = VK_DECIMAL;
2877                 break;
2878             }
2879             if (nParam) {
2880                 if (keystate[VK_NUMLOCK] & 1)
2881                     shift_state |= 1;
2882                 wParam = nParam;
2883             }
2884         }
2885     }
2886
2887     /* If a key is pressed and AltGr is not active */
2888     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2889         /* Okay, prepare for most alts then ... */
2890         if (left_alt)
2891             *p++ = '\033';
2892
2893         /* Lets see if it's a pattern we know all about ... */
2894         if (wParam == VK_PRIOR && shift_state == 1) {
2895             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2896             return 0;
2897         }
2898         if (wParam == VK_NEXT && shift_state == 1) {
2899             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2900             return 0;
2901         }
2902         if (wParam == VK_INSERT && shift_state == 1) {
2903             term_do_paste();
2904             return 0;
2905         }
2906         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2907             return -1;
2908         }
2909         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2910             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2911             return -1;
2912         }
2913         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2914             flip_full_screen();
2915             return -1;
2916         }
2917         /* Control-Numlock for app-keypad mode switch */
2918         if (wParam == VK_PAUSE && shift_state == 2) {
2919             app_keypad_keys ^= 1;
2920             return 0;
2921         }
2922
2923         /* Nethack keypad */
2924         if (cfg.nethack_keypad && !left_alt) {
2925             switch (wParam) {
2926               case VK_NUMPAD1:
2927                 *p++ = shift_state ? 'B' : 'b';
2928                 return p - output;
2929               case VK_NUMPAD2:
2930                 *p++ = shift_state ? 'J' : 'j';
2931                 return p - output;
2932               case VK_NUMPAD3:
2933                 *p++ = shift_state ? 'N' : 'n';
2934                 return p - output;
2935               case VK_NUMPAD4:
2936                 *p++ = shift_state ? 'H' : 'h';
2937                 return p - output;
2938               case VK_NUMPAD5:
2939                 *p++ = shift_state ? '.' : '.';
2940                 return p - output;
2941               case VK_NUMPAD6:
2942                 *p++ = shift_state ? 'L' : 'l';
2943                 return p - output;
2944               case VK_NUMPAD7:
2945                 *p++ = shift_state ? 'Y' : 'y';
2946                 return p - output;
2947               case VK_NUMPAD8:
2948                 *p++ = shift_state ? 'K' : 'k';
2949                 return p - output;
2950               case VK_NUMPAD9:
2951                 *p++ = shift_state ? 'U' : 'u';
2952                 return p - output;
2953             }
2954         }
2955
2956         /* Application Keypad */
2957         if (!left_alt) {
2958             int xkey = 0;
2959
2960             if (cfg.funky_type == 3 ||
2961                 (cfg.funky_type <= 1 &&
2962                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2963                   case VK_EXECUTE:
2964                     xkey = 'P';
2965                     break;
2966                   case VK_DIVIDE:
2967                     xkey = 'Q';
2968                     break;
2969                   case VK_MULTIPLY:
2970                     xkey = 'R';
2971                     break;
2972                   case VK_SUBTRACT:
2973                     xkey = 'S';
2974                     break;
2975                 }
2976             if (app_keypad_keys && !cfg.no_applic_k)
2977                 switch (wParam) {
2978                   case VK_NUMPAD0:
2979                     xkey = 'p';
2980                     break;
2981                   case VK_NUMPAD1:
2982                     xkey = 'q';
2983                     break;
2984                   case VK_NUMPAD2:
2985                     xkey = 'r';
2986                     break;
2987                   case VK_NUMPAD3:
2988                     xkey = 's';
2989                     break;
2990                   case VK_NUMPAD4:
2991                     xkey = 't';
2992                     break;
2993                   case VK_NUMPAD5:
2994                     xkey = 'u';
2995                     break;
2996                   case VK_NUMPAD6:
2997                     xkey = 'v';
2998                     break;
2999                   case VK_NUMPAD7:
3000                     xkey = 'w';
3001                     break;
3002                   case VK_NUMPAD8:
3003                     xkey = 'x';
3004                     break;
3005                   case VK_NUMPAD9:
3006                     xkey = 'y';
3007                     break;
3008
3009                   case VK_DECIMAL:
3010                     xkey = 'n';
3011                     break;
3012                   case VK_ADD:
3013                     if (cfg.funky_type == 2) {
3014                         if (shift_state)
3015                             xkey = 'l';
3016                         else
3017                             xkey = 'k';
3018                     } else if (shift_state)
3019                         xkey = 'm';
3020                     else
3021                         xkey = 'l';
3022                     break;
3023
3024                   case VK_DIVIDE:
3025                     if (cfg.funky_type == 2)
3026                         xkey = 'o';
3027                     break;
3028                   case VK_MULTIPLY:
3029                     if (cfg.funky_type == 2)
3030                         xkey = 'j';
3031                     break;
3032                   case VK_SUBTRACT:
3033                     if (cfg.funky_type == 2)
3034                         xkey = 'm';
3035                     break;
3036
3037                   case VK_RETURN:
3038                     if (HIWORD(lParam) & KF_EXTENDED)
3039                         xkey = 'M';
3040                     break;
3041                 }
3042             if (xkey) {
3043                 if (vt52_mode) {
3044                     if (xkey >= 'P' && xkey <= 'S')
3045                         p += sprintf((char *) p, "\x1B%c", xkey);
3046                     else
3047                         p += sprintf((char *) p, "\x1B?%c", xkey);
3048                 } else
3049                     p += sprintf((char *) p, "\x1BO%c", xkey);
3050                 return p - output;
3051             }
3052         }
3053
3054         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3055             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3056             *p++ = 0;
3057             return -2;
3058         }
3059         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3060             *p++ = 0x1B;
3061             *p++ = '[';
3062             *p++ = 'Z';
3063             return p - output;
3064         }
3065         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3066             *p++ = 0;
3067             return p - output;
3068         }
3069         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3070             *p++ = 160;
3071             return p - output;
3072         }
3073         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3074             *p++ = 3;
3075             *p++ = 0;
3076             return -2;
3077         }
3078         if (wParam == VK_PAUSE) {      /* Break/Pause */
3079             *p++ = 26;
3080             *p++ = 0;
3081             return -2;
3082         }
3083         /* Control-2 to Control-8 are special */
3084         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3085             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3086             return p - output;
3087         }
3088         if (shift_state == 2 && wParam == 0xBD) {
3089             *p++ = 0x1F;
3090             return p - output;
3091         }
3092         if (shift_state == 2 && wParam == 0xDF) {
3093             *p++ = 0x1C;
3094             return p - output;
3095         }
3096         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3097             *p++ = '\r';
3098             *p++ = '\n';
3099             return p - output;
3100         }
3101
3102         /*
3103          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3104          * for integer decimal nn.)
3105          *
3106          * We also deal with the weird ones here. Linux VCs replace F1
3107          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3108          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3109          * respectively.
3110          */
3111         code = 0;
3112         switch (wParam) {
3113           case VK_F1:
3114             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3115             break;
3116           case VK_F2:
3117             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3118             break;
3119           case VK_F3:
3120             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3121             break;
3122           case VK_F4:
3123             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3124             break;
3125           case VK_F5:
3126             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3127             break;
3128           case VK_F6:
3129             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3130             break;
3131           case VK_F7:
3132             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3133             break;
3134           case VK_F8:
3135             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3136             break;
3137           case VK_F9:
3138             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3139             break;
3140           case VK_F10:
3141             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3142             break;
3143           case VK_F11:
3144             code = 23;
3145             break;
3146           case VK_F12:
3147             code = 24;
3148             break;
3149           case VK_F13:
3150             code = 25;
3151             break;
3152           case VK_F14:
3153             code = 26;
3154             break;
3155           case VK_F15:
3156             code = 28;
3157             break;
3158           case VK_F16:
3159             code = 29;
3160             break;
3161           case VK_F17:
3162             code = 31;
3163             break;
3164           case VK_F18:
3165             code = 32;
3166             break;
3167           case VK_F19:
3168             code = 33;
3169             break;
3170           case VK_F20:
3171             code = 34;
3172             break;
3173         }
3174         if ((shift_state&2) == 0) switch (wParam) {
3175           case VK_HOME:
3176             code = 1;
3177             break;
3178           case VK_INSERT:
3179             code = 2;
3180             break;
3181           case VK_DELETE:
3182             code = 3;
3183             break;
3184           case VK_END:
3185             code = 4;
3186             break;
3187           case VK_PRIOR:
3188             code = 5;
3189             break;
3190           case VK_NEXT:
3191             code = 6;
3192             break;
3193         }
3194         /* Reorder edit keys to physical order */
3195         if (cfg.funky_type == 3 && code <= 6)
3196             code = "\0\2\1\4\5\3\6"[code];
3197
3198         if (vt52_mode && code > 0 && code <= 6) {
3199             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3200             return p - output;
3201         }
3202
3203         if (cfg.funky_type == 5 &&     /* SCO function keys */
3204             code >= 11 && code <= 34) {
3205             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3206             int index = 0;
3207             switch (wParam) {
3208               case VK_F1: index = 0; break;
3209               case VK_F2: index = 1; break;
3210               case VK_F3: index = 2; break;
3211               case VK_F4: index = 3; break;
3212               case VK_F5: index = 4; break;
3213               case VK_F6: index = 5; break;
3214               case VK_F7: index = 6; break;
3215               case VK_F8: index = 7; break;
3216               case VK_F9: index = 8; break;
3217               case VK_F10: index = 9; break;
3218               case VK_F11: index = 10; break;
3219               case VK_F12: index = 11; break;
3220             }
3221             if (keystate[VK_SHIFT] & 0x80) index += 12;
3222             if (keystate[VK_CONTROL] & 0x80) index += 24;
3223             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3224             return p - output;
3225         }
3226         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3227             code >= 1 && code <= 6) {
3228             char codes[] = "HL.FIG";
3229             if (code == 3) {
3230                 *p++ = '\x7F';
3231             } else {
3232                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3233             }
3234             return p - output;
3235         }
3236         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3237             int offt = 0;
3238             if (code > 15)
3239                 offt++;
3240             if (code > 21)
3241                 offt++;
3242             if (vt52_mode)
3243                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3244             else
3245                 p +=
3246                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3247             return p - output;
3248         }
3249         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3250             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3251             return p - output;
3252         }
3253         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3254             if (vt52_mode)
3255                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3256             else
3257                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3258             return p - output;
3259         }
3260         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3261             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3262             return p - output;
3263         }
3264         if (code) {
3265             p += sprintf((char *) p, "\x1B[%d~", code);
3266             return p - output;
3267         }
3268
3269         /*
3270          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3271          * some reason seems to send VK_CLEAR to Windows...).
3272          */
3273         {
3274             char xkey = 0;
3275             switch (wParam) {
3276               case VK_UP:
3277                 xkey = 'A';
3278                 break;
3279               case VK_DOWN:
3280                 xkey = 'B';
3281                 break;
3282               case VK_RIGHT:
3283                 xkey = 'C';
3284                 break;
3285               case VK_LEFT:
3286                 xkey = 'D';
3287                 break;
3288               case VK_CLEAR:
3289                 xkey = 'G';
3290                 break;
3291             }
3292             if (xkey) {
3293                 if (vt52_mode)
3294                     p += sprintf((char *) p, "\x1B%c", xkey);
3295                 else {
3296                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3297                     /* VT100 & VT102 manuals both state the app cursor keys
3298                      * only work if the app keypad is on.
3299                      */
3300                     if (!app_keypad_keys)
3301                         app_flg = 0;
3302                     /* Useful mapping of Ctrl-arrows */
3303                     if (shift_state == 2)
3304                         app_flg = !app_flg;
3305
3306                     if (app_flg)
3307                         p += sprintf((char *) p, "\x1BO%c", xkey);
3308                     else
3309                         p += sprintf((char *) p, "\x1B[%c", xkey);
3310                 }
3311                 return p - output;
3312             }
3313         }
3314
3315         /*
3316          * Finally, deal with Return ourselves. (Win95 seems to
3317          * foul it up when Alt is pressed, for some reason.)
3318          */
3319         if (wParam == VK_RETURN) {     /* Return */
3320             *p++ = 0x0D;
3321             *p++ = 0;
3322             return -2;
3323         }
3324
3325         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3326             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3327         else
3328             alt_sum = 0;
3329     }
3330
3331     /* Okay we've done everything interesting; let windows deal with 
3332      * the boring stuff */
3333     {
3334         BOOL capsOn=0;
3335
3336         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3337         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3338             capsOn= !left_alt;
3339             keystate[VK_CAPITAL] = 0;
3340         }
3341
3342         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3343 #ifdef SHOW_TOASCII_RESULT
3344         if (r == 1 && !key_down) {
3345             if (alt_sum) {
3346                 if (in_utf || dbcs_screenfont)
3347                     debug((", (U+%04x)", alt_sum));
3348                 else
3349                     debug((", LCH(%d)", alt_sum));
3350             } else {
3351                 debug((", ACH(%d)", keys[0]));
3352             }
3353         } else if (r > 0) {
3354             int r1;
3355             debug((", ASC("));
3356             for (r1 = 0; r1 < r; r1++) {
3357                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3358             }
3359             debug((")"));
3360         }
3361 #endif
3362         if (r > 0) {
3363             WCHAR keybuf;
3364
3365             /*
3366              * Interrupt an ongoing paste. I'm not sure this is
3367              * sensible, but for the moment it's preferable to
3368              * having to faff about buffering things.
3369              */
3370             term_nopaste();
3371
3372             p = output;
3373             for (i = 0; i < r; i++) {
3374                 unsigned char ch = (unsigned char) keys[i];
3375
3376                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3377                     compose_char = ch;
3378                     compose_state++;
3379                     continue;
3380                 }
3381                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3382                     int nc;
3383                     compose_state = 0;
3384
3385                     if ((nc = check_compose(compose_char, ch)) == -1) {
3386                         MessageBeep(MB_ICONHAND);
3387                         return 0;
3388                     }
3389                     keybuf = nc;
3390                     luni_send(&keybuf, 1, 1);
3391                     continue;
3392                 }
3393
3394                 compose_state = 0;
3395
3396                 if (!key_down) {
3397                     if (alt_sum) {
3398                         if (in_utf || dbcs_screenfont) {
3399                             keybuf = alt_sum;
3400                             luni_send(&keybuf, 1, 1);
3401                         } else {
3402                             ch = (char) alt_sum;
3403                             /*
3404                              * We need not bother about stdin
3405                              * backlogs here, because in GUI PuTTY
3406                              * we can't do anything about it
3407                              * anyway; there's no means of asking
3408                              * Windows to hold off on KEYDOWN
3409                              * messages. We _have_ to buffer
3410                              * everything we're sent.
3411                              */
3412                             ldisc_send(&ch, 1, 1);
3413                         }
3414                         alt_sum = 0;
3415                     } else
3416                         lpage_send(kbd_codepage, &ch, 1, 1);
3417                 } else {
3418                     if(capsOn && ch < 0x80) {
3419                         WCHAR cbuf[2];
3420                         cbuf[0] = 27;
3421                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3422                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3423                     } else {
3424                         char cbuf[2];
3425                         cbuf[0] = '\033';
3426                         cbuf[1] = ch;
3427                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3428                     }
3429                 }
3430                 show_mouseptr(0);
3431             }
3432
3433             /* This is so the ALT-Numpad and dead keys work correctly. */
3434             keys[0] = 0;
3435
3436             return p - output;
3437         }
3438         /* If we're definitly not building up an ALT-54321 then clear it */
3439         if (!left_alt)
3440             keys[0] = 0;
3441         /* If we will be using alt_sum fix the 256s */
3442         else if (keys[0] && (in_utf || dbcs_screenfont))
3443             keys[0] = 10;
3444     }
3445
3446     /*
3447      * ALT alone may or may not want to bring up the System menu.
3448      * If it's not meant to, we return 0 on presses or releases of
3449      * ALT, to show that we've swallowed the keystroke. Otherwise
3450      * we return -1, which means Windows will give the keystroke
3451      * its default handling (i.e. bring up the System menu).
3452      */
3453     if (wParam == VK_MENU && !cfg.alt_only)
3454         return 0;
3455
3456     return -1;
3457 }
3458
3459 void set_title(char *title)
3460 {
3461     sfree(window_name);
3462     window_name = smalloc(1 + strlen(title));
3463     strcpy(window_name, title);
3464     if (cfg.win_name_always || !IsIconic(hwnd))
3465         SetWindowText(hwnd, title);
3466 }
3467
3468 void set_icon(char *title)
3469 {
3470     sfree(icon_name);
3471     icon_name = smalloc(1 + strlen(title));
3472     strcpy(icon_name, title);
3473     if (!cfg.win_name_always && IsIconic(hwnd))
3474         SetWindowText(hwnd, title);
3475 }
3476
3477 void set_sbar(int total, int start, int page)
3478 {
3479     SCROLLINFO si;
3480
3481     if (!cfg.scrollbar)
3482         return;
3483
3484     si.cbSize = sizeof(si);
3485     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3486     si.nMin = 0;
3487     si.nMax = total - 1;
3488     si.nPage = page;
3489     si.nPos = start;
3490     if (hwnd)
3491         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3492 }
3493
3494 Context get_ctx(void)
3495 {
3496     HDC hdc;
3497     if (hwnd) {
3498         hdc = GetDC(hwnd);
3499         if (hdc && pal)
3500             SelectPalette(hdc, pal, FALSE);
3501         return hdc;
3502     } else
3503         return NULL;
3504 }
3505
3506 void free_ctx(Context ctx)
3507 {
3508     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3509     ReleaseDC(hwnd, ctx);
3510 }
3511
3512 static void real_palette_set(int n, int r, int g, int b)
3513 {
3514     if (pal) {
3515         logpal->palPalEntry[n].peRed = r;
3516         logpal->palPalEntry[n].peGreen = g;
3517         logpal->palPalEntry[n].peBlue = b;
3518         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3519         colours[n] = PALETTERGB(r, g, b);
3520         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3521     } else
3522         colours[n] = RGB(r, g, b);
3523 }
3524
3525 void palette_set(int n, int r, int g, int b)
3526 {
3527     static const int first[21] = {
3528         0, 2, 4, 6, 8, 10, 12, 14,
3529         1, 3, 5, 7, 9, 11, 13, 15,
3530         16, 17, 18, 20, 22
3531     };
3532     real_palette_set(first[n], r, g, b);
3533     if (first[n] >= 18)
3534         real_palette_set(first[n] + 1, r, g, b);
3535     if (pal) {
3536         HDC hdc = get_ctx();
3537         UnrealizeObject(pal);
3538         RealizePalette(hdc);
3539         free_ctx(hdc);
3540     }
3541 }
3542
3543 void palette_reset(void)
3544 {
3545     int i;
3546
3547     for (i = 0; i < NCOLOURS; i++) {
3548         if (pal) {
3549             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3550             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3551             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3552             logpal->palPalEntry[i].peFlags = 0;
3553             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3554                                     defpal[i].rgbtGreen,
3555                                     defpal[i].rgbtBlue);
3556         } else
3557             colours[i] = RGB(defpal[i].rgbtRed,
3558                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3559     }
3560
3561     if (pal) {
3562         HDC hdc;
3563         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3564         hdc = get_ctx();
3565         RealizePalette(hdc);
3566         free_ctx(hdc);
3567     }
3568 }
3569
3570 void write_aclip(char *data, int len, int must_deselect)
3571 {
3572     HGLOBAL clipdata;
3573     void *lock;
3574
3575     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3576     if (!clipdata)
3577         return;
3578     lock = GlobalLock(clipdata);
3579     if (!lock)
3580         return;
3581     memcpy(lock, data, len);
3582     ((unsigned char *) lock)[len] = 0;
3583     GlobalUnlock(clipdata);
3584
3585     if (!must_deselect)
3586         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3587
3588     if (OpenClipboard(hwnd)) {
3589         EmptyClipboard();
3590         SetClipboardData(CF_TEXT, clipdata);
3591         CloseClipboard();
3592     } else
3593         GlobalFree(clipdata);
3594
3595     if (!must_deselect)
3596         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3597 }
3598
3599 /*
3600  * Note: unlike write_aclip() this will not append a nul.
3601  */
3602 void write_clip(wchar_t * data, int len, int must_deselect)
3603 {
3604     HGLOBAL clipdata;
3605     HGLOBAL clipdata2;
3606     int len2;
3607     void *lock, *lock2;
3608
3609     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3610
3611     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3612                            len * sizeof(wchar_t));
3613     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3614
3615     if (!clipdata || !clipdata2) {
3616         if (clipdata)
3617             GlobalFree(clipdata);
3618         if (clipdata2)
3619             GlobalFree(clipdata2);
3620         return;
3621     }
3622     if (!(lock = GlobalLock(clipdata)))
3623         return;
3624     if (!(lock2 = GlobalLock(clipdata2)))
3625         return;
3626
3627     memcpy(lock, data, len * sizeof(wchar_t));
3628     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3629
3630     GlobalUnlock(clipdata);
3631     GlobalUnlock(clipdata2);
3632
3633     if (!must_deselect)
3634         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3635
3636     if (OpenClipboard(hwnd)) {
3637         EmptyClipboard();
3638         SetClipboardData(CF_UNICODETEXT, clipdata);
3639         SetClipboardData(CF_TEXT, clipdata2);
3640         CloseClipboard();
3641     } else {
3642         GlobalFree(clipdata);
3643         GlobalFree(clipdata2);
3644     }
3645
3646     if (!must_deselect)
3647         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3648 }
3649
3650 void get_clip(wchar_t ** p, int *len)
3651 {
3652     static HGLOBAL clipdata = NULL;
3653     static wchar_t *converted = 0;
3654     wchar_t *p2;
3655
3656     if (converted) {
3657         sfree(converted);
3658         converted = 0;
3659     }
3660     if (!p) {
3661         if (clipdata)
3662             GlobalUnlock(clipdata);
3663         clipdata = NULL;
3664         return;
3665     } else if (OpenClipboard(NULL)) {
3666         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3667             CloseClipboard();
3668             *p = GlobalLock(clipdata);
3669             if (*p) {
3670                 for (p2 = *p; *p2; p2++);
3671                 *len = p2 - *p;
3672                 return;
3673             }
3674         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3675             char *s;
3676             int i;
3677             CloseClipboard();
3678             s = GlobalLock(clipdata);
3679             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3680             *p = converted = smalloc(i * sizeof(wchar_t));
3681             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3682             *len = i - 1;
3683             return;
3684         } else
3685             CloseClipboard();
3686     }
3687
3688     *p = NULL;
3689     *len = 0;
3690 }
3691
3692 #if 0
3693 /*
3694  * Move `lines' lines from position `from' to position `to' in the
3695  * window.
3696  */
3697 void optimised_move(int to, int from, int lines)
3698 {
3699     RECT r;
3700     int min, max;
3701
3702     min = (to < from ? to : from);
3703     max = to + from - min;
3704
3705     r.left = offset_width;
3706     r.right = offset_width + cols * font_width;
3707     r.top = offset_height + min * font_height;
3708     r.bottom = offset_height + (max + lines) * font_height;
3709     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3710 }
3711 #endif
3712
3713 /*
3714  * Print a message box and perform a fatal exit.
3715  */
3716 void fatalbox(char *fmt, ...)
3717 {
3718     va_list ap;
3719     char stuff[200];
3720
3721     va_start(ap, fmt);
3722     vsprintf(stuff, fmt, ap);
3723     va_end(ap);
3724     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3725     exit(1);
3726 }
3727
3728 /*
3729  * Manage window caption / taskbar flashing, if enabled.
3730  * 0 = stop, 1 = maintain, 2 = start
3731  */
3732 static void flash_window(int mode)
3733 {
3734     static long last_flash = 0;
3735     static int flashing = 0;
3736     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3737         /* stop */
3738         if (flashing) {
3739             FlashWindow(hwnd, FALSE);
3740             flashing = 0;
3741         }
3742
3743     } else if (mode == 2) {
3744         /* start */
3745         if (!flashing) {
3746             last_flash = GetTickCount();
3747             flashing = 1;
3748             FlashWindow(hwnd, TRUE);
3749         }
3750
3751     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3752         /* maintain */
3753         if (flashing) {
3754             long now = GetTickCount();
3755             long fdiff = now - last_flash;
3756             if (fdiff < 0 || fdiff > 450) {
3757                 last_flash = now;
3758                 FlashWindow(hwnd, TRUE);        /* toggle */
3759             }
3760         }
3761     }
3762 }
3763
3764 /*
3765  * Beep.
3766  */
3767 void beep(int mode)
3768 {
3769     if (mode == BELL_DEFAULT) {
3770         /*
3771          * For MessageBeep style bells, we want to be careful of
3772          * timing, because they don't have the nice property of
3773          * PlaySound bells that each one cancels the previous
3774          * active one. So we limit the rate to one per 50ms or so.
3775          */
3776         static long lastbeep = 0;
3777         long beepdiff;
3778
3779         beepdiff = GetTickCount() - lastbeep;
3780         if (beepdiff >= 0 && beepdiff < 50)
3781             return;
3782         MessageBeep(MB_OK);
3783         /*
3784          * The above MessageBeep call takes time, so we record the
3785          * time _after_ it finishes rather than before it starts.
3786          */
3787         lastbeep = GetTickCount();
3788     } else if (mode == BELL_WAVEFILE) {
3789         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3790             char buf[sizeof(cfg.bell_wavefile) + 80];
3791             sprintf(buf, "Unable to play sound file\n%s\n"
3792                     "Using default sound instead", cfg.bell_wavefile);
3793             MessageBox(hwnd, buf, "PuTTY Sound Error",
3794                        MB_OK | MB_ICONEXCLAMATION);
3795             cfg.beep = BELL_DEFAULT;
3796         }
3797     }
3798     /* Otherwise, either visual bell or disabled; do nothing here */
3799     if (!has_focus) {
3800         flash_window(2);               /* start */
3801     }
3802 }
3803
3804 /*
3805  * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3806  * implementation.
3807  */
3808 static void flip_full_screen(void)
3809 {
3810     if (!full_screen) {
3811         int cx, cy;
3812
3813         cx = GetSystemMetrics(SM_CXSCREEN);
3814         cy = GetSystemMetrics(SM_CYSCREEN);
3815         GetWindowPlacement(hwnd, &old_wind_placement);
3816         old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3817         SetWindowLong(hwnd, GWL_STYLE,
3818                       old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3819         SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3820         full_screen = 1;
3821     } else {
3822         SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3823         SetWindowPlacement(hwnd,&old_wind_placement);
3824         full_screen = 0;
3825     }
3826 }