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