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