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