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