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