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