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