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