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