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