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