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