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