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