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