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