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