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