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