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