]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
cfb8eae45924e17c8dfb7292efa8745a71915913
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <richedit.h>
5 #include <mmsystem.h>
6 #ifndef AUTO_WINSOCK
7 #ifdef WINSOCK_TWO
8 #include <winsock2.h>
9 #else
10 #include <winsock.h>
11 #endif
12 #endif
13
14 #ifndef NO_MULTIMON
15 #if WINVER < 0x0500
16 #define COMPILE_MULTIMON_STUBS
17 #include <multimon.h>
18 #endif
19 #endif
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <ctype.h>
24 #include <time.h>
25 #include <assert.h>
26
27 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
28 #include "putty.h"
29 #include "terminal.h"
30 #include "winstuff.h"
31 #include "storage.h"
32 #include "win_res.h"
33
34 #define IDM_SHOWLOG   0x0010
35 #define IDM_NEWSESS   0x0020
36 #define IDM_DUPSESS   0x0030
37 #define IDM_RECONF    0x0040
38 #define IDM_CLRSB     0x0050
39 #define IDM_RESET     0x0060
40 #define IDM_TEL_AYT   0x0070
41 #define IDM_TEL_BRK   0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC    0x00a0
44 #define IDM_TEL_EL    0x00b0
45 #define IDM_TEL_GA    0x00c0
46 #define IDM_TEL_NOP   0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO    0x00f0
49 #define IDM_TEL_IP    0x0100
50 #define IDM_TEL_SUSP  0x0110
51 #define IDM_TEL_EOR   0x0120
52 #define IDM_TEL_EOF   0x0130
53 #define IDM_HELP      0x0140
54 #define IDM_ABOUT     0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL   0x0170
57 #define IDM_FULLSCREEN  0x0180
58
59 #define IDM_SESSLGP   0x0250           /* log type printable */
60 #define IDM_SESSLGA   0x0260           /* log type all chars */
61 #define IDM_SESSLGE   0x0270           /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
64
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
67
68 /* Needed for Chinese support and apparently not always defined. */
69 #ifndef VK_PROCESSKEY
70 #define VK_PROCESSKEY 0xE5
71 #endif
72
73 /* Mouse wheel support. */
74 #ifndef WM_MOUSEWHEEL
75 #define WM_MOUSEWHEEL 0x020A           /* not defined in earlier SDKs */
76 #endif
77 #ifndef WHEEL_DELTA
78 #define WHEEL_DELTA 120
79 #endif
80
81 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
82 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
83                         unsigned char *output);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL);
90
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
95
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width, extra_height;
99 static int font_width, font_height, font_dualwidth;
100 static int offset_width, offset_height;
101 static int was_zoomed = 0;
102 static int prev_rows, prev_cols;
103   
104 static int pending_netevent = 0;
105 static WPARAM pend_netevent_wParam = 0;
106 static LPARAM pend_netevent_lParam = 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode);
109 static void sys_cursor_update(void);
110 static int is_shift_pressed(void);
111 static int get_fullscreen_rect(RECT * ss);
112
113 static time_t last_movement = 0;
114
115 static int caret_x = -1, caret_y = -1;
116
117 static int kbd_codepage;
118
119 static void *ldisc;
120 static Backend *back;
121 static void *backhandle;
122
123 static int session_closed;
124
125 extern struct sesslist sesslist;       /* imported from windlg.c */
126
127 #define FONT_NORMAL 0
128 #define FONT_BOLD 1
129 #define FONT_UNDERLINE 2
130 #define FONT_BOLDUND 3
131 #define FONT_WIDE       0x04
132 #define FONT_HIGH       0x08
133 #define FONT_NARROW     0x10
134
135 #define FONT_OEM        0x20
136 #define FONT_OEMBOLD    0x21
137 #define FONT_OEMUND     0x22
138 #define FONT_OEMBOLDUND 0x23
139
140 #define FONT_MAXNO      0x2F
141 #define FONT_SHIFT      5
142 static HFONT fonts[FONT_MAXNO];
143 static LOGFONT lfont;
144 static int fontflag[FONT_MAXNO];
145 static enum {
146     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
147 } bold_mode;
148 static enum {
149     UND_LINE, UND_FONT
150 } und_mode;
151 static int descent;
152
153 #define NCOLOURS 24
154 static COLORREF colours[NCOLOURS];
155 static HPALETTE pal;
156 static LPLOGPALETTE logpal;
157 static RGBTRIPLE defpal[NCOLOURS];
158
159 static HWND hwnd;
160
161 static HBITMAP caretbm;
162
163 static int dbltime, lasttime, lastact;
164 static Mouse_Button lastbtn;
165
166 /* this allows xterm-style mouse handling. */
167 static int send_raw_mouse = 0;
168 static int wheel_accumulator = 0;
169
170 static char *window_name, *icon_name;
171
172 static int compose_state = 0;
173
174 static int wsa_started = 0;
175
176 static OSVERSIONINFO osVersion;
177
178 static UINT wm_mousewheel = WM_MOUSEWHEEL;
179
180 /* Dummy routine, only required in plink. */
181 void ldisc_update(void *frontend, int echo, int edit)
182 {
183 }
184
185 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
186 {
187     static char appname[] = "PuTTY";
188     WORD winsock_ver;
189     WSADATA wsadata;
190     WNDCLASS wndclass;
191     MSG msg;
192     int guess_width, guess_height;
193
194     hinst = inst;
195     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
196
197     winsock_ver = MAKEWORD(1, 1);
198     if (WSAStartup(winsock_ver, &wsadata)) {
199         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
200                    MB_OK | MB_ICONEXCLAMATION);
201         return 1;
202     }
203     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
204         MessageBox(NULL, "WinSock version is incompatible with 1.1",
205                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
206         WSACleanup();
207         return 1;
208     }
209     wsa_started = 1;
210     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
211     sk_init();
212
213     InitCommonControls();
214
215     /* Ensure a Maximize setting in Explorer doesn't maximise the
216      * config box. */
217     defuse_showwindow();
218
219     {
220         ZeroMemory(&osVersion, sizeof(osVersion));
221         osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
222         if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
223             MessageBox(NULL, "Windows refuses to report a version",
224                        "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
225             return 1;
226         }
227     }
228
229     /*
230      * If we're running a version of Windows that doesn't support
231      * WM_MOUSEWHEEL, find out what message number we should be
232      * using instead.
233      */
234     if (osVersion.dwMajorVersion < 4 ||
235         (osVersion.dwMajorVersion == 4 && 
236          osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
237         wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
238
239     /*
240      * See if we can find our Help file.
241      */
242     {
243         char b[2048], *p, *q, *r;
244         FILE *fp;
245         GetModuleFileName(NULL, b, sizeof(b) - 1);
246         r = b;
247         p = strrchr(b, '\\');
248         if (p && p >= r) r = p+1;
249         q = strrchr(b, ':');
250         if (q && q >= r) r = q+1;
251         strcpy(r, "putty.hlp");
252         if ( (fp = fopen(b, "r")) != NULL) {
253             help_path = dupstr(b);
254             fclose(fp);
255         } else
256             help_path = NULL;
257         strcpy(r, "putty.cnt");
258         if ( (fp = fopen(b, "r")) != NULL) {
259             help_has_contents = TRUE;
260             fclose(fp);
261         } else
262             help_has_contents = FALSE;
263     }
264
265     /*
266      * Process the command line.
267      */
268     {
269         char *p;
270         int got_host = 0;
271
272         default_protocol = DEFAULT_PROTOCOL;
273         default_port = DEFAULT_PORT;
274         cfg.logtype = LGTYP_NONE;
275
276         do_defaults(NULL, &cfg);
277
278         p = cmdline;
279
280         /*
281          * Process a couple of command-line options which are more
282          * easily dealt with before the line is broken up into
283          * words. These are the soon-to-be-defunct @sessionname and
284          * the internal-use-only &sharedmemoryhandle, neither of
285          * which are combined with anything else.
286          */
287         while (*p && isspace(*p))
288             p++;
289         if (*p == '@') {
290             int i = strlen(p);
291             while (i > 1 && isspace(p[i - 1]))
292                 i--;
293             p[i] = '\0';
294             do_defaults(p + 1, &cfg);
295             if (!*cfg.host && !do_config()) {
296                 WSACleanup();
297                 return 0;
298             }
299         } else if (*p == '&') {
300             /*
301              * An initial & means we've been given a command line
302              * containing the hex value of a HANDLE for a file
303              * mapping object, which we must then extract as a
304              * config.
305              */
306             HANDLE filemap;
307             Config *cp;
308             if (sscanf(p + 1, "%p", &filemap) == 1 &&
309                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
310                                     0, 0, sizeof(Config))) != NULL) {
311                 cfg = *cp;
312                 UnmapViewOfFile(cp);
313                 CloseHandle(filemap);
314             } else if (!do_config()) {
315                 WSACleanup();
316                 return 0;
317             }
318         } else {
319             /*
320              * Otherwise, break up the command line and deal with
321              * it sensibly.
322              */
323             int argc, i;
324             char **argv;
325             
326             split_into_argv(cmdline, &argc, &argv, NULL);
327
328             for (i = 0; i < argc; i++) {
329                 char *p = argv[i];
330                 int ret;
331
332                 ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
333                                             1, &cfg);
334                 if (ret == -2) {
335                     cmdline_error("option \"%s\" requires an argument", p);
336                 } else if (ret == 2) {
337                     i++;               /* skip next argument */
338                 } else if (ret == 1) {
339                     continue;          /* nothing further needs doing */
340                 } else if (!strcmp(p, "-cleanup")) {
341                     /*
342                      * `putty -cleanup'. Remove all registry
343                      * entries associated with PuTTY, and also find
344                      * and delete the random seed file.
345                      */
346                     if (MessageBox(NULL,
347                                    "This procedure will remove ALL Registry\n"
348                                    "entries associated with PuTTY, and will\n"
349                                    "also remove the PuTTY random seed file.\n"
350                                    "\n"
351                                    "THIS PROCESS WILL DESTROY YOUR SAVED\n"
352                                    "SESSIONS. Are you really sure you want\n"
353                                    "to continue?",
354                                    "PuTTY Warning",
355                                    MB_YESNO | MB_ICONWARNING) == IDYES) {
356                         cleanup_all();
357                     }
358                     exit(0);
359                 } else if (*p != '-') {
360                     char *q = p;
361                     if (got_host) {
362                         /*
363                          * If we already have a host name, treat
364                          * this argument as a port number. NB we
365                          * have to treat this as a saved -P
366                          * argument, so that it will be deferred
367                          * until it's a good moment to run it.
368                          */
369                         int ret = cmdline_process_param("-P", p, 1, &cfg);
370                         assert(ret == 2);
371                     } else if (!strncmp(q, "telnet:", 7)) {
372                         /*
373                          * If the hostname starts with "telnet:",
374                          * set the protocol to Telnet and process
375                          * the string as a Telnet URL.
376                          */
377                         char c;
378
379                         q += 7;
380                         if (q[0] == '/' && q[1] == '/')
381                             q += 2;
382                         cfg.protocol = PROT_TELNET;
383                         p = q;
384                         while (*p && *p != ':' && *p != '/')
385                             p++;
386                         c = *p;
387                         if (*p)
388                             *p++ = '\0';
389                         if (c == ':')
390                             cfg.port = atoi(p);
391                         else
392                             cfg.port = -1;
393                         strncpy(cfg.host, q, sizeof(cfg.host) - 1);
394                         cfg.host[sizeof(cfg.host) - 1] = '\0';
395                         got_host = 1;
396                     } else {
397                         /*
398                          * Otherwise, treat this argument as a host
399                          * name.
400                          */
401                         while (*p && !isspace(*p))
402                             p++;
403                         if (*p)
404                             *p++ = '\0';
405                         strncpy(cfg.host, q, sizeof(cfg.host) - 1);
406                         cfg.host[sizeof(cfg.host) - 1] = '\0';
407                         got_host = 1;
408                     }
409                 } else {
410                     cmdline_error("unknown option \"%s\"", p);
411                 }
412             }
413         }
414
415         cmdline_run_saved(&cfg);
416
417         if (!*cfg.host && !do_config()) {
418             WSACleanup();
419             return 0;
420         }
421
422         /*
423          * Trim leading whitespace off the hostname if it's there.
424          */
425         {
426             int space = strspn(cfg.host, " \t");
427             memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
428         }
429
430         /* See if host is of the form user@host */
431         if (cfg.host[0] != '\0') {
432             char *atsign = strchr(cfg.host, '@');
433             /* Make sure we're not overflowing the user field */
434             if (atsign) {
435                 if (atsign - cfg.host < sizeof cfg.username) {
436                     strncpy(cfg.username, cfg.host, atsign - cfg.host);
437                     cfg.username[atsign - cfg.host] = '\0';
438                 }
439                 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
440             }
441         }
442
443         /*
444          * Trim a colon suffix off the hostname if it's there.
445          */
446         cfg.host[strcspn(cfg.host, ":")] = '\0';
447
448         /*
449          * Remove any remaining whitespace from the hostname.
450          */
451         {
452             int p1 = 0, p2 = 0;
453             while (cfg.host[p2] != '\0') {
454                 if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
455                     cfg.host[p1] = cfg.host[p2];
456                     p1++;
457                 }
458                 p2++;
459             }
460             cfg.host[p1] = '\0';
461         }
462     }
463
464     /*
465      * Select protocol. This is farmed out into a table in a
466      * separate file to enable an ssh-free variant.
467      */
468     {
469         int i;
470         back = NULL;
471         for (i = 0; backends[i].backend != NULL; i++)
472             if (backends[i].protocol == cfg.protocol) {
473                 back = backends[i].backend;
474                 break;
475             }
476         if (back == NULL) {
477             MessageBox(NULL, "Unsupported protocol number found",
478                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
479             WSACleanup();
480             return 1;
481         }
482     }
483
484     /* Check for invalid Port number (i.e. zero) */
485     if (cfg.port == 0) {
486         MessageBox(NULL, "Invalid Port Number",
487                    "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
488         WSACleanup();
489         return 1;
490     }
491
492     if (!prev) {
493         wndclass.style = 0;
494         wndclass.lpfnWndProc = WndProc;
495         wndclass.cbClsExtra = 0;
496         wndclass.cbWndExtra = 0;
497         wndclass.hInstance = inst;
498         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
499         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
500         wndclass.hbrBackground = NULL;
501         wndclass.lpszMenuName = NULL;
502         wndclass.lpszClassName = appname;
503
504         RegisterClass(&wndclass);
505     }
506
507     hwnd = NULL;
508
509     term = term_init(&cfg, NULL);
510     logctx = log_init(NULL);
511     term_provide_logctx(term, logctx);
512
513     cfgtopalette();
514
515     /*
516      * Guess some defaults for the window size. This all gets
517      * updated later, so we don't really care too much. However, we
518      * do want the font width/height guesses to correspond to a
519      * large font rather than a small one...
520      */
521
522     font_width = 10;
523     font_height = 20;
524     extra_width = 25;
525     extra_height = 28;
526     term_size(term, cfg.height, cfg.width, cfg.savelines);
527     guess_width = extra_width + font_width * term->cols;
528     guess_height = extra_height + font_height * term->rows;
529     {
530         RECT r;
531                 get_fullscreen_rect(&r);
532         if (guess_width > r.right - r.left)
533             guess_width = r.right - r.left;
534         if (guess_height > r.bottom - r.top)
535             guess_height = r.bottom - r.top;
536     }
537
538     {
539         int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
540         int exwinmode = 0;
541         if (!cfg.scrollbar)
542             winmode &= ~(WS_VSCROLL);
543         if (cfg.resize_action == RESIZE_DISABLED)
544             winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
545         if (cfg.alwaysontop)
546             exwinmode |= WS_EX_TOPMOST;
547         if (cfg.sunken_edge)
548             exwinmode |= WS_EX_CLIENTEDGE;
549         hwnd = CreateWindowEx(exwinmode, appname, appname,
550                               winmode, CW_USEDEFAULT, CW_USEDEFAULT,
551                               guess_width, guess_height,
552                               NULL, NULL, inst, NULL);
553     }
554
555     /*
556      * Initialise the fonts, simultaneously correcting the guesses
557      * for font_{width,height}.
558      */
559     init_fonts(0,0);
560
561     /*
562      * Correct the guesses for extra_{width,height}.
563      */
564     {
565         RECT cr, wr;
566         GetWindowRect(hwnd, &wr);
567         GetClientRect(hwnd, &cr);
568         offset_width = offset_height = cfg.window_border;
569         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
570         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
571     }
572
573     /*
574      * Resize the window, now we know what size we _really_ want it
575      * to be.
576      */
577     guess_width = extra_width + font_width * term->cols;
578     guess_height = extra_height + font_height * term->rows;
579     SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
580                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
581
582     /*
583      * Set up a caret bitmap, with no content.
584      */
585     {
586         char *bits;
587         int size = (font_width + 15) / 16 * 2 * font_height;
588         bits = smalloc(size);
589         memset(bits, 0, size);
590         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
591         sfree(bits);
592     }
593     CreateCaret(hwnd, caretbm, font_width, font_height);
594
595     /*
596      * Initialise the scroll bar.
597      */
598     {
599         SCROLLINFO si;
600
601         si.cbSize = sizeof(si);
602         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
603         si.nMin = 0;
604         si.nMax = term->rows - 1;
605         si.nPage = term->rows;
606         si.nPos = 0;
607         SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
608     }
609
610     /*
611      * Start up the telnet connection.
612      */
613     {
614         char *error;
615         char msg[1024], *title;
616         char *realhost;
617
618         error = back->init((void *)term, &backhandle, &cfg,
619                            cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
620         back->provide_logctx(backhandle, logctx);
621         if (error) {
622             sprintf(msg, "Unable to open connection to\n"
623                     "%.800s\n" "%s", cfg.host, error);
624             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
625             return 0;
626         }
627         window_name = icon_name = NULL;
628         if (*cfg.wintitle) {
629             title = cfg.wintitle;
630         } else {
631             sprintf(msg, "%s - PuTTY", realhost);
632             title = msg;
633         }
634         sfree(realhost);
635         set_title(NULL, title);
636         set_icon(NULL, title);
637     }
638
639     /*
640      * Connect the terminal to the backend for resize purposes.
641      */
642     term_provide_resize_fn(term, back->size, backhandle);
643
644     /*
645      * Set up a line discipline.
646      */
647     ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
648
649     session_closed = FALSE;
650
651     /*
652      * Prepare the mouse handler.
653      */
654     lastact = MA_NOTHING;
655     lastbtn = MBT_NOTHING;
656     dbltime = GetDoubleClickTime();
657
658     /*
659      * Set up the session-control options on the system menu.
660      */
661     {
662         HMENU m = GetSystemMenu(hwnd, FALSE);
663         HMENU p, s;
664         int i;
665
666         AppendMenu(m, MF_SEPARATOR, 0, 0);
667         if (cfg.protocol == PROT_TELNET) {
668             p = CreateMenu();
669             AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
670             AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
671             AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
672             AppendMenu(p, MF_SEPARATOR, 0, 0);
673             AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
674             AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
675             AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
676             AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
677             AppendMenu(p, MF_SEPARATOR, 0, 0);
678             AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
679             AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
680             AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
681             AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
682             AppendMenu(p, MF_SEPARATOR, 0, 0);
683             AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
684             AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
685             AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
686                        "Telnet Command");
687             AppendMenu(m, MF_SEPARATOR, 0, 0);
688         }
689         AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
690         AppendMenu(m, MF_SEPARATOR, 0, 0);
691         AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
692         AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
693         s = CreateMenu();
694         get_sesslist(&sesslist, TRUE);
695         for (i = 1;
696              i < ((sesslist.nsessions < 256) ? sesslist.nsessions : 256);
697              i++)
698             AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
699                        sesslist.sessions[i]);
700         AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
701         AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
702         AppendMenu(m, MF_SEPARATOR, 0, 0);
703         AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
704         AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
705         AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
706         AppendMenu(m, MF_SEPARATOR, 0, 0);
707         AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
708                    MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
709         AppendMenu(m, MF_SEPARATOR, 0, 0);
710         if (help_path)
711             AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
712         AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
713     }
714
715     /*
716      * Set up the initial input locale.
717      */
718     set_input_locale(GetKeyboardLayout(0));
719
720     /*
721      * Open the initial log file if there is one.
722      */
723     logfopen(logctx);
724
725     /*
726      * Finally show the window!
727      */
728     ShowWindow(hwnd, show);
729     SetForegroundWindow(hwnd);
730
731     /*
732      * Set the palette up.
733      */
734     pal = NULL;
735     logpal = NULL;
736     init_palette();
737
738     term->has_focus = (GetForegroundWindow() == hwnd);
739     UpdateWindow(hwnd);
740
741     if (GetMessage(&msg, NULL, 0, 0) == 1) {
742         int timer_id = 0, long_timer = 0;
743
744         while (msg.message != WM_QUIT) {
745             /* Sometimes DispatchMessage calls routines that use their own
746              * GetMessage loop, setup this timer so we get some control back.
747              *
748              * Also call term_update() from the timer so that if the host
749              * is sending data flat out we still do redraws.
750              */
751             if (timer_id && long_timer) {
752                 KillTimer(hwnd, timer_id);
753                 long_timer = timer_id = 0;
754             }
755             if (!timer_id)
756                 timer_id = SetTimer(hwnd, 1, 20, NULL);
757             if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
758                 DispatchMessage(&msg);
759
760             /* Make sure we blink everything that needs it. */
761             term_blink(term, 0);
762
763             /* Send the paste buffer if there's anything to send */
764             term_paste(term);
765
766             /* If there's nothing new in the queue then we can do everything
767              * we've delayed, reading the socket, writing, and repainting
768              * the window.
769              */
770             if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
771                 continue;
772
773             if (pending_netevent) {
774                 enact_pending_netevent();
775
776                 /* Force the cursor blink on */
777                 term_blink(term, 1);
778
779                 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
780                     continue;
781             }
782
783             /* Okay there is now nothing to do so we make sure the screen is
784              * completely up to date then tell windows to call us in a little 
785              * while.
786              */
787             if (timer_id) {
788                 KillTimer(hwnd, timer_id);
789                 timer_id = 0;
790             }
791             HideCaret(hwnd);
792             if (GetCapture() != hwnd || 
793                 (send_raw_mouse &&
794                  !(cfg.mouse_override && is_shift_pressed())))
795                 term_out(term);
796             term_update(term);
797             ShowCaret(hwnd);
798
799             flash_window(1);           /* maintain */
800
801             /* The messages seem unreliable; especially if we're being tricky */
802             term->has_focus = (GetForegroundWindow() == hwnd);
803
804             if (term->in_vbell)
805                 /* Hmm, term_update didn't want to do an update too soon ... */
806                 timer_id = SetTimer(hwnd, 1, 50, NULL);
807             else if (!term->has_focus)
808                 timer_id = SetTimer(hwnd, 1, 500, NULL);
809             else
810                 timer_id = SetTimer(hwnd, 1, 100, NULL);
811             long_timer = 1;
812
813             /* There's no point rescanning everything in the message queue
814              * so we do an apparently unnecessary wait here
815              */
816             WaitMessage();
817             if (GetMessage(&msg, NULL, 0, 0) != 1)
818                 break;
819         }
820     }
821
822     cleanup_exit(msg.wParam);          /* this doesn't return... */
823     return msg.wParam;                 /* ... but optimiser doesn't know */
824 }
825
826 /*
827  * Clean up and exit.
828  */
829 void cleanup_exit(int code)
830 {
831     /*
832      * Clean up.
833      */
834     deinit_fonts();
835     sfree(logpal);
836     if (pal)
837         DeleteObject(pal);
838     sk_cleanup();
839     if (wsa_started)
840         WSACleanup();
841
842     if (cfg.protocol == PROT_SSH) {
843         random_save_seed();
844 #ifdef MSCRYPTOAPI
845         crypto_wrapup();
846 #endif
847     }
848
849     exit(code);
850 }
851
852 /*
853  * Set up, or shut down, an AsyncSelect. Called from winnet.c.
854  */
855 char *do_select(SOCKET skt, int startup)
856 {
857     int msg, events;
858     if (startup) {
859         msg = WM_NETEVENT;
860         events = (FD_CONNECT | FD_READ | FD_WRITE |
861                   FD_OOB | FD_CLOSE | FD_ACCEPT);
862     } else {
863         msg = events = 0;
864     }
865     if (!hwnd)
866         return "do_select(): internal error (hwnd==NULL)";
867     if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
868         switch (WSAGetLastError()) {
869           case WSAENETDOWN:
870             return "Network is down";
871           default:
872             return "WSAAsyncSelect(): unknown error";
873         }
874     }
875     return NULL;
876 }
877
878 /*
879  * set or clear the "raw mouse message" mode
880  */
881 void set_raw_mouse_mode(void *frontend, int activate)
882 {
883     activate = activate && !cfg.no_mouse_rep;
884     send_raw_mouse = activate;
885     SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
886 }
887
888 /*
889  * Print a message box and close the connection.
890  */
891 void connection_fatal(void *frontend, char *fmt, ...)
892 {
893     va_list ap;
894     char stuff[200];
895
896     va_start(ap, fmt);
897     vsprintf(stuff, fmt, ap);
898     va_end(ap);
899     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
900     if (cfg.close_on_exit == COE_ALWAYS)
901         PostQuitMessage(1);
902     else {
903         session_closed = TRUE;
904         SetWindowText(hwnd, "PuTTY (inactive)");
905     }
906 }
907
908 /*
909  * Report an error at the command-line parsing stage.
910  */
911 void cmdline_error(char *fmt, ...)
912 {
913     va_list ap;
914     char stuff[200];
915
916     va_start(ap, fmt);
917     vsprintf(stuff, fmt, ap);
918     va_end(ap);
919     MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
920     exit(1);
921 }
922
923 /*
924  * Actually do the job requested by a WM_NETEVENT
925  */
926 static void enact_pending_netevent(void)
927 {
928     static int reentering = 0;
929     extern int select_result(WPARAM, LPARAM);
930     int ret;
931
932     if (reentering)
933         return;                        /* don't unpend the pending */
934
935     pending_netevent = FALSE;
936
937     reentering = 1;
938     ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
939     reentering = 0;
940
941     if (ret == 0 && !session_closed) {
942         /* Abnormal exits will already have set session_closed and taken
943          * appropriate action. */
944         if (cfg.close_on_exit == COE_ALWAYS ||
945             cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
946         else {
947             session_closed = TRUE;
948             SetWindowText(hwnd, "PuTTY (inactive)");
949             MessageBox(hwnd, "Connection closed by remote host",
950                        "PuTTY", MB_OK | MB_ICONINFORMATION);
951         }
952     }
953 }
954
955 /*
956  * Copy the colour palette from the configuration data into defpal.
957  * This is non-trivial because the colour indices are different.
958  */
959 static void cfgtopalette(void)
960 {
961     int i;
962     static const int ww[] = {
963         6, 7, 8, 9, 10, 11, 12, 13,
964         14, 15, 16, 17, 18, 19, 20, 21,
965         0, 1, 2, 3, 4, 4, 5, 5
966     };
967
968     for (i = 0; i < 24; i++) {
969         int w = ww[i];
970         defpal[i].rgbtRed = cfg.colours[w][0];
971         defpal[i].rgbtGreen = cfg.colours[w][1];
972         defpal[i].rgbtBlue = cfg.colours[w][2];
973     }
974 }
975
976 /*
977  * Set up the colour palette.
978  */
979 static void init_palette(void)
980 {
981     int i;
982     HDC hdc = GetDC(hwnd);
983     if (hdc) {
984         if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
985             logpal = smalloc(sizeof(*logpal)
986                              - sizeof(logpal->palPalEntry)
987                              + NCOLOURS * sizeof(PALETTEENTRY));
988             logpal->palVersion = 0x300;
989             logpal->palNumEntries = NCOLOURS;
990             for (i = 0; i < NCOLOURS; i++) {
991                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
992                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
993                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
994                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
995             }
996             pal = CreatePalette(logpal);
997             if (pal) {
998                 SelectPalette(hdc, pal, FALSE);
999                 RealizePalette(hdc);
1000                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1001             }
1002         }
1003         ReleaseDC(hwnd, hdc);
1004     }
1005     if (pal)
1006         for (i = 0; i < NCOLOURS; i++)
1007             colours[i] = PALETTERGB(defpal[i].rgbtRed,
1008                                     defpal[i].rgbtGreen,
1009                                     defpal[i].rgbtBlue);
1010     else
1011         for (i = 0; i < NCOLOURS; i++)
1012             colours[i] = RGB(defpal[i].rgbtRed,
1013                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1014 }
1015
1016 /*
1017  * Initialise all the fonts we will need initially. There may be as many as
1018  * three or as few as one.  The other (poentially) twentyone fonts are done
1019  * if/when they are needed.
1020  *
1021  * We also:
1022  *
1023  * - check the font width and height, correcting our guesses if
1024  *   necessary.
1025  *
1026  * - verify that the bold font is the same width as the ordinary
1027  *   one, and engage shadow bolding if not.
1028  * 
1029  * - verify that the underlined font is the same width as the
1030  *   ordinary one (manual underlining by means of line drawing can
1031  *   be done in a pinch).
1032  */
1033 static void init_fonts(int pick_width, int pick_height)
1034 {
1035     TEXTMETRIC tm;
1036     CPINFO cpinfo;
1037     int fontsize[3];
1038     int i;
1039     HDC hdc;
1040     int fw_dontcare, fw_bold;
1041
1042     for (i = 0; i < FONT_MAXNO; i++)
1043         fonts[i] = NULL;
1044
1045     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1046     und_mode = UND_FONT;
1047
1048     if (cfg.fontisbold) {
1049         fw_dontcare = FW_BOLD;
1050         fw_bold = FW_HEAVY;
1051     } else {
1052         fw_dontcare = FW_DONTCARE;
1053         fw_bold = FW_BOLD;
1054     }
1055
1056     hdc = GetDC(hwnd);
1057
1058     if (pick_height)
1059         font_height = pick_height;
1060     else {
1061         font_height = cfg.fontheight;
1062         if (font_height > 0) {
1063             font_height =
1064                 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1065         }
1066     }
1067     font_width = pick_width;
1068
1069 #define f(i,c,w,u) \
1070     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1071                            c, OUT_DEFAULT_PRECIS, \
1072                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1073                            FIXED_PITCH | FF_DONTCARE, cfg.font)
1074
1075     f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
1076
1077     lfont.lfHeight = font_height;
1078     lfont.lfWidth = font_width;
1079     lfont.lfEscapement = 0;
1080     lfont.lfOrientation  = 0;
1081     lfont.lfWeight  = fw_dontcare;
1082     lfont.lfItalic = FALSE;
1083     lfont.lfUnderline = FALSE;
1084     lfont.lfStrikeOut = FALSE;
1085     lfont.lfCharSet = cfg.fontcharset;
1086     lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1087     lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1088     lfont.lfQuality = DEFAULT_QUALITY;
1089     lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1090     strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
1091
1092     SelectObject(hdc, fonts[FONT_NORMAL]);
1093     GetTextMetrics(hdc, &tm);
1094
1095     if (pick_width == 0 || pick_height == 0) {
1096         font_height = tm.tmHeight;
1097         font_width = tm.tmAveCharWidth;
1098     }
1099     font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1100
1101 #ifdef RDB_DEBUG_PATCH
1102     debug(23, "Primary font H=%d, AW=%d, MW=%d",
1103             tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1104 #endif
1105
1106     {
1107         CHARSETINFO info;
1108         DWORD cset = tm.tmCharSet;
1109         memset(&info, 0xFF, sizeof(info));
1110
1111         /* !!! Yes the next line is right */
1112         if (cset == OEM_CHARSET)
1113             font_codepage = GetOEMCP();
1114         else
1115             if (TranslateCharsetInfo
1116                 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
1117                 info.ciACP;
1118         else
1119             font_codepage = -1;
1120
1121         GetCPInfo(font_codepage, &cpinfo);
1122         dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1123     }
1124
1125     f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
1126
1127     /*
1128      * Some fonts, e.g. 9-pt Courier, draw their underlines
1129      * outside their character cell. We successfully prevent
1130      * screen corruption by clipping the text output, but then
1131      * we lose the underline completely. Here we try to work
1132      * out whether this is such a font, and if it is, we set a
1133      * flag that causes underlines to be drawn by hand.
1134      *
1135      * Having tried other more sophisticated approaches (such
1136      * as examining the TEXTMETRIC structure or requesting the
1137      * height of a string), I think we'll do this the brute
1138      * force way: we create a small bitmap, draw an underlined
1139      * space on it, and test to see whether any pixels are
1140      * foreground-coloured. (Since we expect the underline to
1141      * go all the way across the character cell, we only search
1142      * down a single column of the bitmap, half way across.)
1143      */
1144     {
1145         HDC und_dc;
1146         HBITMAP und_bm, und_oldbm;
1147         int i, gotit;
1148         COLORREF c;
1149
1150         und_dc = CreateCompatibleDC(hdc);
1151         und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1152         und_oldbm = SelectObject(und_dc, und_bm);
1153         SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1154         SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1155         SetTextColor(und_dc, RGB(255, 255, 255));
1156         SetBkColor(und_dc, RGB(0, 0, 0));
1157         SetBkMode(und_dc, OPAQUE);
1158         ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1159         gotit = FALSE;
1160         for (i = 0; i < font_height; i++) {
1161             c = GetPixel(und_dc, font_width / 2, i);
1162             if (c != RGB(0, 0, 0))
1163                 gotit = TRUE;
1164         }
1165         SelectObject(und_dc, und_oldbm);
1166         DeleteObject(und_bm);
1167         DeleteDC(und_dc);
1168         if (!gotit) {
1169             und_mode = UND_LINE;
1170             DeleteObject(fonts[FONT_UNDERLINE]);
1171             fonts[FONT_UNDERLINE] = 0;
1172         }
1173     }
1174
1175     if (bold_mode == BOLD_FONT) {
1176         f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1177     }
1178 #undef f
1179
1180     descent = tm.tmAscent + 1;
1181     if (descent >= font_height)
1182         descent = font_height - 1;
1183
1184     for (i = 0; i < 3; i++) {
1185         if (fonts[i]) {
1186             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1187                 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1188             else
1189                 fontsize[i] = -i;
1190         } else
1191             fontsize[i] = -i;
1192     }
1193
1194     ReleaseDC(hwnd, hdc);
1195
1196     if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1197         und_mode = UND_LINE;
1198         DeleteObject(fonts[FONT_UNDERLINE]);
1199         fonts[FONT_UNDERLINE] = 0;
1200     }
1201
1202     if (bold_mode == BOLD_FONT &&
1203         fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1204         bold_mode = BOLD_SHADOW;
1205         DeleteObject(fonts[FONT_BOLD]);
1206         fonts[FONT_BOLD] = 0;
1207     }
1208     fontflag[0] = fontflag[1] = fontflag[2] = 1;
1209
1210     init_ucs(&cfg);
1211 }
1212
1213 static void another_font(int fontno)
1214 {
1215     int basefont;
1216     int fw_dontcare, fw_bold;
1217     int c, u, w, x;
1218     char *s;
1219
1220     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1221         return;
1222
1223     basefont = (fontno & ~(FONT_BOLDUND));
1224     if (basefont != fontno && !fontflag[basefont])
1225         another_font(basefont);
1226
1227     if (cfg.fontisbold) {
1228         fw_dontcare = FW_BOLD;
1229         fw_bold = FW_HEAVY;
1230     } else {
1231         fw_dontcare = FW_DONTCARE;
1232         fw_bold = FW_BOLD;
1233     }
1234
1235     c = cfg.fontcharset;
1236     w = fw_dontcare;
1237     u = FALSE;
1238     s = cfg.font;
1239     x = font_width;
1240
1241     if (fontno & FONT_WIDE)
1242         x *= 2;
1243     if (fontno & FONT_NARROW)
1244         x = (x+1)/2;
1245     if (fontno & FONT_OEM)
1246         c = OEM_CHARSET;
1247     if (fontno & FONT_BOLD)
1248         w = fw_bold;
1249     if (fontno & FONT_UNDERLINE)
1250         u = TRUE;
1251
1252     fonts[fontno] =
1253         CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1254                    FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1255                    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1256                    FIXED_PITCH | FF_DONTCARE, s);
1257
1258     fontflag[fontno] = 1;
1259 }
1260
1261 static void deinit_fonts(void)
1262 {
1263     int i;
1264     for (i = 0; i < FONT_MAXNO; i++) {
1265         if (fonts[i])
1266             DeleteObject(fonts[i]);
1267         fonts[i] = 0;
1268         fontflag[i] = 0;
1269     }
1270 }
1271
1272 void request_resize(void *frontend, int w, int h)
1273 {
1274     int width, height;
1275
1276     /* If the window is maximized supress resizing attempts */
1277     if (IsZoomed(hwnd)) {
1278         if (cfg.resize_action == RESIZE_TERM)
1279             return;
1280     }
1281
1282     if (cfg.resize_action == RESIZE_DISABLED) return;
1283     if (h == term->rows && w == term->cols) return;
1284
1285     /* Sanity checks ... */
1286     {
1287         static int first_time = 1;
1288         static RECT ss;
1289
1290         switch (first_time) {
1291           case 1:
1292             /* Get the size of the screen */
1293             if (get_fullscreen_rect(&ss))
1294                 /* first_time = 0 */ ;
1295             else {
1296                 first_time = 2;
1297                 break;
1298             }
1299           case 0:
1300             /* Make sure the values are sane */
1301             width = (ss.right - ss.left - extra_width) / 4;
1302             height = (ss.bottom - ss.top - extra_height) / 6;
1303
1304             if (w > width || h > height)
1305                 return;
1306             if (w < 15)
1307                 w = 15;
1308             if (h < 1)
1309                 h = 1;
1310         }
1311     }
1312
1313     term_size(term, h, w, cfg.savelines);
1314
1315     if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1316         width = extra_width + font_width * w;
1317         height = extra_height + font_height * h;
1318
1319         SetWindowPos(hwnd, NULL, 0, 0, width, height,
1320             SWP_NOACTIVATE | SWP_NOCOPYBITS |
1321             SWP_NOMOVE | SWP_NOZORDER);
1322     } else
1323         reset_window(0);
1324
1325     InvalidateRect(hwnd, NULL, TRUE);
1326 }
1327
1328 static void reset_window(int reinit) {
1329     /*
1330      * This function decides how to resize or redraw when the 
1331      * user changes something. 
1332      *
1333      * This function doesn't like to change the terminal size but if the
1334      * font size is locked that may be it's only soluion.
1335      */
1336     int win_width, win_height;
1337     RECT cr, wr;
1338
1339 #ifdef RDB_DEBUG_PATCH
1340     debug((27, "reset_window()"));
1341 #endif
1342
1343     /* Current window sizes ... */
1344     GetWindowRect(hwnd, &wr);
1345     GetClientRect(hwnd, &cr);
1346
1347     win_width  = cr.right - cr.left;
1348     win_height = cr.bottom - cr.top;
1349
1350     if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1351
1352     /* Are we being forced to reload the fonts ? */
1353     if (reinit>1) {
1354 #ifdef RDB_DEBUG_PATCH
1355         debug((27, "reset_window() -- Forced deinit"));
1356 #endif
1357         deinit_fonts();
1358         init_fonts(0,0);
1359     }
1360
1361     /* Oh, looks like we're minimised */
1362     if (win_width == 0 || win_height == 0)
1363         return;
1364
1365     /* Is the window out of position ? */
1366     if ( !reinit && 
1367             (offset_width != (win_width-font_width*term->cols)/2 ||
1368              offset_height != (win_height-font_height*term->rows)/2) ){
1369         offset_width = (win_width-font_width*term->cols)/2;
1370         offset_height = (win_height-font_height*term->rows)/2;
1371         InvalidateRect(hwnd, NULL, TRUE);
1372 #ifdef RDB_DEBUG_PATCH
1373         debug((27, "reset_window() -> Reposition terminal"));
1374 #endif
1375     }
1376
1377     if (IsZoomed(hwnd)) {
1378         /* We're fullscreen, this means we must not change the size of
1379          * the window so it's the font size or the terminal itself.
1380          */
1381
1382         extra_width = wr.right - wr.left - cr.right + cr.left;
1383         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1384
1385         if (cfg.resize_action != RESIZE_TERM) {
1386             if (  font_width != win_width/term->cols || 
1387                   font_height != win_height/term->rows) {
1388                 deinit_fonts();
1389                 init_fonts(win_width/term->cols, win_height/term->rows);
1390                 offset_width = (win_width-font_width*term->cols)/2;
1391                 offset_height = (win_height-font_height*term->rows)/2;
1392                 InvalidateRect(hwnd, NULL, TRUE);
1393 #ifdef RDB_DEBUG_PATCH
1394                 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1395                         font_width, font_height));
1396 #endif
1397             }
1398         } else {
1399             if (  font_width != win_width/term->cols || 
1400                   font_height != win_height/term->rows) {
1401                 /* Our only choice at this point is to change the 
1402                  * size of the terminal; Oh well.
1403                  */
1404                 term_size(term, win_height/font_height, win_width/font_width,
1405                           cfg.savelines);
1406                 offset_width = (win_width-font_width*term->cols)/2;
1407                 offset_height = (win_height-font_height*term->rows)/2;
1408                 InvalidateRect(hwnd, NULL, TRUE);
1409 #ifdef RDB_DEBUG_PATCH
1410                 debug((27, "reset_window() -> Zoomed term_size"));
1411 #endif
1412             }
1413         }
1414         return;
1415     }
1416
1417     /* Hmm, a force re-init means we should ignore the current window
1418      * so we resize to the default font size.
1419      */
1420     if (reinit>0) {
1421 #ifdef RDB_DEBUG_PATCH
1422         debug((27, "reset_window() -> Forced re-init"));
1423 #endif
1424
1425         offset_width = offset_height = cfg.window_border;
1426         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1427         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1428
1429         if (win_width != font_width*term->cols + offset_width*2 ||
1430             win_height != font_height*term->rows + offset_height*2) {
1431
1432             /* If this is too large windows will resize it to the maximum
1433              * allowed window size, we will then be back in here and resize
1434              * the font or terminal to fit.
1435              */
1436             SetWindowPos(hwnd, NULL, 0, 0, 
1437                          font_width*term->cols + extra_width, 
1438                          font_height*term->rows + extra_height,
1439                          SWP_NOMOVE | SWP_NOZORDER);
1440         }
1441
1442         InvalidateRect(hwnd, NULL, TRUE);
1443         return;
1444     }
1445
1446     /* Okay the user doesn't want us to change the font so we try the 
1447      * window. But that may be too big for the screen which forces us
1448      * to change the terminal.
1449      */
1450     if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1451         (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1452             reinit>0) {
1453         offset_width = offset_height = cfg.window_border;
1454         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1455         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1456
1457         if (win_width != font_width*term->cols + offset_width*2 ||
1458             win_height != font_height*term->rows + offset_height*2) {
1459
1460             static RECT ss;
1461             int width, height;
1462                 
1463                 get_fullscreen_rect(&ss);
1464
1465             width = (ss.right - ss.left - extra_width) / font_width;
1466             height = (ss.bottom - ss.top - extra_height) / font_height;
1467
1468             /* Grrr too big */
1469             if ( term->rows > height || term->cols > width ) {
1470                 if (cfg.resize_action == RESIZE_EITHER) {
1471                     /* Make the font the biggest we can */
1472                     if (term->cols > width)
1473                         font_width = (ss.right - ss.left - extra_width)
1474                             / term->cols;
1475                     if (term->rows > height)
1476                         font_height = (ss.bottom - ss.top - extra_height)
1477                             / term->rows;
1478
1479                     deinit_fonts();
1480                     init_fonts(font_width, font_height);
1481
1482                     width = (ss.right - ss.left - extra_width) / font_width;
1483                     height = (ss.bottom - ss.top - extra_height) / font_height;
1484                 } else {
1485                     if ( height > term->rows ) height = term->rows;
1486                     if ( width > term->cols )  width = term->cols;
1487                     term_size(term, height, width, cfg.savelines);
1488 #ifdef RDB_DEBUG_PATCH
1489                     debug((27, "reset_window() -> term resize to (%d,%d)",
1490                                height, width));
1491 #endif
1492                 }
1493             }
1494             
1495             SetWindowPos(hwnd, NULL, 0, 0, 
1496                          font_width*term->cols + extra_width, 
1497                          font_height*term->rows + extra_height,
1498                          SWP_NOMOVE | SWP_NOZORDER);
1499
1500             InvalidateRect(hwnd, NULL, TRUE);
1501 #ifdef RDB_DEBUG_PATCH
1502             debug((27, "reset_window() -> window resize to (%d,%d)",
1503                         font_width*term->cols + extra_width,
1504                         font_height*term->rows + extra_height));
1505 #endif
1506         }
1507         return;
1508     }
1509
1510     /* We're allowed to or must change the font but do we want to ?  */
1511
1512     if (font_width != (win_width-cfg.window_border*2)/term->cols || 
1513         font_height != (win_height-cfg.window_border*2)/term->rows) {
1514
1515         deinit_fonts();
1516         init_fonts((win_width-cfg.window_border*2)/term->cols, 
1517                    (win_height-cfg.window_border*2)/term->rows);
1518         offset_width = (win_width-font_width*term->cols)/2;
1519         offset_height = (win_height-font_height*term->rows)/2;
1520
1521         extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1522         extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1523
1524         InvalidateRect(hwnd, NULL, TRUE);
1525 #ifdef RDB_DEBUG_PATCH
1526         debug((25, "reset_window() -> font resize to (%d,%d)", 
1527                    font_width, font_height));
1528 #endif
1529     }
1530 }
1531
1532 static void set_input_locale(HKL kl)
1533 {
1534     char lbuf[20];
1535
1536     GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1537                   lbuf, sizeof(lbuf));
1538
1539     kbd_codepage = atoi(lbuf);
1540 }
1541
1542 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1543 {
1544     int thistime = GetMessageTime();
1545
1546     if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1547         lastbtn = MBT_NOTHING;
1548         term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt);
1549         return;
1550     }
1551
1552     if (lastbtn == b && thistime - lasttime < dbltime) {
1553         lastact = (lastact == MA_CLICK ? MA_2CLK :
1554                    lastact == MA_2CLK ? MA_3CLK :
1555                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1556     } else {
1557         lastbtn = b;
1558         lastact = MA_CLICK;
1559     }
1560     if (lastact != MA_NOTHING)
1561         term_mouse(term, b, lastact, x, y, shift, ctrl, alt);
1562     lasttime = thistime;
1563 }
1564
1565 /*
1566  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1567  * into a cooked one (SELECT, EXTEND, PASTE).
1568  */
1569 Mouse_Button translate_button(void *frontend, Mouse_Button button)
1570 {
1571     if (button == MBT_LEFT)
1572         return MBT_SELECT;
1573     if (button == MBT_MIDDLE)
1574         return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1575     if (button == MBT_RIGHT)
1576         return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1577     return 0;                          /* shouldn't happen */
1578 }
1579
1580 static void show_mouseptr(int show)
1581 {
1582     static int cursor_visible = 1;
1583     if (!cfg.hide_mouseptr)            /* override if this feature disabled */
1584         show = 1;
1585     if (cursor_visible && !show)
1586         ShowCursor(FALSE);
1587     else if (!cursor_visible && show)
1588         ShowCursor(TRUE);
1589     cursor_visible = show;
1590 }
1591
1592 static int is_alt_pressed(void)
1593 {
1594     BYTE keystate[256];
1595     int r = GetKeyboardState(keystate);
1596     if (!r)
1597         return FALSE;
1598     if (keystate[VK_MENU] & 0x80)
1599         return TRUE;
1600     if (keystate[VK_RMENU] & 0x80)
1601         return TRUE;
1602     return FALSE;
1603 }
1604
1605 static int is_shift_pressed(void)
1606 {
1607     BYTE keystate[256];
1608     int r = GetKeyboardState(keystate);
1609     if (!r)
1610         return FALSE;
1611     if (keystate[VK_SHIFT] & 0x80)
1612         return TRUE;
1613     return FALSE;
1614 }
1615
1616 static int resizing;
1617
1618 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1619                                 WPARAM wParam, LPARAM lParam)
1620 {
1621     HDC hdc;
1622     static int ignore_clip = FALSE;
1623     static int need_backend_resize = FALSE;
1624     static int fullscr_on_max = FALSE;
1625
1626     switch (message) {
1627       case WM_TIMER:
1628         if (pending_netevent)
1629             enact_pending_netevent();
1630         if (GetCapture() != hwnd || 
1631             (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
1632             term_out(term);
1633         noise_regular();
1634         HideCaret(hwnd);
1635         term_update(term);
1636         ShowCaret(hwnd);
1637         if (cfg.ping_interval > 0) {
1638             time_t now;
1639             time(&now);
1640             if (now - last_movement > cfg.ping_interval) {
1641                 back->special(backhandle, TS_PING);
1642                 last_movement = now;
1643             }
1644         }
1645         net_pending_errors();
1646         return 0;
1647       case WM_CREATE:
1648         break;
1649       case WM_CLOSE:
1650         show_mouseptr(1);
1651         if (!cfg.warn_on_close || session_closed ||
1652             MessageBox(hwnd,
1653                        "Are you sure you want to close this session?",
1654                        "PuTTY Exit Confirmation",
1655                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1656             DestroyWindow(hwnd);
1657         return 0;
1658       case WM_DESTROY:
1659         show_mouseptr(1);
1660         PostQuitMessage(0);
1661         return 0;
1662       case WM_SYSCOMMAND:
1663         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1664           case IDM_SHOWLOG:
1665             showeventlog(hwnd);
1666             break;
1667           case IDM_NEWSESS:
1668           case IDM_DUPSESS:
1669           case IDM_SAVEDSESS:
1670             {
1671                 char b[2048];
1672                 char c[30], *cl;
1673                 int freecl = FALSE;
1674                 STARTUPINFO si;
1675                 PROCESS_INFORMATION pi;
1676                 HANDLE filemap = NULL;
1677
1678                 if (wParam == IDM_DUPSESS) {
1679                     /*
1680                      * Allocate a file-mapping memory chunk for the
1681                      * config structure.
1682                      */
1683                     SECURITY_ATTRIBUTES sa;
1684                     Config *p;
1685
1686                     sa.nLength = sizeof(sa);
1687                     sa.lpSecurityDescriptor = NULL;
1688                     sa.bInheritHandle = TRUE;
1689                     filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1690                                                 &sa,
1691                                                 PAGE_READWRITE,
1692                                                 0, sizeof(Config), NULL);
1693                     if (filemap) {
1694                         p = (Config *) MapViewOfFile(filemap,
1695                                                      FILE_MAP_WRITE,
1696                                                      0, 0, sizeof(Config));
1697                         if (p) {
1698                             *p = cfg;  /* structure copy */
1699                             UnmapViewOfFile(p);
1700                         }
1701                     }
1702                     sprintf(c, "putty &%p", filemap);
1703                     cl = c;
1704                 } else if (wParam == IDM_SAVEDSESS) {
1705                     if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
1706                         char *session =
1707                             sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
1708                         cl = smalloc(16 + strlen(session));
1709                                        /* 8, but play safe */
1710                         if (!cl)
1711                             cl = NULL;    
1712                                        /* not a very important failure mode */
1713                         else {
1714                             sprintf(cl, "putty @%s", session);
1715                             freecl = TRUE;
1716                         }
1717                     } else
1718                         break;
1719                 } else
1720                     cl = NULL;
1721
1722                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1723                 si.cb = sizeof(si);
1724                 si.lpReserved = NULL;
1725                 si.lpDesktop = NULL;
1726                 si.lpTitle = NULL;
1727                 si.dwFlags = 0;
1728                 si.cbReserved2 = 0;
1729                 si.lpReserved2 = NULL;
1730                 CreateProcess(b, cl, NULL, NULL, TRUE,
1731                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1732
1733                 if (filemap)
1734                     CloseHandle(filemap);
1735                 if (freecl)
1736                     sfree(cl);
1737             }
1738             break;
1739           case IDM_RECONF:
1740             {
1741                 Config prev_cfg;
1742                 int init_lvl = 1;
1743
1744                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1745                 prev_cfg = cfg;
1746
1747                 if (!do_reconfig(hwnd))
1748                     break;
1749
1750                 {
1751                     /* Disable full-screen if resizing forbidden */
1752                     HMENU m = GetSystemMenu (hwnd, FALSE);
1753                     EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND | 
1754                                    (cfg.resize_action == RESIZE_DISABLED)
1755                                    ? MF_GRAYED : MF_ENABLED);
1756                     /* Gracefully unzoom if necessary */
1757                     if (IsZoomed(hwnd) &&
1758                         (cfg.resize_action == RESIZE_DISABLED)) {
1759                         ShowWindow(hwnd, SW_RESTORE);
1760                     }
1761                 }
1762
1763                 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1764                     prev_cfg.logtype != cfg.logtype) {
1765                     logfclose(logctx); /* reset logging */
1766                     logfopen(logctx);
1767                 }
1768
1769                 sfree(logpal);
1770                 /*
1771                  * Flush the line discipline's edit buffer in the
1772                  * case where local editing has just been disabled.
1773                  */
1774                 ldisc_send(ldisc, NULL, 0, 0);
1775                 if (pal)
1776                     DeleteObject(pal);
1777                 logpal = NULL;
1778                 pal = NULL;
1779                 cfgtopalette();
1780                 init_palette();
1781
1782                 /* Pass new config data to the terminal */
1783                 term_reconfig(term, &cfg);
1784
1785                 /* Pass new config data to the back end */
1786                 back->reconfig(back, &cfg);
1787
1788                 /* Screen size changed ? */
1789                 if (cfg.height != prev_cfg.height ||
1790                     cfg.width != prev_cfg.width ||
1791                     cfg.savelines != prev_cfg.savelines ||
1792                     cfg.resize_action == RESIZE_FONT ||
1793                     (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1794                     cfg.resize_action == RESIZE_DISABLED)
1795                     term_size(term, cfg.height, cfg.width, cfg.savelines);
1796
1797                 /* Enable or disable the scroll bar, etc */
1798                 {
1799                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1800                     LONG nexflag, exflag =
1801                         GetWindowLong(hwnd, GWL_EXSTYLE);
1802
1803                     nexflag = exflag;
1804                     if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1805                         if (cfg.alwaysontop) {
1806                             nexflag |= WS_EX_TOPMOST;
1807                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1808                                          SWP_NOMOVE | SWP_NOSIZE);
1809                         } else {
1810                             nexflag &= ~(WS_EX_TOPMOST);
1811                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1812                                          SWP_NOMOVE | SWP_NOSIZE);
1813                         }
1814                     }
1815                     if (cfg.sunken_edge)
1816                         nexflag |= WS_EX_CLIENTEDGE;
1817                     else
1818                         nexflag &= ~(WS_EX_CLIENTEDGE);
1819
1820                     nflg = flag;
1821                     if (is_full_screen() ?
1822                         cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1823                         nflg |= WS_VSCROLL;
1824                     else
1825                         nflg &= ~WS_VSCROLL;
1826
1827                     if (cfg.resize_action == RESIZE_DISABLED ||
1828                         is_full_screen())
1829                         nflg &= ~WS_THICKFRAME;
1830                     else
1831                         nflg |= WS_THICKFRAME;
1832
1833                     if (cfg.resize_action == RESIZE_DISABLED)
1834                         nflg &= ~WS_MAXIMIZEBOX;
1835                     else
1836                         nflg |= WS_MAXIMIZEBOX;
1837
1838                     if (nflg != flag || nexflag != exflag) {
1839                         if (nflg != flag)
1840                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1841                         if (nexflag != exflag)
1842                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1843
1844                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1845                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1846                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1847                                      SWP_FRAMECHANGED);
1848
1849                         init_lvl = 2;
1850                     }
1851                 }
1852
1853                 /* Oops */
1854                 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1855                     force_normal(hwnd);
1856                     init_lvl = 2;
1857                 }
1858
1859                 set_title(NULL, cfg.wintitle);
1860                 if (IsIconic(hwnd)) {
1861                     SetWindowText(hwnd,
1862                                   cfg.win_name_always ? window_name :
1863                                   icon_name);
1864                 }
1865
1866                 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1867                     strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1868                     cfg.fontisbold != prev_cfg.fontisbold ||
1869                     cfg.fontheight != prev_cfg.fontheight ||
1870                     cfg.fontcharset != prev_cfg.fontcharset ||
1871                     cfg.vtmode != prev_cfg.vtmode ||
1872                     cfg.bold_colour != prev_cfg.bold_colour ||
1873                     cfg.resize_action == RESIZE_DISABLED ||
1874                     cfg.resize_action == RESIZE_EITHER ||
1875                     (cfg.resize_action != prev_cfg.resize_action))
1876                     init_lvl = 2;
1877
1878                 InvalidateRect(hwnd, NULL, TRUE);
1879                 reset_window(init_lvl);
1880                 net_pending_errors();
1881             }
1882             break;
1883           case IDM_COPYALL:
1884             term_copyall(term);
1885             break;
1886           case IDM_CLRSB:
1887             term_clrsb(term);
1888             break;
1889           case IDM_RESET:
1890             term_pwron(term);
1891             ldisc_send(ldisc, NULL, 0, 0);
1892             break;
1893           case IDM_TEL_AYT:
1894             back->special(backhandle, TS_AYT);
1895             net_pending_errors();
1896             break;
1897           case IDM_TEL_BRK:
1898             back->special(backhandle, TS_BRK);
1899             net_pending_errors();
1900             break;
1901           case IDM_TEL_SYNCH:
1902             back->special(backhandle, TS_SYNCH);
1903             net_pending_errors();
1904             break;
1905           case IDM_TEL_EC:
1906             back->special(backhandle, TS_EC);
1907             net_pending_errors();
1908             break;
1909           case IDM_TEL_EL:
1910             back->special(backhandle, TS_EL);
1911             net_pending_errors();
1912             break;
1913           case IDM_TEL_GA:
1914             back->special(backhandle, TS_GA);
1915             net_pending_errors();
1916             break;
1917           case IDM_TEL_NOP:
1918             back->special(backhandle, TS_NOP);
1919             net_pending_errors();
1920             break;
1921           case IDM_TEL_ABORT:
1922             back->special(backhandle, TS_ABORT);
1923             net_pending_errors();
1924             break;
1925           case IDM_TEL_AO:
1926             back->special(backhandle, TS_AO);
1927             net_pending_errors();
1928             break;
1929           case IDM_TEL_IP:
1930             back->special(backhandle, TS_IP);
1931             net_pending_errors();
1932             break;
1933           case IDM_TEL_SUSP:
1934             back->special(backhandle, TS_SUSP);
1935             net_pending_errors();
1936             break;
1937           case IDM_TEL_EOR:
1938             back->special(backhandle, TS_EOR);
1939             net_pending_errors();
1940             break;
1941           case IDM_TEL_EOF:
1942             back->special(backhandle, TS_EOF);
1943             net_pending_errors();
1944             break;
1945           case IDM_ABOUT:
1946             showabout(hwnd);
1947             break;
1948           case IDM_HELP:
1949             WinHelp(hwnd, help_path,
1950                     help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1951             break;
1952           case SC_MOUSEMENU:
1953             /*
1954              * We get this if the System menu has been activated
1955              * using the mouse.
1956              */
1957             show_mouseptr(1);
1958             break;
1959           case SC_KEYMENU:
1960             /*
1961              * We get this if the System menu has been activated
1962              * using the keyboard. This might happen from within
1963              * TranslateKey, in which case it really wants to be
1964              * followed by a `space' character to actually _bring
1965              * the menu up_ rather than just sitting there in
1966              * `ready to appear' state.
1967              */
1968             show_mouseptr(1);          /* make sure pointer is visible */
1969             if( lParam == 0 )
1970                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1971             break;
1972           case IDM_FULLSCREEN:
1973             flip_full_screen();
1974             break;
1975           default:
1976             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1977                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1978             }
1979         }
1980         break;
1981
1982 #define X_POS(l) ((int)(short)LOWORD(l))
1983 #define Y_POS(l) ((int)(short)HIWORD(l))
1984
1985 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1986 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1987       case WM_LBUTTONDOWN:
1988       case WM_MBUTTONDOWN:
1989       case WM_RBUTTONDOWN:
1990       case WM_LBUTTONUP:
1991       case WM_MBUTTONUP:
1992       case WM_RBUTTONUP:
1993         {
1994             int button, press;
1995
1996             switch (message) {
1997               case WM_LBUTTONDOWN:
1998                 button = MBT_LEFT;
1999                 press = 1;
2000                 break;
2001               case WM_MBUTTONDOWN:
2002                 button = MBT_MIDDLE;
2003                 press = 1;
2004                 break;
2005               case WM_RBUTTONDOWN:
2006                 button = MBT_RIGHT;
2007                 press = 1;
2008                 break;
2009               case WM_LBUTTONUP:
2010                 button = MBT_LEFT;
2011                 press = 0;
2012                 break;
2013               case WM_MBUTTONUP:
2014                 button = MBT_MIDDLE;
2015                 press = 0;
2016                 break;
2017               case WM_RBUTTONUP:
2018                 button = MBT_RIGHT;
2019                 press = 0;
2020                 break;
2021               default:
2022                 button = press = 0;    /* shouldn't happen */
2023             }
2024             show_mouseptr(1);
2025             /*
2026              * Special case: in full-screen mode, if the left
2027              * button is clicked in the very top left corner of the
2028              * window, we put up the System menu instead of doing
2029              * selection.
2030              */
2031             if (is_full_screen() && press && button == MBT_LEFT &&
2032                 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2033                 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2034                 return 0;
2035             }
2036             if (press) {
2037                 click(button,
2038                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2039                       wParam & MK_SHIFT, wParam & MK_CONTROL,
2040                       is_alt_pressed());
2041                 SetCapture(hwnd);
2042             } else {
2043                 term_mouse(term, button, MA_RELEASE,
2044                            TO_CHR_X(X_POS(lParam)),
2045                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2046                            wParam & MK_CONTROL, is_alt_pressed());
2047                 ReleaseCapture();
2048             }
2049         }
2050         return 0;
2051       case WM_MOUSEMOVE:
2052         show_mouseptr(1);
2053         /*
2054          * Add the mouse position and message time to the random
2055          * number noise.
2056          */
2057         noise_ultralight(lParam);
2058
2059         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2060             GetCapture() == hwnd) {
2061             Mouse_Button b;
2062             if (wParam & MK_LBUTTON)
2063                 b = MBT_LEFT;
2064             else if (wParam & MK_MBUTTON)
2065                 b = MBT_MIDDLE;
2066             else
2067                 b = MBT_RIGHT;
2068             term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
2069                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2070                        wParam & MK_CONTROL, is_alt_pressed());
2071         }
2072         return 0;
2073       case WM_NCMOUSEMOVE:
2074         show_mouseptr(1);
2075         noise_ultralight(lParam);
2076         return 0;
2077       case WM_IGNORE_CLIP:
2078         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
2079         break;
2080       case WM_DESTROYCLIPBOARD:
2081         if (!ignore_clip)
2082             term_deselect(term);
2083         ignore_clip = FALSE;
2084         return 0;
2085       case WM_PAINT:
2086         {
2087             PAINTSTRUCT p;
2088             HideCaret(hwnd);
2089             hdc = BeginPaint(hwnd, &p);
2090             if (pal) {
2091                 SelectPalette(hdc, pal, TRUE);
2092                 RealizePalette(hdc);
2093             }
2094             term_paint(term, hdc, 
2095                        (p.rcPaint.left-offset_width)/font_width,
2096                        (p.rcPaint.top-offset_height)/font_height,
2097                        (p.rcPaint.right-offset_width-1)/font_width,
2098                        (p.rcPaint.bottom-offset_height-1)/font_height,
2099                        is_alt_pressed());
2100
2101             if (p.fErase ||
2102                 p.rcPaint.left  < offset_width  ||
2103                 p.rcPaint.top   < offset_height ||
2104                 p.rcPaint.right >= offset_width + font_width*term->cols ||
2105                 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2106             {
2107                 HBRUSH fillcolour, oldbrush;
2108                 HPEN   edge, oldpen;
2109                 fillcolour = CreateSolidBrush (
2110                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2111                 oldbrush = SelectObject(hdc, fillcolour);
2112                 edge = CreatePen(PS_SOLID, 0, 
2113                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2114                 oldpen = SelectObject(hdc, edge);
2115
2116                 /*
2117                  * Jordan Russell reports that this apparently
2118                  * ineffectual IntersectClipRect() call masks a
2119                  * Windows NT/2K bug causing strange display
2120                  * problems when the PuTTY window is taller than
2121                  * the primary monitor. It seems harmless enough...
2122                  */
2123                 IntersectClipRect(hdc,
2124                         p.rcPaint.left, p.rcPaint.top,
2125                         p.rcPaint.right, p.rcPaint.bottom);
2126
2127                 ExcludeClipRect(hdc, 
2128                         offset_width, offset_height,
2129                         offset_width+font_width*term->cols,
2130                         offset_height+font_height*term->rows);
2131
2132                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
2133                           p.rcPaint.right, p.rcPaint.bottom);
2134
2135                 // SelectClipRgn(hdc, NULL);
2136
2137                 SelectObject(hdc, oldbrush);
2138                 DeleteObject(fillcolour);
2139                 SelectObject(hdc, oldpen);
2140                 DeleteObject(edge);
2141             }
2142             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2143             SelectObject(hdc, GetStockObject(WHITE_PEN));
2144             EndPaint(hwnd, &p);
2145             ShowCaret(hwnd);
2146         }
2147         return 0;
2148       case WM_NETEVENT:
2149         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2150          * but the only one that's likely to try to overload us is FD_READ.
2151          * This means buffering just one is fine.
2152          */
2153         if (pending_netevent)
2154             enact_pending_netevent();
2155
2156         pending_netevent = TRUE;
2157         pend_netevent_wParam = wParam;
2158         pend_netevent_lParam = lParam;
2159         if (WSAGETSELECTEVENT(lParam) != FD_READ)
2160             enact_pending_netevent();
2161
2162         time(&last_movement);
2163         return 0;
2164       case WM_SETFOCUS:
2165         term->has_focus = TRUE;
2166         CreateCaret(hwnd, caretbm, font_width, font_height);
2167         ShowCaret(hwnd);
2168         flash_window(0);               /* stop */
2169         compose_state = 0;
2170         term_out(term);
2171         term_update(term);
2172         break;
2173       case WM_KILLFOCUS:
2174         show_mouseptr(1);
2175         term->has_focus = FALSE;
2176         DestroyCaret();
2177         caret_x = caret_y = -1;        /* ensure caret is replaced next time */
2178         term_out(term);
2179         term_update(term);
2180         break;
2181       case WM_ENTERSIZEMOVE:
2182 #ifdef RDB_DEBUG_PATCH
2183         debug((27, "WM_ENTERSIZEMOVE"));
2184 #endif
2185         EnableSizeTip(1);
2186         resizing = TRUE;
2187         need_backend_resize = FALSE;
2188         break;
2189       case WM_EXITSIZEMOVE:
2190         EnableSizeTip(0);
2191         resizing = FALSE;
2192 #ifdef RDB_DEBUG_PATCH
2193         debug((27, "WM_EXITSIZEMOVE"));
2194 #endif
2195         if (need_backend_resize) {
2196             term_size(term, cfg.height, cfg.width, cfg.savelines);
2197             InvalidateRect(hwnd, NULL, TRUE);
2198         }
2199         break;
2200       case WM_SIZING:
2201         /*
2202          * This does two jobs:
2203          * 1) Keep the sizetip uptodate
2204          * 2) Make sure the window size is _stepped_ in units of the font size.
2205          */
2206         if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2207             int width, height, w, h, ew, eh;
2208             LPRECT r = (LPRECT) lParam;
2209
2210             if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2211                     (cfg.height != term->rows || cfg.width != term->cols )) {
2212                 /* 
2213                  * Great! It seems that both the terminal size and the
2214                  * font size have been changed and the user is now dragging.
2215                  * 
2216                  * It will now be difficult to get back to the configured
2217                  * font size!
2218                  *
2219                  * This would be easier but it seems to be too confusing.
2220
2221                 term_size(term, cfg.height, cfg.width, cfg.savelines);
2222                 reset_window(2);
2223                  */
2224                 cfg.height=term->rows; cfg.width=term->cols;
2225
2226                 InvalidateRect(hwnd, NULL, TRUE);
2227                 need_backend_resize = TRUE;
2228             }
2229
2230             width = r->right - r->left - extra_width;
2231             height = r->bottom - r->top - extra_height;
2232             w = (width + font_width / 2) / font_width;
2233             if (w < 1)
2234                 w = 1;
2235             h = (height + font_height / 2) / font_height;
2236             if (h < 1)
2237                 h = 1;
2238             UpdateSizeTip(hwnd, w, h);
2239             ew = width - w * font_width;
2240             eh = height - h * font_height;
2241             if (ew != 0) {
2242                 if (wParam == WMSZ_LEFT ||
2243                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2244                     r->left += ew;
2245                 else
2246                     r->right -= ew;
2247             }
2248             if (eh != 0) {
2249                 if (wParam == WMSZ_TOP ||
2250                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2251                     r->top += eh;
2252                 else
2253                     r->bottom -= eh;
2254             }
2255             if (ew || eh)
2256                 return 1;
2257             else
2258                 return 0;
2259         } else {
2260             int width, height, w, h, rv = 0;
2261             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2262             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2263             LPRECT r = (LPRECT) lParam;
2264
2265             width = r->right - r->left - ex_width;
2266             height = r->bottom - r->top - ex_height;
2267
2268             w = (width + term->cols/2)/term->cols;
2269             h = (height + term->rows/2)/term->rows;
2270             if ( r->right != r->left + w*term->cols + ex_width)
2271                 rv = 1;
2272
2273             if (wParam == WMSZ_LEFT ||
2274                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2275                 r->left = r->right - w*term->cols - ex_width;
2276             else
2277                 r->right = r->left + w*term->cols + ex_width;
2278
2279             if (r->bottom != r->top + h*term->rows + ex_height)
2280                 rv = 1;
2281
2282             if (wParam == WMSZ_TOP ||
2283                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2284                 r->top = r->bottom - h*term->rows - ex_height;
2285             else
2286                 r->bottom = r->top + h*term->rows + ex_height;
2287
2288             return rv;
2289         }
2290         /* break;  (never reached) */
2291       case WM_FULLSCR_ON_MAX:
2292         fullscr_on_max = TRUE;
2293         break;
2294       case WM_MOVE:
2295         sys_cursor_update();
2296         break;
2297       case WM_SIZE:
2298 #ifdef RDB_DEBUG_PATCH
2299         debug((27, "WM_SIZE %s (%d,%d)",
2300                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2301                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2302                 (wParam == SIZE_RESTORED && resizing) ? "to":
2303                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2304                 "...",
2305             LOWORD(lParam), HIWORD(lParam)));
2306 #endif
2307         if (wParam == SIZE_MINIMIZED)
2308             SetWindowText(hwnd,
2309                           cfg.win_name_always ? window_name : icon_name);
2310         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2311             SetWindowText(hwnd, window_name);
2312         if (wParam == SIZE_RESTORED)
2313             clear_full_screen();
2314         if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2315             fullscr_on_max = FALSE;
2316             make_full_screen();
2317         }
2318
2319         if (cfg.resize_action == RESIZE_DISABLED) {
2320             /* A resize, well it better be a minimize. */
2321             reset_window(-1);
2322         } else {
2323
2324             int width, height, w, h;
2325
2326             width = LOWORD(lParam);
2327             height = HIWORD(lParam);
2328
2329             if (!resizing) {
2330                 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2331                     was_zoomed = 1;
2332                     prev_rows = term->rows;
2333                     prev_cols = term->cols;
2334                     if (cfg.resize_action == RESIZE_TERM) {
2335                         w = width / font_width;
2336                         if (w < 1) w = 1;
2337                         h = height / font_height;
2338                         if (h < 1) h = 1;
2339
2340                         term_size(term, h, w, cfg.savelines);
2341                     }
2342                     reset_window(0);
2343                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2344                     was_zoomed = 0;
2345                     if (cfg.resize_action == RESIZE_TERM)
2346                         term_size(term, prev_rows, prev_cols, cfg.savelines);
2347                     if (cfg.resize_action != RESIZE_FONT)
2348                         reset_window(2);
2349                     else
2350                         reset_window(0);
2351                 }
2352                 /* This is an unexpected resize, these will normally happen
2353                  * if the window is too large. Probably either the user
2354                  * selected a huge font or the screen size has changed.
2355                  *
2356                  * This is also called with minimize.
2357                  */
2358                 else reset_window(-1);
2359             }
2360
2361             /*
2362              * Don't call back->size in mid-resize. (To prevent
2363              * massive numbers of resize events getting sent
2364              * down the connection during an NT opaque drag.)
2365              */
2366             if (resizing) {
2367                 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2368                     need_backend_resize = TRUE;
2369                     w = (width-cfg.window_border*2) / font_width;
2370                     if (w < 1) w = 1;
2371                     h = (height-cfg.window_border*2) / font_height;
2372                     if (h < 1) h = 1;
2373
2374                     cfg.height = h;
2375                     cfg.width = w;
2376                 } else 
2377                     reset_window(0);
2378             }
2379         }
2380         sys_cursor_update();
2381         return 0;
2382       case WM_VSCROLL:
2383         switch (LOWORD(wParam)) {
2384           case SB_BOTTOM:
2385             term_scroll(term, -1, 0);
2386             break;
2387           case SB_TOP:
2388             term_scroll(term, +1, 0);
2389             break;
2390           case SB_LINEDOWN:
2391             term_scroll(term, 0, +1);
2392             break;
2393           case SB_LINEUP:
2394             term_scroll(term, 0, -1);
2395             break;
2396           case SB_PAGEDOWN:
2397             term_scroll(term, 0, +term->rows / 2);
2398             break;
2399           case SB_PAGEUP:
2400             term_scroll(term, 0, -term->rows / 2);
2401             break;
2402           case SB_THUMBPOSITION:
2403           case SB_THUMBTRACK:
2404             term_scroll(term, 1, HIWORD(wParam));
2405             break;
2406         }
2407         break;
2408       case WM_PALETTECHANGED:
2409         if ((HWND) wParam != hwnd && pal != NULL) {
2410             HDC hdc = get_ctx(NULL);
2411             if (hdc) {
2412                 if (RealizePalette(hdc) > 0)
2413                     UpdateColors(hdc);
2414                 free_ctx(hdc);
2415             }
2416         }
2417         break;
2418       case WM_QUERYNEWPALETTE:
2419         if (pal != NULL) {
2420             HDC hdc = get_ctx(NULL);
2421             if (hdc) {
2422                 if (RealizePalette(hdc) > 0)
2423                     UpdateColors(hdc);
2424                 free_ctx(hdc);
2425                 return TRUE;
2426             }
2427         }
2428         return FALSE;
2429       case WM_KEYDOWN:
2430       case WM_SYSKEYDOWN:
2431       case WM_KEYUP:
2432       case WM_SYSKEYUP:
2433         /*
2434          * Add the scan code and keypress timing to the random
2435          * number noise.
2436          */
2437         noise_ultralight(lParam);
2438
2439         /*
2440          * We don't do TranslateMessage since it disassociates the
2441          * resulting CHAR message from the KEYDOWN that sparked it,
2442          * which we occasionally don't want. Instead, we process
2443          * KEYDOWN, and call the Win32 translator functions so that
2444          * we get the translations under _our_ control.
2445          */
2446         {
2447             unsigned char buf[20];
2448             int len;
2449
2450             if (wParam == VK_PROCESSKEY) {
2451                 MSG m;
2452                 m.hwnd = hwnd;
2453                 m.message = WM_KEYDOWN;
2454                 m.wParam = wParam;
2455                 m.lParam = lParam & 0xdfff;
2456                 TranslateMessage(&m);
2457             } else {
2458                 len = TranslateKey(message, wParam, lParam, buf);
2459                 if (len == -1)
2460                     return DefWindowProc(hwnd, message, wParam, lParam);
2461
2462                 if (len != 0) {
2463                     /*
2464                      * Interrupt an ongoing paste. I'm not sure
2465                      * this is sensible, but for the moment it's
2466                      * preferable to having to faff about buffering
2467                      * things.
2468                      */
2469                     term_nopaste(term);
2470
2471                     /*
2472                      * We need not bother about stdin backlogs
2473                      * here, because in GUI PuTTY we can't do
2474                      * anything about it anyway; there's no means
2475                      * of asking Windows to hold off on KEYDOWN
2476                      * messages. We _have_ to buffer everything
2477                      * we're sent.
2478                      */
2479                     term_seen_key_event(term);
2480                     ldisc_send(ldisc, buf, len, 1);
2481                     show_mouseptr(0);
2482                 }
2483             }
2484         }
2485         net_pending_errors();
2486         return 0;
2487       case WM_INPUTLANGCHANGE:
2488         /* wParam == Font number */
2489         /* lParam == Locale */
2490         set_input_locale((HKL)lParam);
2491         sys_cursor_update();
2492         break;
2493       case WM_IME_NOTIFY:
2494         if(wParam == IMN_SETOPENSTATUS) {
2495             HIMC hImc = ImmGetContext(hwnd);
2496             ImmSetCompositionFont(hImc, &lfont);
2497             ImmReleaseContext(hwnd, hImc);
2498             return 0;
2499         }
2500         break;
2501       case WM_IME_COMPOSITION:
2502         {
2503             HIMC hIMC;
2504             int n;
2505             char *buff;
2506
2507             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2508                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2509
2510             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2511                 break; /* fall back to DefWindowProc */
2512
2513             hIMC = ImmGetContext(hwnd);
2514             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2515
2516             if (n > 0) {
2517                 int i;
2518                 buff = (char*) smalloc(n);
2519                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2520                 /*
2521                  * Jaeyoun Chung reports that Korean character
2522                  * input doesn't work correctly if we do a single
2523                  * luni_send() covering the whole of buff. So
2524                  * instead we luni_send the characters one by one.
2525                  */
2526                 term_seen_key_event(term);
2527                 for (i = 0; i < n; i += 2) {
2528                     luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2529                 }
2530                 free(buff);
2531             }
2532             ImmReleaseContext(hwnd, hIMC);
2533             return 1;
2534         }
2535
2536       case WM_IME_CHAR:
2537         if (wParam & 0xFF00) {
2538             unsigned char buf[2];
2539
2540             buf[1] = wParam;
2541             buf[0] = wParam >> 8;
2542             term_seen_key_event(term);
2543             lpage_send(ldisc, kbd_codepage, buf, 2, 1);
2544         } else {
2545             char c = (unsigned char) wParam;
2546             term_seen_key_event(term);
2547             lpage_send(ldisc, kbd_codepage, &c, 1, 1);
2548         }
2549         return (0);
2550       case WM_CHAR:
2551       case WM_SYSCHAR:
2552         /*
2553          * Nevertheless, we are prepared to deal with WM_CHAR
2554          * messages, should they crop up. So if someone wants to
2555          * post the things to us as part of a macro manoeuvre,
2556          * we're ready to cope.
2557          */
2558         {
2559             char c = (unsigned char)wParam;
2560             term_seen_key_event(term);
2561             lpage_send(ldisc, CP_ACP, &c, 1, 1);
2562         }
2563         return 0;
2564       case WM_SETCURSOR:
2565         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2566             SetCursor(LoadCursor(NULL, IDC_ARROW));
2567             return TRUE;
2568         }
2569       default:
2570         if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
2571             int shift_pressed=0, control_pressed=0;
2572
2573             if (message == WM_MOUSEWHEEL) {
2574                 wheel_accumulator += (short)HIWORD(wParam);
2575                 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2576                 control_pressed=LOWORD(wParam) & MK_CONTROL;
2577             } else {
2578                 BYTE keys[256];
2579                 wheel_accumulator += (int)wParam;
2580                 if (GetKeyboardState(keys)!=0) {
2581                     shift_pressed=keys[VK_SHIFT]&0x80;
2582                     control_pressed=keys[VK_CONTROL]&0x80;
2583                 }
2584             }
2585
2586             /* process events when the threshold is reached */
2587             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2588                 int b;
2589
2590                 /* reduce amount for next time */
2591                 if (wheel_accumulator > 0) {
2592                     b = MBT_WHEEL_UP;
2593                     wheel_accumulator -= WHEEL_DELTA;
2594                 } else if (wheel_accumulator < 0) {
2595                     b = MBT_WHEEL_DOWN;
2596                     wheel_accumulator += WHEEL_DELTA;
2597                 } else
2598                     break;
2599
2600                 if (send_raw_mouse &&
2601                     !(cfg.mouse_override && shift_pressed)) {
2602                     /* send a mouse-down followed by a mouse up */
2603                     term_mouse(term, b,
2604                                MA_CLICK,
2605                                TO_CHR_X(X_POS(lParam)),
2606                                TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2607                                control_pressed, is_alt_pressed());
2608                     term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2609                                TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2610                                control_pressed, is_alt_pressed());
2611                 } else {
2612                     /* trigger a scroll */
2613                     term_scroll(term, 0,
2614                                 b == MBT_WHEEL_UP ?
2615                                 -term->rows / 2 : term->rows / 2);
2616                 }
2617             }
2618             return 0;
2619         }
2620     }
2621
2622     return DefWindowProc(hwnd, message, wParam, lParam);
2623 }
2624
2625 /*
2626  * Move the system caret. (We maintain one, even though it's
2627  * invisible, for the benefit of blind people: apparently some
2628  * helper software tracks the system caret, so we should arrange to
2629  * have one.)
2630  */
2631 void sys_cursor(void *frontend, int x, int y)
2632 {
2633     int cx, cy;
2634
2635     if (!term->has_focus) return;
2636
2637     /*
2638      * Avoid gratuitously re-updating the cursor position and IMM
2639      * window if there's no actual change required.
2640      */
2641     cx = x * font_width + offset_width;
2642     cy = y * font_height + offset_height;
2643     if (cx == caret_x && cy == caret_y)
2644         return;
2645     caret_x = cx;
2646     caret_y = cy;
2647
2648     sys_cursor_update();
2649 }
2650
2651 static void sys_cursor_update(void)
2652 {
2653     COMPOSITIONFORM cf;
2654     HIMC hIMC;
2655
2656     if (!term->has_focus) return;
2657
2658     if (caret_x < 0 || caret_y < 0)
2659         return;
2660
2661     SetCaretPos(caret_x, caret_y);
2662
2663     /* IMM calls on Win98 and beyond only */
2664     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2665     
2666     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2667             osVersion.dwMinorVersion == 0) return; /* 95 */
2668
2669     /* we should have the IMM functions */
2670     hIMC = ImmGetContext(hwnd);
2671     cf.dwStyle = CFS_POINT;
2672     cf.ptCurrentPos.x = caret_x;
2673     cf.ptCurrentPos.y = caret_y;
2674     ImmSetCompositionWindow(hIMC, &cf);
2675
2676     ImmReleaseContext(hwnd, hIMC);
2677 }
2678
2679 /*
2680  * Draw a line of text in the window, at given character
2681  * coordinates, in given attributes.
2682  *
2683  * We are allowed to fiddle with the contents of `text'.
2684  */
2685 void do_text(Context ctx, int x, int y, char *text, int len,
2686              unsigned long attr, int lattr)
2687 {
2688     COLORREF fg, bg, t;
2689     int nfg, nbg, nfont;
2690     HDC hdc = ctx;
2691     RECT line_box;
2692     int force_manual_underline = 0;
2693     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2694     int char_width = fnt_width;
2695     int text_adjust = 0;
2696     static int *IpDx = 0, IpDxLEN = 0;
2697
2698     if (attr & ATTR_WIDE)
2699         char_width *= 2;
2700
2701     if (len > IpDxLEN || IpDx[0] != char_width) {
2702         int i;
2703         if (len > IpDxLEN) {
2704             sfree(IpDx);
2705             IpDx = smalloc((len + 16) * sizeof(int));
2706             IpDxLEN = (len + 16);
2707         }
2708         for (i = 0; i < IpDxLEN; i++)
2709             IpDx[i] = char_width;
2710     }
2711
2712     /* Only want the left half of double width lines */
2713     if (lattr != LATTR_NORM && x*2 >= term->cols)
2714         return;
2715
2716     x *= fnt_width;
2717     y *= font_height;
2718     x += offset_width;
2719     y += offset_height;
2720
2721     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
2722         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2723         attr ^= ATTR_CUR_XOR;
2724     }
2725
2726     nfont = 0;
2727     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2728         /* Assume a poorman font is borken in other ways too. */
2729         lattr = LATTR_WIDE;
2730     } else
2731         switch (lattr) {
2732           case LATTR_NORM:
2733             break;
2734           case LATTR_WIDE:
2735             nfont |= FONT_WIDE;
2736             break;
2737           default:
2738             nfont |= FONT_WIDE + FONT_HIGH;
2739             break;
2740         }
2741     if (attr & ATTR_NARROW)
2742         nfont |= FONT_NARROW;
2743
2744     /* Special hack for the VT100 linedraw glyphs. */
2745     if ((attr & CSET_MASK) == 0x2300) {
2746         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2747             switch ((unsigned char) (text[0])) {
2748               case 0xBA:
2749                 text_adjust = -2 * font_height / 5;
2750                 break;
2751               case 0xBB:
2752                 text_adjust = -1 * font_height / 5;
2753                 break;
2754               case 0xBC:
2755                 text_adjust = font_height / 5;
2756                 break;
2757               case 0xBD:
2758                 text_adjust = 2 * font_height / 5;
2759                 break;
2760             }
2761             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2762                 text_adjust *= 2;
2763             attr &= ~CSET_MASK;
2764             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2765             attr |= (unitab_xterm['q'] & CSET_MASK);
2766             if (attr & ATTR_UNDER) {
2767                 attr &= ~ATTR_UNDER;
2768                 force_manual_underline = 1;
2769             }
2770         }
2771     }
2772
2773     /* Anything left as an original character set is unprintable. */
2774     if (DIRECT_CHAR(attr)) {
2775         attr &= ~CSET_MASK;
2776         attr |= 0xFF00;
2777         memset(text, 0xFD, len);
2778     }
2779
2780     /* OEM CP */
2781     if ((attr & CSET_MASK) == ATTR_OEMCP)
2782         nfont |= FONT_OEM;
2783
2784     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2785     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2786     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2787         nfont |= FONT_BOLD;
2788     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2789         nfont |= FONT_UNDERLINE;
2790     another_font(nfont);
2791     if (!fonts[nfont]) {
2792         if (nfont & FONT_UNDERLINE)
2793             force_manual_underline = 1;
2794         /* Don't do the same for manual bold, it could be bad news. */
2795
2796         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2797     }
2798     another_font(nfont);
2799     if (!fonts[nfont])
2800         nfont = FONT_NORMAL;
2801     if (attr & ATTR_REVERSE) {
2802         t = nfg;
2803         nfg = nbg;
2804         nbg = t;
2805     }
2806     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2807         nfg++;
2808     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2809         nbg++;
2810     fg = colours[nfg];
2811     bg = colours[nbg];
2812     SelectObject(hdc, fonts[nfont]);
2813     SetTextColor(hdc, fg);
2814     SetBkColor(hdc, bg);
2815     SetBkMode(hdc, OPAQUE);
2816     line_box.left = x;
2817     line_box.top = y;
2818     line_box.right = x + char_width * len;
2819     line_box.bottom = y + font_height;
2820
2821     /* Only want the left half of double width lines */
2822     if (line_box.right > font_width*term->cols+offset_width)
2823         line_box.right = font_width*term->cols+offset_width;
2824
2825     /* We're using a private area for direct to font. (512 chars.) */
2826     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2827         /* Ho Hum, dbcs fonts are a PITA! */
2828         /* To display on W9x I have to convert to UCS */
2829         static wchar_t *uni_buf = 0;
2830         static int uni_len = 0;
2831         int nlen, mptr;
2832         if (len > uni_len) {
2833             sfree(uni_buf);
2834             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2835         }
2836
2837         for(nlen = mptr = 0; mptr<len; mptr++) {
2838             uni_buf[nlen] = 0xFFFD;
2839             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2840                 IpDx[nlen] += char_width;
2841                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2842                                    text+mptr, 2, uni_buf+nlen, 1);
2843                 mptr++;
2844             }
2845             else
2846             {
2847                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2848                                    text+mptr, 1, uni_buf+nlen, 1);
2849             }
2850             nlen++;
2851         }
2852         if (nlen <= 0)
2853             return;                    /* Eeek! */
2854
2855         ExtTextOutW(hdc, x,
2856                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2857                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2858         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2859             SetBkMode(hdc, TRANSPARENT);
2860             ExtTextOutW(hdc, x - 1,
2861                         y - font_height * (lattr ==
2862                                            LATTR_BOT) + text_adjust,
2863                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2864         }
2865
2866         IpDx[0] = -1;
2867     } else if (DIRECT_FONT(attr)) {
2868         ExtTextOut(hdc, x,
2869                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2870                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2871         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2872             SetBkMode(hdc, TRANSPARENT);
2873
2874             /* GRR: This draws the character outside it's box and can leave
2875              * 'droppings' even with the clip box! I suppose I could loop it
2876              * one character at a time ... yuk. 
2877              * 
2878              * Or ... I could do a test print with "W", and use +1 or -1 for this
2879              * shift depending on if the leftmost column is blank...
2880              */
2881             ExtTextOut(hdc, x - 1,
2882                        y - font_height * (lattr ==
2883                                           LATTR_BOT) + text_adjust,
2884                        ETO_CLIPPED, &line_box, text, len, IpDx);
2885         }
2886     } else {
2887         /* And 'normal' unicode characters */
2888         static WCHAR *wbuf = NULL;
2889         static int wlen = 0;
2890         int i;
2891         if (wlen < len) {
2892             sfree(wbuf);
2893             wlen = len;
2894             wbuf = smalloc(wlen * sizeof(WCHAR));
2895         }
2896         for (i = 0; i < len; i++)
2897             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2898
2899         ExtTextOutW(hdc, x,
2900                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2901                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2902
2903         /* And the shadow bold hack. */
2904         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2905             SetBkMode(hdc, TRANSPARENT);
2906             ExtTextOutW(hdc, x - 1,
2907                         y - font_height * (lattr ==
2908                                            LATTR_BOT) + text_adjust,
2909                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2910         }
2911     }
2912     if (lattr != LATTR_TOP && (force_manual_underline ||
2913                                (und_mode == UND_LINE
2914                                 && (attr & ATTR_UNDER)))) {
2915         HPEN oldpen;
2916         int dec = descent;
2917         if (lattr == LATTR_BOT)
2918             dec = dec * 2 - font_height;
2919
2920         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2921         MoveToEx(hdc, x, y + dec, NULL);
2922         LineTo(hdc, x + len * char_width, y + dec);
2923         oldpen = SelectObject(hdc, oldpen);
2924         DeleteObject(oldpen);
2925     }
2926 }
2927
2928 void do_cursor(Context ctx, int x, int y, char *text, int len,
2929                unsigned long attr, int lattr)
2930 {
2931
2932     int fnt_width;
2933     int char_width;
2934     HDC hdc = ctx;
2935     int ctype = cfg.cursor_type;
2936
2937     if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
2938         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2939             do_text(ctx, x, y, text, len, attr, lattr);
2940             return;
2941         }
2942         ctype = 2;
2943         attr |= TATTR_RIGHTCURS;
2944     }
2945
2946     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2947     if (attr & ATTR_WIDE)
2948         char_width *= 2;
2949     x *= fnt_width;
2950     y *= font_height;
2951     x += offset_width;
2952     y += offset_height;
2953
2954     if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
2955         POINT pts[5];
2956         HPEN oldpen;
2957         pts[0].x = pts[1].x = pts[4].x = x;
2958         pts[2].x = pts[3].x = x + char_width - 1;
2959         pts[0].y = pts[3].y = pts[4].y = y;
2960         pts[1].y = pts[2].y = y + font_height - 1;
2961         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2962         Polyline(hdc, pts, 5);
2963         oldpen = SelectObject(hdc, oldpen);
2964         DeleteObject(oldpen);
2965     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2966         int startx, starty, dx, dy, length, i;
2967         if (ctype == 1) {
2968             startx = x;
2969             starty = y + descent;
2970             dx = 1;
2971             dy = 0;
2972             length = char_width;
2973         } else {
2974             int xadjust = 0;
2975             if (attr & TATTR_RIGHTCURS)
2976                 xadjust = char_width - 1;
2977             startx = x + xadjust;
2978             starty = y;
2979             dx = 0;
2980             dy = 1;
2981             length = font_height;
2982         }
2983         if (attr & TATTR_ACTCURS) {
2984             HPEN oldpen;
2985             oldpen =
2986                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2987             MoveToEx(hdc, startx, starty, NULL);
2988             LineTo(hdc, startx + dx * length, starty + dy * length);
2989             oldpen = SelectObject(hdc, oldpen);
2990             DeleteObject(oldpen);
2991         } else {
2992             for (i = 0; i < length; i++) {
2993                 if (i % 2 == 0) {
2994                     SetPixel(hdc, startx, starty, colours[23]);
2995                 }
2996                 startx += dx;
2997                 starty += dy;
2998             }
2999         }
3000     }
3001 }
3002
3003 /* This function gets the actual width of a character in the normal font.
3004  */
3005 int char_width(Context ctx, int uc) {
3006     HDC hdc = ctx;
3007     int ibuf = 0;
3008
3009     /* If the font max is the same as the font ave width then this
3010      * function is a no-op.
3011      */
3012     if (!font_dualwidth) return 1;
3013
3014     switch (uc & CSET_MASK) {
3015       case ATTR_ASCII:
3016         uc = unitab_line[uc & 0xFF];
3017         break;
3018       case ATTR_LINEDRW:
3019         uc = unitab_xterm[uc & 0xFF];
3020         break;
3021       case ATTR_SCOACS:
3022         uc = unitab_scoacs[uc & 0xFF];
3023         break;
3024     }
3025     if (DIRECT_FONT(uc)) {
3026         if (dbcs_screenfont) return 1;
3027
3028         /* Speedup, I know of no font where ascii is the wrong width */
3029         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
3030             return 1;
3031
3032         if ( (uc & CSET_MASK) == ATTR_ACP ) {
3033             SelectObject(hdc, fonts[FONT_NORMAL]);
3034         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3035             another_font(FONT_OEM);
3036             if (!fonts[FONT_OEM]) return 0;
3037
3038             SelectObject(hdc, fonts[FONT_OEM]);
3039         } else
3040             return 0;
3041
3042         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
3043              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3044             return 0;
3045     } else {
3046         /* Speedup, I know of no font where ascii is the wrong width */
3047         if (uc >= ' ' && uc <= '~') return 1;
3048
3049         SelectObject(hdc, fonts[FONT_NORMAL]);
3050         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3051             /* Okay that one worked */ ;
3052         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3053             /* This should work on 9x too, but it's "less accurate" */ ;
3054         else
3055             return 0;
3056     }
3057
3058     ibuf += font_width / 2 -1;
3059     ibuf /= font_width;
3060
3061     return ibuf;
3062 }
3063
3064 /*
3065  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3066  * codes. Returns number of bytes used or zero to drop the message
3067  * or -1 to forward the message to windows.
3068  */
3069 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3070                         unsigned char *output)
3071 {
3072     BYTE keystate[256];
3073     int scan, left_alt = 0, key_down, shift_state;
3074     int r, i, code;
3075     unsigned char *p = output;
3076     static int alt_sum = 0;
3077
3078     HKL kbd_layout = GetKeyboardLayout(0);
3079
3080     static WORD keys[3];
3081     static int compose_char = 0;
3082     static WPARAM compose_key = 0;
3083
3084     r = GetKeyboardState(keystate);
3085     if (!r)
3086         memset(keystate, 0, sizeof(keystate));
3087     else {
3088 #if 0
3089 #define SHOW_TOASCII_RESULT
3090         {                              /* Tell us all about key events */
3091             static BYTE oldstate[256];
3092             static int first = 1;
3093             static int scan;
3094             int ch;
3095             if (first)
3096                 memcpy(oldstate, keystate, sizeof(oldstate));
3097             first = 0;
3098
3099             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3100                 debug(("+"));
3101             } else if ((HIWORD(lParam) & KF_UP)
3102                        && scan == (HIWORD(lParam) & 0xFF)) {
3103                 debug((". U"));
3104             } else {
3105                 debug((".\n"));
3106                 if (wParam >= VK_F1 && wParam <= VK_F20)
3107                     debug(("K_F%d", wParam + 1 - VK_F1));
3108                 else
3109                     switch (wParam) {
3110                       case VK_SHIFT:
3111                         debug(("SHIFT"));
3112                         break;
3113                       case VK_CONTROL:
3114                         debug(("CTRL"));
3115                         break;
3116                       case VK_MENU:
3117                         debug(("ALT"));
3118                         break;
3119                       default:
3120                         debug(("VK_%02x", wParam));
3121                     }
3122                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3123                     debug(("*"));
3124                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3125
3126                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3127                 if (ch >= ' ' && ch <= '~')
3128                     debug((", '%c'", ch));
3129                 else if (ch)
3130                     debug((", $%02x", ch));
3131
3132                 if (keys[0])
3133                     debug((", KB0=%02x", keys[0]));
3134                 if (keys[1])
3135                     debug((", KB1=%02x", keys[1]));
3136                 if (keys[2])
3137                     debug((", KB2=%02x", keys[2]));
3138
3139                 if ((keystate[VK_SHIFT] & 0x80) != 0)
3140                     debug((", S"));
3141                 if ((keystate[VK_CONTROL] & 0x80) != 0)
3142                     debug((", C"));
3143                 if ((HIWORD(lParam) & KF_EXTENDED))
3144                     debug((", E"));
3145                 if ((HIWORD(lParam) & KF_UP))
3146                     debug((", U"));
3147             }
3148
3149             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3150             else if ((HIWORD(lParam) & KF_UP))
3151                 oldstate[wParam & 0xFF] ^= 0x80;
3152             else
3153                 oldstate[wParam & 0xFF] ^= 0x81;
3154
3155             for (ch = 0; ch < 256; ch++)
3156                 if (oldstate[ch] != keystate[ch])
3157                     debug((", M%02x=%02x", ch, keystate[ch]));
3158
3159             memcpy(oldstate, keystate, sizeof(oldstate));
3160         }
3161 #endif
3162
3163         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3164             keystate[VK_RMENU] = keystate[VK_MENU];
3165         }
3166
3167
3168         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3169         if ((cfg.funky_type == 3 ||
3170              (cfg.funky_type <= 1 && term->app_keypad_keys &&
3171               !cfg.no_applic_k))
3172             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3173
3174             wParam = VK_EXECUTE;
3175
3176             /* UnToggle NUMLock */
3177             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3178                 keystate[VK_NUMLOCK] ^= 1;
3179         }
3180
3181         /* And write back the 'adjusted' state */
3182         SetKeyboardState(keystate);
3183     }
3184
3185     /* Disable Auto repeat if required */
3186     if (term->repeat_off &&
3187         (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3188         return 0;
3189
3190     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3191         left_alt = 1;
3192
3193     key_down = ((HIWORD(lParam) & KF_UP) == 0);
3194
3195     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3196     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3197         if (cfg.ctrlaltkeys)
3198             keystate[VK_MENU] = 0;
3199         else {
3200             keystate[VK_RMENU] = 0x80;
3201             left_alt = 0;
3202         }
3203     }
3204
3205     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3206     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3207         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3208
3209     /* Note if AltGr was pressed and if it was used as a compose key */
3210     if (!compose_state) {
3211         compose_key = 0x100;
3212         if (cfg.compose_key) {
3213             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3214                 compose_key = wParam;
3215         }
3216         if (wParam == VK_APPS)
3217             compose_key = wParam;
3218     }
3219
3220     if (wParam == compose_key) {
3221         if (compose_state == 0
3222             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3223                 1;
3224         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3225             compose_state = 2;
3226         else
3227             compose_state = 0;
3228     } else if (compose_state == 1 && wParam != VK_CONTROL)
3229         compose_state = 0;
3230
3231     if (compose_state > 1 && left_alt)
3232         compose_state = 0;
3233
3234     /* Sanitize the number pad if not using a PC NumPad */
3235     if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
3236                      && cfg.funky_type != 2)
3237         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3238         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3239             int nParam = 0;
3240             switch (wParam) {
3241               case VK_INSERT:
3242                 nParam = VK_NUMPAD0;
3243                 break;
3244               case VK_END:
3245                 nParam = VK_NUMPAD1;
3246                 break;
3247               case VK_DOWN:
3248                 nParam = VK_NUMPAD2;
3249                 break;
3250               case VK_NEXT:
3251                 nParam = VK_NUMPAD3;
3252                 break;
3253               case VK_LEFT:
3254                 nParam = VK_NUMPAD4;
3255                 break;
3256               case VK_CLEAR:
3257                 nParam = VK_NUMPAD5;
3258                 break;
3259               case VK_RIGHT:
3260                 nParam = VK_NUMPAD6;
3261                 break;
3262               case VK_HOME:
3263                 nParam = VK_NUMPAD7;
3264                 break;
3265               case VK_UP:
3266                 nParam = VK_NUMPAD8;
3267                 break;
3268               case VK_PRIOR:
3269                 nParam = VK_NUMPAD9;
3270                 break;
3271               case VK_DELETE:
3272                 nParam = VK_DECIMAL;
3273                 break;
3274             }
3275             if (nParam) {
3276                 if (keystate[VK_NUMLOCK] & 1)
3277                     shift_state |= 1;
3278                 wParam = nParam;
3279             }
3280         }
3281     }
3282
3283     /* If a key is pressed and AltGr is not active */
3284     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3285         /* Okay, prepare for most alts then ... */
3286         if (left_alt)
3287             *p++ = '\033';
3288
3289         /* Lets see if it's a pattern we know all about ... */
3290         if (wParam == VK_PRIOR && shift_state == 1) {
3291             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3292             return 0;
3293         }
3294         if (wParam == VK_NEXT && shift_state == 1) {
3295             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 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) {
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) || 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) || 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) || 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 = smalloc(1 + strlen(title));
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 = smalloc(1 + strlen(title));
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);
4076         rtf = smalloc(rtfsize);
4077         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4078                 GetACP(), cfg.font);
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 = srealloc(rtf, rtfsize);
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 = smalloc(i * sizeof(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, NULL, SND_ASYNC | SND_FILENAME)) {
4355             char buf[sizeof(cfg.bell_wavefile) + 80];
4356             sprintf(buf, "Unable to play sound file\n%s\n"
4357                     "Using default sound instead", cfg.bell_wavefile);
4358             MessageBox(hwnd, buf, "PuTTY Sound Error",
4359                        MB_OK | MB_ICONEXCLAMATION);
4360             cfg.beep = BELL_DEFAULT;
4361         }
4362     }
4363     /* Otherwise, either visual bell or disabled; do nothing here */
4364     if (!term->has_focus) {
4365         flash_window(2);               /* start */
4366     }
4367 }
4368
4369 /*
4370  * Minimise or restore the window in response to a server-side
4371  * request.
4372  */
4373 void set_iconic(void *frontend, int iconic)
4374 {
4375     if (IsIconic(hwnd)) {
4376         if (!iconic)
4377             ShowWindow(hwnd, SW_RESTORE);
4378     } else {
4379         if (iconic)
4380             ShowWindow(hwnd, SW_MINIMIZE);
4381     }
4382 }
4383
4384 /*
4385  * Move the window in response to a server-side request.
4386  */
4387 void move_window(void *frontend, int x, int y)
4388 {
4389     if (cfg.resize_action == RESIZE_DISABLED || 
4390         cfg.resize_action == RESIZE_FONT ||
4391         IsZoomed(hwnd))
4392        return;
4393
4394     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4395 }
4396
4397 /*
4398  * Move the window to the top or bottom of the z-order in response
4399  * to a server-side request.
4400  */
4401 void set_zorder(void *frontend, int top)
4402 {
4403     if (cfg.alwaysontop)
4404         return;                        /* ignore */
4405     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4406                  SWP_NOMOVE | SWP_NOSIZE);
4407 }
4408
4409 /*
4410  * Refresh the window in response to a server-side request.
4411  */
4412 void refresh_window(void *frontend)
4413 {
4414     InvalidateRect(hwnd, NULL, TRUE);
4415 }
4416
4417 /*
4418  * Maximise or restore the window in response to a server-side
4419  * request.
4420  */
4421 void set_zoomed(void *frontend, int zoomed)
4422 {
4423     if (IsZoomed(hwnd)) {
4424         if (!zoomed)
4425             ShowWindow(hwnd, SW_RESTORE);
4426     } else {
4427         if (zoomed)
4428             ShowWindow(hwnd, SW_MAXIMIZE);
4429     }
4430 }
4431
4432 /*
4433  * Report whether the window is iconic, for terminal reports.
4434  */
4435 int is_iconic(void *frontend)
4436 {
4437     return IsIconic(hwnd);
4438 }
4439
4440 /*
4441  * Report the window's position, for terminal reports.
4442  */
4443 void get_window_pos(void *frontend, int *x, int *y)
4444 {
4445     RECT r;
4446     GetWindowRect(hwnd, &r);
4447     *x = r.left;
4448     *y = r.top;
4449 }
4450
4451 /*
4452  * Report the window's pixel size, for terminal reports.
4453  */
4454 void get_window_pixels(void *frontend, int *x, int *y)
4455 {
4456     RECT r;
4457     GetWindowRect(hwnd, &r);
4458     *x = r.right - r.left;
4459     *y = r.bottom - r.top;
4460 }
4461
4462 /*
4463  * Return the window or icon title.
4464  */
4465 char *get_window_title(void *frontend, int icon)
4466 {
4467     return icon ? icon_name : window_name;
4468 }
4469
4470 /*
4471  * See if we're in full-screen mode.
4472  */
4473 int is_full_screen()
4474 {
4475     if (!IsZoomed(hwnd))
4476         return FALSE;
4477     if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4478         return FALSE;
4479     return TRUE;
4480 }
4481
4482 /* Get the rect/size of a full screen window using the nearest available
4483  * monitor in multimon systems; default to something sensible if only
4484  * one monitor is present. */
4485 static int get_fullscreen_rect(RECT * ss)
4486 {
4487 #ifdef MONITOR_DEFAULTTONEAREST
4488         HMONITOR mon;
4489         MONITORINFO mi;
4490         mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4491         mi.cbSize = sizeof(mi);
4492         GetMonitorInfo(mon, &mi);
4493
4494         /* structure copy */
4495         *ss = mi.rcMonitor;
4496         return TRUE;
4497 #else
4498 /* could also use code like this:
4499         ss->left = ss->top = 0;
4500         ss->right = GetSystemMetrics(SM_CXSCREEN);
4501         ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4502 */ 
4503         return GetClientRect(GetDesktopWindow(), ss);
4504 #endif
4505 }
4506
4507
4508 /*
4509  * Go full-screen. This should only be called when we are already
4510  * maximised.
4511  */
4512 void make_full_screen()
4513 {
4514     DWORD style;
4515         RECT ss;
4516
4517     assert(IsZoomed(hwnd));
4518
4519         if (is_full_screen())
4520                 return;
4521         
4522     /* Remove the window furniture. */
4523     style = GetWindowLong(hwnd, GWL_STYLE);
4524     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4525     if (cfg.scrollbar_in_fullscreen)
4526         style |= WS_VSCROLL;
4527     else
4528         style &= ~WS_VSCROLL;
4529     SetWindowLong(hwnd, GWL_STYLE, style);
4530
4531     /* Resize ourselves to exactly cover the nearest monitor. */
4532         get_fullscreen_rect(&ss);
4533     SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4534                         ss.right - ss.left,
4535                         ss.bottom - ss.top,
4536                         SWP_FRAMECHANGED);
4537
4538     /* Tick the menu item in the System menu. */
4539     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4540                   MF_CHECKED);
4541 }
4542
4543 /*
4544  * Clear the full-screen attributes.
4545  */
4546 void clear_full_screen()
4547 {
4548     DWORD oldstyle, style;
4549
4550     /* Reinstate the window furniture. */
4551     style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4552     style |= WS_CAPTION | WS_BORDER;
4553     if (cfg.resize_action == RESIZE_DISABLED)
4554         style &= ~WS_THICKFRAME;
4555     else
4556         style |= WS_THICKFRAME;
4557     if (cfg.scrollbar)
4558         style |= WS_VSCROLL;
4559     else
4560         style &= ~WS_VSCROLL;
4561     if (style != oldstyle) {
4562         SetWindowLong(hwnd, GWL_STYLE, style);
4563         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4564                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4565                      SWP_FRAMECHANGED);
4566     }
4567
4568     /* Untick the menu item in the System menu. */
4569     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4570                   MF_UNCHECKED);
4571 }
4572
4573 /*
4574  * Toggle full-screen mode.
4575  */
4576 void flip_full_screen()
4577 {
4578     if (is_full_screen()) {
4579         ShowWindow(hwnd, SW_RESTORE);
4580     } else if (IsZoomed(hwnd)) {
4581         make_full_screen();
4582     } else {
4583         SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4584         ShowWindow(hwnd, SW_MAXIMIZE);
4585     }
4586 }
4587
4588 void frontend_keypress(void *handle)
4589 {
4590     /*
4591      * Keypress termination in non-Close-On-Exit mode is not
4592      * currently supported in PuTTY proper, because the window
4593      * always has a perfectly good Close button anyway. So we do
4594      * nothing here.
4595      */
4596     return;
4597 }