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