]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - window.c
149665048f213e02ed87e9ef9dba0cc5eb00f960
[PuTTY_svn.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_KEYMENU:
1691             /*
1692              * We get this if the System menu has been activated.
1693              * This might happen from within TranslateKey, in which
1694              * case it really wants to be followed by a `space'
1695              * character to actually _bring the menu up_ rather
1696              * than just sitting there in `ready to appear' state.
1697              */
1698             if( lParam == 0 )
1699                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1700             break;
1701           case IDM_FULLSCREEN:
1702                 flip_full_screen();     
1703                 break;
1704           default:
1705             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1706                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1707             }
1708         }
1709         break;
1710
1711 #define X_POS(l) ((int)(short)LOWORD(l))
1712 #define Y_POS(l) ((int)(short)HIWORD(l))
1713
1714 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1715 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1716 #define WHEEL_DELTA 120
1717       case WM_MOUSEWHEEL:
1718         {
1719             wheel_accumulator += (short) HIWORD(wParam);
1720             wParam = LOWORD(wParam);
1721
1722             /* process events when the threshold is reached */
1723             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1724                 int b;
1725
1726                 /* reduce amount for next time */
1727                 if (wheel_accumulator > 0) {
1728                     b = MBT_WHEEL_UP;
1729                     wheel_accumulator -= WHEEL_DELTA;
1730                 } else if (wheel_accumulator < 0) {
1731                     b = MBT_WHEEL_DOWN;
1732                     wheel_accumulator += WHEEL_DELTA;
1733                 } else
1734                     break;
1735
1736                 if (send_raw_mouse) {
1737                     /* send a mouse-down followed by a mouse up */
1738                     
1739                     term_mouse(b,
1740                                MA_CLICK,
1741                                TO_CHR_X(X_POS(lParam)),
1742                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1743                                wParam & MK_CONTROL, is_alt_pressed());
1744                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1745                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1746                                wParam & MK_CONTROL, is_alt_pressed());
1747                 } else {
1748                     /* trigger a scroll */
1749                     term_scroll(0,
1750                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1751                 }
1752             }
1753             return 0;
1754         }
1755       case WM_LBUTTONDOWN:
1756       case WM_MBUTTONDOWN:
1757       case WM_RBUTTONDOWN:
1758       case WM_LBUTTONUP:
1759       case WM_MBUTTONUP:
1760       case WM_RBUTTONUP:
1761         {
1762             int button, press;
1763
1764             switch (message) {
1765               case WM_LBUTTONDOWN:
1766                 button = MBT_LEFT;
1767                 press = 1;
1768                 break;
1769               case WM_MBUTTONDOWN:
1770                 button = MBT_MIDDLE;
1771                 press = 1;
1772                 break;
1773               case WM_RBUTTONDOWN:
1774                 button = MBT_RIGHT;
1775                 press = 1;
1776                 break;
1777               case WM_LBUTTONUP:
1778                 button = MBT_LEFT;
1779                 press = 0;
1780                 break;
1781               case WM_MBUTTONUP:
1782                 button = MBT_MIDDLE;
1783                 press = 0;
1784                 break;
1785               case WM_RBUTTONUP:
1786                 button = MBT_RIGHT;
1787                 press = 0;
1788                 break;
1789               default:
1790                 button = press = 0;    /* shouldn't happen */
1791             }
1792             show_mouseptr(1);
1793             if (press) {
1794                 click(button,
1795                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1796                       wParam & MK_SHIFT, wParam & MK_CONTROL,
1797                       is_alt_pressed());
1798                 SetCapture(hwnd);
1799             } else {
1800                 term_mouse(button, MA_RELEASE,
1801                            TO_CHR_X(X_POS(lParam)),
1802                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1803                            wParam & MK_CONTROL, is_alt_pressed());
1804                 ReleaseCapture();
1805             }
1806         }
1807         return 0;
1808       case WM_MOUSEMOVE:
1809         show_mouseptr(1);
1810         /*
1811          * Add the mouse position and message time to the random
1812          * number noise.
1813          */
1814         noise_ultralight(lParam);
1815
1816         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1817             Mouse_Button b;
1818             if (wParam & MK_LBUTTON)
1819                 b = MBT_LEFT;
1820             else if (wParam & MK_MBUTTON)
1821                 b = MBT_MIDDLE;
1822             else
1823                 b = MBT_RIGHT;
1824             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1825                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1826                        wParam & MK_CONTROL, is_alt_pressed());
1827         }
1828         return 0;
1829       case WM_NCMOUSEMOVE:
1830         show_mouseptr(1);
1831         noise_ultralight(lParam);
1832         return 0;
1833       case WM_IGNORE_CLIP:
1834         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1835         break;
1836       case WM_DESTROYCLIPBOARD:
1837         if (!ignore_clip)
1838             term_deselect();
1839         ignore_clip = FALSE;
1840         return 0;
1841       case WM_PAINT:
1842         {
1843             PAINTSTRUCT p;
1844             HideCaret(hwnd);
1845             hdc = BeginPaint(hwnd, &p);
1846             if (pal) {
1847                 SelectPalette(hdc, pal, TRUE);
1848                 RealizePalette(hdc);
1849             }
1850             term_paint(hdc, 
1851                        (p.rcPaint.left-offset_width)/font_width,
1852                        (p.rcPaint.top-offset_height)/font_height,
1853                        (p.rcPaint.right-offset_width-1)/font_width,
1854                        (p.rcPaint.bottom-offset_height-1)/font_height);
1855
1856             if (p.fErase ||
1857                 p.rcPaint.left  < offset_width  ||
1858                 p.rcPaint.top   < offset_height ||
1859                 p.rcPaint.right >= offset_width + font_width*cols ||
1860                 p.rcPaint.bottom>= offset_height + font_height*rows)
1861             {
1862                 HBRUSH fillcolour, oldbrush;
1863                 HPEN   edge, oldpen;
1864                 fillcolour = CreateSolidBrush (
1865                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1866                 oldbrush = SelectObject(hdc, fillcolour);
1867                 edge = CreatePen(PS_SOLID, 0, 
1868                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1869                 oldpen = SelectObject(hdc, edge);
1870
1871                 ExcludeClipRect(hdc, 
1872                         offset_width, offset_height,
1873                         offset_width+font_width*cols,
1874                         offset_height+font_height*rows);
1875
1876                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
1877                           p.rcPaint.right, p.rcPaint.bottom);
1878
1879                 // SelectClipRgn(hdc, NULL);
1880
1881                 SelectObject(hdc, oldbrush);
1882                 DeleteObject(fillcolour);
1883                 SelectObject(hdc, oldpen);
1884                 DeleteObject(edge);
1885             }
1886             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1887             SelectObject(hdc, GetStockObject(WHITE_PEN));
1888             EndPaint(hwnd, &p);
1889             ShowCaret(hwnd);
1890         }
1891         return 0;
1892       case WM_NETEVENT:
1893         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1894          * but the only one that's likely to try to overload us is FD_READ.
1895          * This means buffering just one is fine.
1896          */
1897         if (pending_netevent)
1898             enact_pending_netevent();
1899
1900         pending_netevent = TRUE;
1901         pend_netevent_wParam = wParam;
1902         pend_netevent_lParam = lParam;
1903         if (WSAGETSELECTEVENT(lParam) != FD_READ)
1904             enact_pending_netevent();
1905
1906         time(&last_movement);
1907         return 0;
1908       case WM_SETFOCUS:
1909         has_focus = TRUE;
1910         CreateCaret(hwnd, caretbm, font_width, font_height);
1911         ShowCaret(hwnd);
1912         flash_window(0);               /* stop */
1913         compose_state = 0;
1914         term_out();
1915         term_update();
1916         break;
1917       case WM_KILLFOCUS:
1918         show_mouseptr(1);
1919         has_focus = FALSE;
1920         DestroyCaret();
1921         term_out();
1922         term_update();
1923         break;
1924       case WM_ENTERSIZEMOVE:
1925 #ifdef RDB_DEBUG_PATCH
1926         debug((27, "WM_ENTERSIZEMOVE"));
1927 #endif
1928         EnableSizeTip(1);
1929         resizing = TRUE;
1930         need_backend_resize = FALSE;
1931         break;
1932       case WM_EXITSIZEMOVE:
1933         EnableSizeTip(0);
1934         resizing = FALSE;
1935 #ifdef RDB_DEBUG_PATCH
1936         debug((27, "WM_EXITSIZEMOVE"));
1937 #endif
1938         if (need_backend_resize) {
1939             term_size(cfg.height, cfg.width, cfg.savelines);
1940             InvalidateRect(hwnd, NULL, TRUE);
1941         }
1942         break;
1943       case WM_SIZING:
1944         /*
1945          * This does two jobs:
1946          * 1) Keep the sizetip uptodate
1947          * 2) Make sure the window size is _stepped_ in units of the font size.
1948          */
1949         if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1950             int width, height, w, h, ew, eh;
1951             LPRECT r = (LPRECT) lParam;
1952
1953             if ( !need_backend_resize && 
1954                     (cfg.height != rows || cfg.width != cols )) {
1955                 /* 
1956                  * Great! It seems the host has been changing the terminal
1957                  * size, well the user is now grabbing so this is probably
1958                  * the least confusing solution in the long run even though
1959                  * it a is suprise. Unfortunatly the only way to prevent 
1960                  * this seems to be to let the host change the window size 
1961                  * and as that's a user option we're still right back here.
1962                  */
1963                 term_size(cfg.height, cfg.width, cfg.savelines);
1964                 reset_window(2);
1965                 InvalidateRect(hwnd, NULL, TRUE);
1966                 need_backend_resize = TRUE;
1967             }
1968
1969             width = r->right - r->left - extra_width;
1970             height = r->bottom - r->top - extra_height;
1971             w = (width + font_width / 2) / font_width;
1972             if (w < 1)
1973                 w = 1;
1974             h = (height + font_height / 2) / font_height;
1975             if (h < 1)
1976                 h = 1;
1977             UpdateSizeTip(hwnd, w, h);
1978             ew = width - w * font_width;
1979             eh = height - h * font_height;
1980             if (ew != 0) {
1981                 if (wParam == WMSZ_LEFT ||
1982                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1983                     r->left += ew;
1984                 else
1985                     r->right -= ew;
1986             }
1987             if (eh != 0) {
1988                 if (wParam == WMSZ_TOP ||
1989                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1990                     r->top += eh;
1991                 else
1992                     r->bottom -= eh;
1993             }
1994             if (ew || eh)
1995                 return 1;
1996             else
1997                 return 0;
1998         } else {
1999             int width, height, w, h, rv = 0;
2000             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2001             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2002             LPRECT r = (LPRECT) lParam;
2003
2004             width = r->right - r->left - ex_width;
2005             height = r->bottom - r->top - ex_height;
2006
2007             w = (width + cols/2)/cols;
2008             h = (height + rows/2)/rows;
2009             if ( r->right != r->left + w*cols + ex_width) 
2010                 rv = 1;
2011
2012             if (wParam == WMSZ_LEFT ||
2013                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2014                 r->left = r->right - w*cols - ex_width;
2015             else
2016                 r->right = r->left + w*cols + ex_width;
2017
2018             if (r->bottom != r->top + h*rows + ex_height)
2019                 rv = 1;
2020
2021             if (wParam == WMSZ_TOP ||
2022                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2023                 r->top = r->bottom - h*rows - ex_height;
2024             else
2025                 r->bottom = r->top + h*rows + ex_height;
2026
2027             return rv;
2028         }
2029         /* break;  (never reached) */
2030       case WM_SIZE:
2031 #ifdef RDB_DEBUG_PATCH
2032         debug((27, "WM_SIZE %s (%d,%d)",
2033                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2034                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2035                 (wParam == SIZE_RESTORED && resizing) ? "to":
2036                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2037                 "...",
2038             LOWORD(lParam), HIWORD(lParam)));
2039 #endif
2040         if (wParam == SIZE_MINIMIZED) {
2041             SetWindowText(hwnd,
2042                           cfg.win_name_always ? window_name : icon_name);
2043             break;
2044         }
2045         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2046             SetWindowText(hwnd, window_name);
2047
2048         if (cfg.resize_action == RESIZE_DISABLED) {
2049             /* A resize, well it better be a minimize. */
2050             reset_window(-1);
2051         } else {
2052
2053             int width, height, w, h;
2054
2055             width = LOWORD(lParam);
2056             height = HIWORD(lParam);
2057
2058             if (!resizing) {
2059                 if (wParam == SIZE_MAXIMIZED) {
2060                     was_zoomed = 1;
2061                     prev_rows = rows;
2062                     prev_cols = cols;
2063                     if (cfg.resize_action != RESIZE_FONT) {
2064                         w = width / font_width;
2065                         if (w < 1) w = 1;
2066                         h = height / font_height;
2067                         if (h < 1) h = 1;
2068
2069                         term_size(h, w, cfg.savelines);
2070                     }
2071                     reset_window(0);
2072                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2073                     was_zoomed = 0;
2074                     if (cfg.resize_action != RESIZE_FONT)
2075                         term_size(prev_rows, prev_cols, cfg.savelines);
2076                     reset_window(0);
2077                 }
2078                 /* This is an unexpected resize, these will normally happen
2079                  * if the window is too large. Probably either the user
2080                  * selected a huge font or the screen size has changed.
2081                  *
2082                  * This is also called with minimize.
2083                  */
2084                 else reset_window(-1);
2085             }
2086
2087             /*
2088              * Don't call back->size in mid-resize. (To prevent
2089              * massive numbers of resize events getting sent
2090              * down the connection during an NT opaque drag.)
2091              */
2092             if (resizing) {
2093                 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2094                     need_backend_resize = TRUE;
2095                     w = (width-cfg.window_border*2) / font_width;
2096                     if (w < 1) w = 1;
2097                     h = (height-cfg.window_border*2) / font_height;
2098                     if (h < 1) h = 1;
2099
2100                     cfg.height = h;
2101                     cfg.width = w;
2102                 } else 
2103                     reset_window(0);
2104             }
2105         }
2106         return 0;
2107       case WM_VSCROLL:
2108         switch (LOWORD(wParam)) {
2109           case SB_BOTTOM:
2110             term_scroll(-1, 0);
2111             break;
2112           case SB_TOP:
2113             term_scroll(+1, 0);
2114             break;
2115           case SB_LINEDOWN:
2116             term_scroll(0, +1);
2117             break;
2118           case SB_LINEUP:
2119             term_scroll(0, -1);
2120             break;
2121           case SB_PAGEDOWN:
2122             term_scroll(0, +rows / 2);
2123             break;
2124           case SB_PAGEUP:
2125             term_scroll(0, -rows / 2);
2126             break;
2127           case SB_THUMBPOSITION:
2128           case SB_THUMBTRACK:
2129             term_scroll(1, HIWORD(wParam));
2130             break;
2131         }
2132         break;
2133       case WM_PALETTECHANGED:
2134         if ((HWND) wParam != hwnd && pal != NULL) {
2135             HDC hdc = get_ctx();
2136             if (hdc) {
2137                 if (RealizePalette(hdc) > 0)
2138                     UpdateColors(hdc);
2139                 free_ctx(hdc);
2140             }
2141         }
2142         break;
2143       case WM_QUERYNEWPALETTE:
2144         if (pal != NULL) {
2145             HDC hdc = get_ctx();
2146             if (hdc) {
2147                 if (RealizePalette(hdc) > 0)
2148                     UpdateColors(hdc);
2149                 free_ctx(hdc);
2150                 return TRUE;
2151             }
2152         }
2153         return FALSE;
2154       case WM_KEYDOWN:
2155       case WM_SYSKEYDOWN:
2156       case WM_KEYUP:
2157       case WM_SYSKEYUP:
2158         /*
2159          * Add the scan code and keypress timing to the random
2160          * number noise.
2161          */
2162         noise_ultralight(lParam);
2163
2164         /*
2165          * We don't do TranslateMessage since it disassociates the
2166          * resulting CHAR message from the KEYDOWN that sparked it,
2167          * which we occasionally don't want. Instead, we process
2168          * KEYDOWN, and call the Win32 translator functions so that
2169          * we get the translations under _our_ control.
2170          */
2171         {
2172             unsigned char buf[20];
2173             int len;
2174
2175             if (wParam == VK_PROCESSKEY) {
2176                 MSG m;
2177                 m.hwnd = hwnd;
2178                 m.message = WM_KEYDOWN;
2179                 m.wParam = wParam;
2180                 m.lParam = lParam & 0xdfff;
2181                 TranslateMessage(&m);
2182             } else {
2183                 len = TranslateKey(message, wParam, lParam, buf);
2184                 if (len == -1)
2185                     return DefWindowProc(hwnd, message, wParam, lParam);
2186
2187                 if (len != 0) {
2188                     /*
2189                      * Interrupt an ongoing paste. I'm not sure
2190                      * this is sensible, but for the moment it's
2191                      * preferable to having to faff about buffering
2192                      * things.
2193                      */
2194                     term_nopaste();
2195
2196                     /*
2197                      * We need not bother about stdin backlogs
2198                      * here, because in GUI PuTTY we can't do
2199                      * anything about it anyway; there's no means
2200                      * of asking Windows to hold off on KEYDOWN
2201                      * messages. We _have_ to buffer everything
2202                      * we're sent.
2203                      */
2204                     ldisc_send(buf, len, 1);
2205                     show_mouseptr(0);
2206                 }
2207             }
2208         }
2209         net_pending_errors();
2210         return 0;
2211       case WM_INPUTLANGCHANGE:
2212         {
2213             /* wParam == Font number */
2214             /* lParam == Locale */
2215             char lbuf[20];
2216             HKL NewInputLocale = (HKL) lParam;
2217
2218             // lParam == GetKeyboardLayout(0);
2219
2220             GetLocaleInfo(LOWORD(NewInputLocale),
2221                           LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2222
2223             kbd_codepage = atoi(lbuf);
2224         }
2225         break;
2226       case WM_IME_COMPOSITION:
2227         {
2228             HIMC hIMC;
2229             int n;
2230             char *buff;
2231    
2232             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2233                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2234
2235             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2236                 break; /* fall back to DefWindowProc */
2237
2238             hIMC = ImmGetContext(hwnd);
2239             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2240
2241             if (n > 0) {
2242                 buff = (char*) smalloc(n);
2243                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2244                 luni_send((unsigned short *)buff, n / 2, 1);
2245                 free(buff);
2246             }
2247             ImmReleaseContext(hwnd, hIMC);
2248             return 1;
2249         }
2250
2251       case WM_IME_CHAR:
2252         if (wParam & 0xFF00) {
2253             unsigned char buf[2];
2254
2255             buf[1] = wParam;
2256             buf[0] = wParam >> 8;
2257             lpage_send(kbd_codepage, buf, 2, 1);
2258         } else {
2259             char c = (unsigned char) wParam;
2260             lpage_send(kbd_codepage, &c, 1, 1);
2261         }
2262         return (0);
2263       case WM_CHAR:
2264       case WM_SYSCHAR:
2265         /*
2266          * Nevertheless, we are prepared to deal with WM_CHAR
2267          * messages, should they crop up. So if someone wants to
2268          * post the things to us as part of a macro manoeuvre,
2269          * we're ready to cope.
2270          */
2271         {
2272             char c = (unsigned char)wParam;
2273             lpage_send(CP_ACP, &c, 1, 1);
2274         }
2275         return 0;
2276       case WM_SETCURSOR:
2277         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2278             SetCursor(LoadCursor(NULL, IDC_ARROW));
2279             return TRUE;
2280         }
2281     }
2282
2283     return DefWindowProc(hwnd, message, wParam, lParam);
2284 }
2285
2286 /*
2287  * Move the system caret. (We maintain one, even though it's
2288  * invisible, for the benefit of blind people: apparently some
2289  * helper software tracks the system caret, so we should arrange to
2290  * have one.)
2291  */
2292 void sys_cursor(int x, int y)
2293 {
2294     COMPOSITIONFORM cf;
2295     HIMC hIMC;
2296
2297     if (!has_focus) return;
2298     
2299     SetCaretPos(x * font_width + offset_width,
2300                 y * font_height + offset_height);
2301
2302     /* IMM calls on Win98 and beyond only */
2303     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2304     
2305     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2306             osVersion.dwMinorVersion == 0) return; /* 95 */
2307
2308     /* we should have the IMM functions */
2309     hIMC = ImmGetContext(hwnd);
2310     cf.dwStyle = CFS_POINT;
2311     cf.ptCurrentPos.x = x * font_width;
2312     cf.ptCurrentPos.y = y * font_height;
2313     ImmSetCompositionWindow(hIMC, &cf);
2314
2315     ImmReleaseContext(hwnd, hIMC);
2316 }
2317
2318 /*
2319  * Draw a line of text in the window, at given character
2320  * coordinates, in given attributes.
2321  *
2322  * We are allowed to fiddle with the contents of `text'.
2323  */
2324 void do_text(Context ctx, int x, int y, char *text, int len,
2325              unsigned long attr, int lattr)
2326 {
2327     COLORREF fg, bg, t;
2328     int nfg, nbg, nfont;
2329     HDC hdc = ctx;
2330     RECT line_box;
2331     int force_manual_underline = 0;
2332     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2333     int char_width = fnt_width;
2334     int text_adjust = 0;
2335     static int *IpDx = 0, IpDxLEN = 0;
2336
2337     if (attr & ATTR_WIDE)
2338         char_width *= 2;
2339
2340     if (len > IpDxLEN || IpDx[0] != char_width) {
2341         int i;
2342         if (len > IpDxLEN) {
2343             sfree(IpDx);
2344             IpDx = smalloc((len + 16) * sizeof(int));
2345             IpDxLEN = (len + 16);
2346         }
2347         for (i = 0; i < IpDxLEN; i++)
2348             IpDx[i] = char_width;
2349     }
2350
2351     /* Only want the left half of double width lines */
2352     if (lattr != LATTR_NORM && x*2 >= cols)
2353         return;
2354
2355     x *= fnt_width;
2356     y *= font_height;
2357     x += offset_width;
2358     y += offset_height;
2359
2360     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2361         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2362         attr ^= ATTR_CUR_XOR;
2363     }
2364
2365     nfont = 0;
2366     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2367         /* Assume a poorman font is borken in other ways too. */
2368         lattr = LATTR_WIDE;
2369     } else
2370         switch (lattr) {
2371           case LATTR_NORM:
2372             break;
2373           case LATTR_WIDE:
2374             nfont |= FONT_WIDE;
2375             break;
2376           default:
2377             nfont |= FONT_WIDE + FONT_HIGH;
2378             break;
2379         }
2380     if (attr & ATTR_NARROW)
2381         nfont |= FONT_NARROW;
2382
2383     /* Special hack for the VT100 linedraw glyphs. */
2384     if ((attr & CSET_MASK) == 0x2300) {
2385         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2386             switch ((unsigned char) (text[0])) {
2387               case 0xBA:
2388                 text_adjust = -2 * font_height / 5;
2389                 break;
2390               case 0xBB:
2391                 text_adjust = -1 * font_height / 5;
2392                 break;
2393               case 0xBC:
2394                 text_adjust = font_height / 5;
2395                 break;
2396               case 0xBD:
2397                 text_adjust = 2 * font_height / 5;
2398                 break;
2399             }
2400             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2401                 text_adjust *= 2;
2402             attr &= ~CSET_MASK;
2403             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2404             attr |= (unitab_xterm['q'] & CSET_MASK);
2405             if (attr & ATTR_UNDER) {
2406                 attr &= ~ATTR_UNDER;
2407                 force_manual_underline = 1;
2408             }
2409         }
2410     }
2411
2412     /* Anything left as an original character set is unprintable. */
2413     if (DIRECT_CHAR(attr)) {
2414         attr &= ~CSET_MASK;
2415         attr |= 0xFF00;
2416         memset(text, 0xFD, len);
2417     }
2418
2419     /* OEM CP */
2420     if ((attr & CSET_MASK) == ATTR_OEMCP)
2421         nfont |= FONT_OEM;
2422
2423     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2424     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2425     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2426         nfont |= FONT_BOLD;
2427     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2428         nfont |= FONT_UNDERLINE;
2429     another_font(nfont);
2430     if (!fonts[nfont]) {
2431         if (nfont & FONT_UNDERLINE)
2432             force_manual_underline = 1;
2433         /* Don't do the same for manual bold, it could be bad news. */
2434
2435         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2436     }
2437     another_font(nfont);
2438     if (!fonts[nfont])
2439         nfont = FONT_NORMAL;
2440     if (attr & ATTR_REVERSE) {
2441         t = nfg;
2442         nfg = nbg;
2443         nbg = t;
2444     }
2445     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2446         nfg++;
2447     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2448         nbg++;
2449     fg = colours[nfg];
2450     bg = colours[nbg];
2451     SelectObject(hdc, fonts[nfont]);
2452     SetTextColor(hdc, fg);
2453     SetBkColor(hdc, bg);
2454     SetBkMode(hdc, OPAQUE);
2455     line_box.left = x;
2456     line_box.top = y;
2457     line_box.right = x + char_width * len;
2458     line_box.bottom = y + font_height;
2459
2460     /* Only want the left half of double width lines */
2461     if (line_box.right > font_width*cols+offset_width)
2462         line_box.right = font_width*cols+offset_width;
2463
2464     /* We're using a private area for direct to font. (512 chars.) */
2465     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2466         /* Ho Hum, dbcs fonts are a PITA! */
2467         /* To display on W9x I have to convert to UCS */
2468         static wchar_t *uni_buf = 0;
2469         static int uni_len = 0;
2470         int nlen, mptr;
2471         if (len > uni_len) {
2472             sfree(uni_buf);
2473             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2474         }
2475
2476         for(nlen = mptr = 0; mptr<len; mptr++) {
2477             uni_buf[nlen] = 0xFFFD;
2478             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2479                 IpDx[nlen] += char_width;
2480                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2481                                    text+mptr, 2, uni_buf+nlen, 1);
2482                 mptr++;
2483             }
2484             else
2485             {
2486                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2487                                    text+mptr, 1, uni_buf+nlen, 1);
2488             }
2489             nlen++;
2490         }
2491         if (nlen <= 0)
2492             return;                    /* Eeek! */
2493
2494         ExtTextOutW(hdc, x,
2495                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2496                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2497         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2498             SetBkMode(hdc, TRANSPARENT);
2499             ExtTextOutW(hdc, x - 1,
2500                         y - font_height * (lattr ==
2501                                            LATTR_BOT) + text_adjust,
2502                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2503         }
2504
2505         IpDx[0] = -1;
2506     } else if (DIRECT_FONT(attr)) {
2507         ExtTextOut(hdc, x,
2508                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2509                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2510         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2511             SetBkMode(hdc, TRANSPARENT);
2512
2513             /* GRR: This draws the character outside it's box and can leave
2514              * 'droppings' even with the clip box! I suppose I could loop it
2515              * one character at a time ... yuk. 
2516              * 
2517              * Or ... I could do a test print with "W", and use +1 or -1 for this
2518              * shift depending on if the leftmost column is blank...
2519              */
2520             ExtTextOut(hdc, x - 1,
2521                        y - font_height * (lattr ==
2522                                           LATTR_BOT) + text_adjust,
2523                        ETO_CLIPPED, &line_box, text, len, IpDx);
2524         }
2525     } else {
2526         /* And 'normal' unicode characters */
2527         static WCHAR *wbuf = NULL;
2528         static int wlen = 0;
2529         int i;
2530         if (wlen < len) {
2531             sfree(wbuf);
2532             wlen = len;
2533             wbuf = smalloc(wlen * sizeof(WCHAR));
2534         }
2535         for (i = 0; i < len; i++)
2536             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2537
2538         ExtTextOutW(hdc, x,
2539                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2540                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2541
2542         /* And the shadow bold hack. */
2543         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2544             SetBkMode(hdc, TRANSPARENT);
2545             ExtTextOutW(hdc, x - 1,
2546                         y - font_height * (lattr ==
2547                                            LATTR_BOT) + text_adjust,
2548                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2549         }
2550     }
2551     if (lattr != LATTR_TOP && (force_manual_underline ||
2552                                (und_mode == UND_LINE
2553                                 && (attr & ATTR_UNDER)))) {
2554         HPEN oldpen;
2555         int dec = descent;
2556         if (lattr == LATTR_BOT)
2557             dec = dec * 2 - font_height;
2558
2559         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2560         MoveToEx(hdc, x, y + dec, NULL);
2561         LineTo(hdc, x + len * char_width, y + dec);
2562         oldpen = SelectObject(hdc, oldpen);
2563         DeleteObject(oldpen);
2564     }
2565 }
2566
2567 void do_cursor(Context ctx, int x, int y, char *text, int len,
2568                unsigned long attr, int lattr)
2569 {
2570
2571     int fnt_width;
2572     int char_width;
2573     HDC hdc = ctx;
2574     int ctype = cfg.cursor_type;
2575
2576     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2577         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2578             do_text(ctx, x, y, text, len, attr, lattr);
2579             return;
2580         }
2581         ctype = 2;
2582         attr |= TATTR_RIGHTCURS;
2583     }
2584
2585     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2586     if (attr & ATTR_WIDE)
2587         char_width *= 2;
2588     x *= fnt_width;
2589     y *= font_height;
2590     x += offset_width;
2591     y += offset_height;
2592
2593     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2594         POINT pts[5];
2595         HPEN oldpen;
2596         pts[0].x = pts[1].x = pts[4].x = x;
2597         pts[2].x = pts[3].x = x + char_width - 1;
2598         pts[0].y = pts[3].y = pts[4].y = y;
2599         pts[1].y = pts[2].y = y + font_height - 1;
2600         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2601         Polyline(hdc, pts, 5);
2602         oldpen = SelectObject(hdc, oldpen);
2603         DeleteObject(oldpen);
2604     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2605         int startx, starty, dx, dy, length, i;
2606         if (ctype == 1) {
2607             startx = x;
2608             starty = y + descent;
2609             dx = 1;
2610             dy = 0;
2611             length = char_width;
2612         } else {
2613             int xadjust = 0;
2614             if (attr & TATTR_RIGHTCURS)
2615                 xadjust = char_width - 1;
2616             startx = x + xadjust;
2617             starty = y;
2618             dx = 0;
2619             dy = 1;
2620             length = font_height;
2621         }
2622         if (attr & TATTR_ACTCURS) {
2623             HPEN oldpen;
2624             oldpen =
2625                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2626             MoveToEx(hdc, startx, starty, NULL);
2627             LineTo(hdc, startx + dx * length, starty + dy * length);
2628             oldpen = SelectObject(hdc, oldpen);
2629             DeleteObject(oldpen);
2630         } else {
2631             for (i = 0; i < length; i++) {
2632                 if (i % 2 == 0) {
2633                     SetPixel(hdc, startx, starty, colours[23]);
2634                 }
2635                 startx += dx;
2636                 starty += dy;
2637             }
2638         }
2639     }
2640 }
2641
2642 /* This function gets the actual width of a character in the normal font.
2643  */
2644 int CharWidth(Context ctx, int uc) {
2645     HDC hdc = ctx;
2646     int ibuf = 0;
2647
2648     /* If the font max is the same as the font ave width then this
2649      * function is a no-op.
2650      */
2651     if (!font_dualwidth) return 1;
2652
2653     switch (uc & CSET_MASK) {
2654       case ATTR_ASCII:
2655         uc = unitab_line[uc & 0xFF];
2656         break;
2657       case ATTR_LINEDRW:
2658         uc = unitab_xterm[uc & 0xFF];
2659         break;
2660       case ATTR_SCOACS:
2661         uc = unitab_scoacs[uc & 0xFF];
2662         break;
2663     }
2664     if (DIRECT_FONT(uc)) {
2665         if (dbcs_screenfont) return 1;
2666
2667         /* Speedup, I know of no font where ascii is the wrong width */
2668         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2669             return 1;
2670
2671         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2672             SelectObject(hdc, fonts[FONT_NORMAL]);
2673         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2674             another_font(FONT_OEM);
2675             if (!fonts[FONT_OEM]) return 0;
2676
2677             SelectObject(hdc, fonts[FONT_OEM]);
2678         } else
2679             return 0;
2680
2681         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2682              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2683             return 0;
2684     } else {
2685         /* Speedup, I know of no font where ascii is the wrong width */
2686         if (uc >= ' ' && uc <= '~') return 1;
2687
2688         SelectObject(hdc, fonts[FONT_NORMAL]);
2689         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2690             /* Okay that one worked */ ;
2691         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2692             /* This should work on 9x too, but it's "less accurate" */ ;
2693         else
2694             return 0;
2695     }
2696
2697     ibuf += font_width / 2 -1;
2698     ibuf /= font_width;
2699
2700     return ibuf;
2701 }
2702
2703 /*
2704  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2705  * codes. Returns number of bytes used or zero to drop the message
2706  * or -1 to forward the message to windows.
2707  */
2708 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2709                         unsigned char *output)
2710 {
2711     BYTE keystate[256];
2712     int scan, left_alt = 0, key_down, shift_state;
2713     int r, i, code;
2714     unsigned char *p = output;
2715     static int alt_sum = 0;
2716
2717     HKL kbd_layout = GetKeyboardLayout(0);
2718
2719     static WORD keys[3];
2720     static int compose_char = 0;
2721     static WPARAM compose_key = 0;
2722
2723     r = GetKeyboardState(keystate);
2724     if (!r)
2725         memset(keystate, 0, sizeof(keystate));
2726     else {
2727 #if 0
2728 #define SHOW_TOASCII_RESULT
2729         {                              /* Tell us all about key events */
2730             static BYTE oldstate[256];
2731             static int first = 1;
2732             static int scan;
2733             int ch;
2734             if (first)
2735                 memcpy(oldstate, keystate, sizeof(oldstate));
2736             first = 0;
2737
2738             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2739                 debug(("+"));
2740             } else if ((HIWORD(lParam) & KF_UP)
2741                        && scan == (HIWORD(lParam) & 0xFF)) {
2742                 debug((". U"));
2743             } else {
2744                 debug((".\n"));
2745                 if (wParam >= VK_F1 && wParam <= VK_F20)
2746                     debug(("K_F%d", wParam + 1 - VK_F1));
2747                 else
2748                     switch (wParam) {
2749                       case VK_SHIFT:
2750                         debug(("SHIFT"));
2751                         break;
2752                       case VK_CONTROL:
2753                         debug(("CTRL"));
2754                         break;
2755                       case VK_MENU:
2756                         debug(("ALT"));
2757                         break;
2758                       default:
2759                         debug(("VK_%02x", wParam));
2760                     }
2761                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2762                     debug(("*"));
2763                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2764
2765                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2766                 if (ch >= ' ' && ch <= '~')
2767                     debug((", '%c'", ch));
2768                 else if (ch)
2769                     debug((", $%02x", ch));
2770
2771                 if (keys[0])
2772                     debug((", KB0=%02x", keys[0]));
2773                 if (keys[1])
2774                     debug((", KB1=%02x", keys[1]));
2775                 if (keys[2])
2776                     debug((", KB2=%02x", keys[2]));
2777
2778                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2779                     debug((", S"));
2780                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2781                     debug((", C"));
2782                 if ((HIWORD(lParam) & KF_EXTENDED))
2783                     debug((", E"));
2784                 if ((HIWORD(lParam) & KF_UP))
2785                     debug((", U"));
2786             }
2787
2788             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2789             else if ((HIWORD(lParam) & KF_UP))
2790                 oldstate[wParam & 0xFF] ^= 0x80;
2791             else
2792                 oldstate[wParam & 0xFF] ^= 0x81;
2793
2794             for (ch = 0; ch < 256; ch++)
2795                 if (oldstate[ch] != keystate[ch])
2796                     debug((", M%02x=%02x", ch, keystate[ch]));
2797
2798             memcpy(oldstate, keystate, sizeof(oldstate));
2799         }
2800 #endif
2801
2802         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2803             keystate[VK_RMENU] = keystate[VK_MENU];
2804         }
2805
2806
2807         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2808         if ((cfg.funky_type == 3 ||
2809              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2810             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2811
2812             wParam = VK_EXECUTE;
2813
2814             /* UnToggle NUMLock */
2815             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2816                 keystate[VK_NUMLOCK] ^= 1;
2817         }
2818
2819         /* And write back the 'adjusted' state */
2820         SetKeyboardState(keystate);
2821     }
2822
2823     /* Disable Auto repeat if required */
2824     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2825         return 0;
2826
2827     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2828         left_alt = 1;
2829
2830     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2831
2832     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2833     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2834         if (cfg.ctrlaltkeys)
2835             keystate[VK_MENU] = 0;
2836         else {
2837             keystate[VK_RMENU] = 0x80;
2838             left_alt = 0;
2839         }
2840     }
2841
2842     alt_pressed = (left_alt && key_down);
2843
2844     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2845     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2846         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2847
2848     /* Note if AltGr was pressed and if it was used as a compose key */
2849     if (!compose_state) {
2850         compose_key = 0x100;
2851         if (cfg.compose_key) {
2852             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2853                 compose_key = wParam;
2854         }
2855         if (wParam == VK_APPS)
2856             compose_key = wParam;
2857     }
2858
2859     if (wParam == compose_key) {
2860         if (compose_state == 0
2861             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2862                 1;
2863         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2864             compose_state = 2;
2865         else
2866             compose_state = 0;
2867     } else if (compose_state == 1 && wParam != VK_CONTROL)
2868         compose_state = 0;
2869
2870     /* 
2871      * Record that we pressed key so the scroll window can be reset, but
2872      * be careful to avoid Shift-UP/Down
2873      */
2874     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
2875         wParam != VK_MENU && wParam != VK_CONTROL) {
2876         seen_key_event = 1;
2877     }
2878
2879     if (compose_state > 1 && left_alt)
2880         compose_state = 0;
2881
2882     /* Sanitize the number pad if not using a PC NumPad */
2883     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2884                      && cfg.funky_type != 2)
2885         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2886         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2887             int nParam = 0;
2888             switch (wParam) {
2889               case VK_INSERT:
2890                 nParam = VK_NUMPAD0;
2891                 break;
2892               case VK_END:
2893                 nParam = VK_NUMPAD1;
2894                 break;
2895               case VK_DOWN:
2896                 nParam = VK_NUMPAD2;
2897                 break;
2898               case VK_NEXT:
2899                 nParam = VK_NUMPAD3;
2900                 break;
2901               case VK_LEFT:
2902                 nParam = VK_NUMPAD4;
2903                 break;
2904               case VK_CLEAR:
2905                 nParam = VK_NUMPAD5;
2906                 break;
2907               case VK_RIGHT:
2908                 nParam = VK_NUMPAD6;
2909                 break;
2910               case VK_HOME:
2911                 nParam = VK_NUMPAD7;
2912                 break;
2913               case VK_UP:
2914                 nParam = VK_NUMPAD8;
2915                 break;
2916               case VK_PRIOR:
2917                 nParam = VK_NUMPAD9;
2918                 break;
2919               case VK_DELETE:
2920                 nParam = VK_DECIMAL;
2921                 break;
2922             }
2923             if (nParam) {
2924                 if (keystate[VK_NUMLOCK] & 1)
2925                     shift_state |= 1;
2926                 wParam = nParam;
2927             }
2928         }
2929     }
2930
2931     /* If a key is pressed and AltGr is not active */
2932     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2933         /* Okay, prepare for most alts then ... */
2934         if (left_alt)
2935             *p++ = '\033';
2936
2937         /* Lets see if it's a pattern we know all about ... */
2938         if (wParam == VK_PRIOR && shift_state == 1) {
2939             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2940             return 0;
2941         }
2942         if (wParam == VK_NEXT && shift_state == 1) {
2943             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2944             return 0;
2945         }
2946         if (wParam == VK_INSERT && shift_state == 1) {
2947             term_do_paste();
2948             return 0;
2949         }
2950         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2951             return -1;
2952         }
2953         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2954             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2955             return -1;
2956         }
2957         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2958             flip_full_screen();
2959             return -1;
2960         }
2961         /* Control-Numlock for app-keypad mode switch */
2962         if (wParam == VK_PAUSE && shift_state == 2) {
2963             app_keypad_keys ^= 1;
2964             return 0;
2965         }
2966
2967         /* Nethack keypad */
2968         if (cfg.nethack_keypad && !left_alt) {
2969             switch (wParam) {
2970               case VK_NUMPAD1:
2971                 *p++ = shift_state ? 'B' : 'b';
2972                 return p - output;
2973               case VK_NUMPAD2:
2974                 *p++ = shift_state ? 'J' : 'j';
2975                 return p - output;
2976               case VK_NUMPAD3:
2977                 *p++ = shift_state ? 'N' : 'n';
2978                 return p - output;
2979               case VK_NUMPAD4:
2980                 *p++ = shift_state ? 'H' : 'h';
2981                 return p - output;
2982               case VK_NUMPAD5:
2983                 *p++ = shift_state ? '.' : '.';
2984                 return p - output;
2985               case VK_NUMPAD6:
2986                 *p++ = shift_state ? 'L' : 'l';
2987                 return p - output;
2988               case VK_NUMPAD7:
2989                 *p++ = shift_state ? 'Y' : 'y';
2990                 return p - output;
2991               case VK_NUMPAD8:
2992                 *p++ = shift_state ? 'K' : 'k';
2993                 return p - output;
2994               case VK_NUMPAD9:
2995                 *p++ = shift_state ? 'U' : 'u';
2996                 return p - output;
2997             }
2998         }
2999
3000         /* Application Keypad */
3001         if (!left_alt) {
3002             int xkey = 0;
3003
3004             if (cfg.funky_type == 3 ||
3005                 (cfg.funky_type <= 1 &&
3006                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3007                   case VK_EXECUTE:
3008                     xkey = 'P';
3009                     break;
3010                   case VK_DIVIDE:
3011                     xkey = 'Q';
3012                     break;
3013                   case VK_MULTIPLY:
3014                     xkey = 'R';
3015                     break;
3016                   case VK_SUBTRACT:
3017                     xkey = 'S';
3018                     break;
3019                 }
3020             if (app_keypad_keys && !cfg.no_applic_k)
3021                 switch (wParam) {
3022                   case VK_NUMPAD0:
3023                     xkey = 'p';
3024                     break;
3025                   case VK_NUMPAD1:
3026                     xkey = 'q';
3027                     break;
3028                   case VK_NUMPAD2:
3029                     xkey = 'r';
3030                     break;
3031                   case VK_NUMPAD3:
3032                     xkey = 's';
3033                     break;
3034                   case VK_NUMPAD4:
3035                     xkey = 't';
3036                     break;
3037                   case VK_NUMPAD5:
3038                     xkey = 'u';
3039                     break;
3040                   case VK_NUMPAD6:
3041                     xkey = 'v';
3042                     break;
3043                   case VK_NUMPAD7:
3044                     xkey = 'w';
3045                     break;
3046                   case VK_NUMPAD8:
3047                     xkey = 'x';
3048                     break;
3049                   case VK_NUMPAD9:
3050                     xkey = 'y';
3051                     break;
3052
3053                   case VK_DECIMAL:
3054                     xkey = 'n';
3055                     break;
3056                   case VK_ADD:
3057                     if (cfg.funky_type == 2) {
3058                         if (shift_state)
3059                             xkey = 'l';
3060                         else
3061                             xkey = 'k';
3062                     } else if (shift_state)
3063                         xkey = 'm';
3064                     else
3065                         xkey = 'l';
3066                     break;
3067
3068                   case VK_DIVIDE:
3069                     if (cfg.funky_type == 2)
3070                         xkey = 'o';
3071                     break;
3072                   case VK_MULTIPLY:
3073                     if (cfg.funky_type == 2)
3074                         xkey = 'j';
3075                     break;
3076                   case VK_SUBTRACT:
3077                     if (cfg.funky_type == 2)
3078                         xkey = 'm';
3079                     break;
3080
3081                   case VK_RETURN:
3082                     if (HIWORD(lParam) & KF_EXTENDED)
3083                         xkey = 'M';
3084                     break;
3085                 }
3086             if (xkey) {
3087                 if (vt52_mode) {
3088                     if (xkey >= 'P' && xkey <= 'S')
3089                         p += sprintf((char *) p, "\x1B%c", xkey);
3090                     else
3091                         p += sprintf((char *) p, "\x1B?%c", xkey);
3092                 } else
3093                     p += sprintf((char *) p, "\x1BO%c", xkey);
3094                 return p - output;
3095             }
3096         }
3097
3098         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3099             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3100             *p++ = 0;
3101             return -2;
3102         }
3103         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3104             *p++ = 0x1B;
3105             *p++ = '[';
3106             *p++ = 'Z';
3107             return p - output;
3108         }
3109         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3110             *p++ = 0;
3111             return p - output;
3112         }
3113         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3114             *p++ = 160;
3115             return p - output;
3116         }
3117         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3118             *p++ = 3;
3119             *p++ = 0;
3120             return -2;
3121         }
3122         if (wParam == VK_PAUSE) {      /* Break/Pause */
3123             *p++ = 26;
3124             *p++ = 0;
3125             return -2;
3126         }
3127         /* Control-2 to Control-8 are special */
3128         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3129             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3130             return p - output;
3131         }
3132         if (shift_state == 2 && wParam == 0xBD) {
3133             *p++ = 0x1F;
3134             return p - output;
3135         }
3136         if (shift_state == 2 && wParam == 0xDF) {
3137             *p++ = 0x1C;
3138             return p - output;
3139         }
3140         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3141             *p++ = '\r';
3142             *p++ = '\n';
3143             return p - output;
3144         }
3145
3146         /*
3147          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3148          * for integer decimal nn.)
3149          *
3150          * We also deal with the weird ones here. Linux VCs replace F1
3151          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3152          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3153          * respectively.
3154          */
3155         code = 0;
3156         switch (wParam) {
3157           case VK_F1:
3158             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3159             break;
3160           case VK_F2:
3161             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3162             break;
3163           case VK_F3:
3164             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3165             break;
3166           case VK_F4:
3167             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3168             break;
3169           case VK_F5:
3170             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3171             break;
3172           case VK_F6:
3173             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3174             break;
3175           case VK_F7:
3176             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3177             break;
3178           case VK_F8:
3179             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3180             break;
3181           case VK_F9:
3182             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3183             break;
3184           case VK_F10:
3185             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3186             break;
3187           case VK_F11:
3188             code = 23;
3189             break;
3190           case VK_F12:
3191             code = 24;
3192             break;
3193           case VK_F13:
3194             code = 25;
3195             break;
3196           case VK_F14:
3197             code = 26;
3198             break;
3199           case VK_F15:
3200             code = 28;
3201             break;
3202           case VK_F16:
3203             code = 29;
3204             break;
3205           case VK_F17:
3206             code = 31;
3207             break;
3208           case VK_F18:
3209             code = 32;
3210             break;
3211           case VK_F19:
3212             code = 33;
3213             break;
3214           case VK_F20:
3215             code = 34;
3216             break;
3217         }
3218         if ((shift_state&2) == 0) switch (wParam) {
3219           case VK_HOME:
3220             code = 1;
3221             break;
3222           case VK_INSERT:
3223             code = 2;
3224             break;
3225           case VK_DELETE:
3226             code = 3;
3227             break;
3228           case VK_END:
3229             code = 4;
3230             break;
3231           case VK_PRIOR:
3232             code = 5;
3233             break;
3234           case VK_NEXT:
3235             code = 6;
3236             break;
3237         }
3238         /* Reorder edit keys to physical order */
3239         if (cfg.funky_type == 3 && code <= 6)
3240             code = "\0\2\1\4\5\3\6"[code];
3241
3242         if (vt52_mode && code > 0 && code <= 6) {
3243             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3244             return p - output;
3245         }
3246
3247         if (cfg.funky_type == 5 &&     /* SCO function keys */
3248             code >= 11 && code <= 34) {
3249             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3250             int index = 0;
3251             switch (wParam) {
3252               case VK_F1: index = 0; break;
3253               case VK_F2: index = 1; break;
3254               case VK_F3: index = 2; break;
3255               case VK_F4: index = 3; break;
3256               case VK_F5: index = 4; break;
3257               case VK_F6: index = 5; break;
3258               case VK_F7: index = 6; break;
3259               case VK_F8: index = 7; break;
3260               case VK_F9: index = 8; break;
3261               case VK_F10: index = 9; break;
3262               case VK_F11: index = 10; break;
3263               case VK_F12: index = 11; break;
3264             }
3265             if (keystate[VK_SHIFT] & 0x80) index += 12;
3266             if (keystate[VK_CONTROL] & 0x80) index += 24;
3267             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3268             return p - output;
3269         }
3270         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3271             code >= 1 && code <= 6) {
3272             char codes[] = "HL.FIG";
3273             if (code == 3) {
3274                 *p++ = '\x7F';
3275             } else {
3276                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3277             }
3278             return p - output;
3279         }
3280         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3281             int offt = 0;
3282             if (code > 15)
3283                 offt++;
3284             if (code > 21)
3285                 offt++;
3286             if (vt52_mode)
3287                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3288             else
3289                 p +=
3290                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3291             return p - output;
3292         }
3293         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3294             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3295             return p - output;
3296         }
3297         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3298             if (vt52_mode)
3299                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3300             else
3301                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3302             return p - output;
3303         }
3304         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3305             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3306             return p - output;
3307         }
3308         if (code) {
3309             p += sprintf((char *) p, "\x1B[%d~", code);
3310             return p - output;
3311         }
3312
3313         /*
3314          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3315          * some reason seems to send VK_CLEAR to Windows...).
3316          */
3317         {
3318             char xkey = 0;
3319             switch (wParam) {
3320               case VK_UP:
3321                 xkey = 'A';
3322                 break;
3323               case VK_DOWN:
3324                 xkey = 'B';
3325                 break;
3326               case VK_RIGHT:
3327                 xkey = 'C';
3328                 break;
3329               case VK_LEFT:
3330                 xkey = 'D';
3331                 break;
3332               case VK_CLEAR:
3333                 xkey = 'G';
3334                 break;
3335             }
3336             if (xkey) {
3337                 if (vt52_mode)
3338                     p += sprintf((char *) p, "\x1B%c", xkey);
3339                 else {
3340                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3341                     /* VT100 & VT102 manuals both state the app cursor keys
3342                      * only work if the app keypad is on.
3343                      */
3344                     if (!app_keypad_keys)
3345                         app_flg = 0;
3346                     /* Useful mapping of Ctrl-arrows */
3347                     if (shift_state == 2)
3348                         app_flg = !app_flg;
3349
3350                     if (app_flg)
3351                         p += sprintf((char *) p, "\x1BO%c", xkey);
3352                     else
3353                         p += sprintf((char *) p, "\x1B[%c", xkey);
3354                 }
3355                 return p - output;
3356             }
3357         }
3358
3359         /*
3360          * Finally, deal with Return ourselves. (Win95 seems to
3361          * foul it up when Alt is pressed, for some reason.)
3362          */
3363         if (wParam == VK_RETURN) {     /* Return */
3364             *p++ = 0x0D;
3365             *p++ = 0;
3366             return -2;
3367         }
3368
3369         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3370             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3371         else
3372             alt_sum = 0;
3373     }
3374
3375     /* Okay we've done everything interesting; let windows deal with 
3376      * the boring stuff */
3377     {
3378         BOOL capsOn=0;
3379
3380         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3381         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3382             capsOn= !left_alt;
3383             keystate[VK_CAPITAL] = 0;
3384         }
3385
3386         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3387 #ifdef SHOW_TOASCII_RESULT
3388         if (r == 1 && !key_down) {
3389             if (alt_sum) {
3390                 if (in_utf || dbcs_screenfont)
3391                     debug((", (U+%04x)", alt_sum));
3392                 else
3393                     debug((", LCH(%d)", alt_sum));
3394             } else {
3395                 debug((", ACH(%d)", keys[0]));
3396             }
3397         } else if (r > 0) {
3398             int r1;
3399             debug((", ASC("));
3400             for (r1 = 0; r1 < r; r1++) {
3401                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3402             }
3403             debug((")"));
3404         }
3405 #endif
3406         if (r > 0) {
3407             WCHAR keybuf;
3408
3409             /*
3410              * Interrupt an ongoing paste. I'm not sure this is
3411              * sensible, but for the moment it's preferable to
3412              * having to faff about buffering things.
3413              */
3414             term_nopaste();
3415
3416             p = output;
3417             for (i = 0; i < r; i++) {
3418                 unsigned char ch = (unsigned char) keys[i];
3419
3420                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3421                     compose_char = ch;
3422                     compose_state++;
3423                     continue;
3424                 }
3425                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3426                     int nc;
3427                     compose_state = 0;
3428
3429                     if ((nc = check_compose(compose_char, ch)) == -1) {
3430                         MessageBeep(MB_ICONHAND);
3431                         return 0;
3432                     }
3433                     keybuf = nc;
3434                     luni_send(&keybuf, 1, 1);
3435                     continue;
3436                 }
3437
3438                 compose_state = 0;
3439
3440                 if (!key_down) {
3441                     if (alt_sum) {
3442                         if (in_utf || dbcs_screenfont) {
3443                             keybuf = alt_sum;
3444                             luni_send(&keybuf, 1, 1);
3445                         } else {
3446                             ch = (char) alt_sum;
3447                             /*
3448                              * We need not bother about stdin
3449                              * backlogs here, because in GUI PuTTY
3450                              * we can't do anything about it
3451                              * anyway; there's no means of asking
3452                              * Windows to hold off on KEYDOWN
3453                              * messages. We _have_ to buffer
3454                              * everything we're sent.
3455                              */
3456                             ldisc_send(&ch, 1, 1);
3457                         }
3458                         alt_sum = 0;
3459                     } else
3460                         lpage_send(kbd_codepage, &ch, 1, 1);
3461                 } else {
3462                     if(capsOn && ch < 0x80) {
3463                         WCHAR cbuf[2];
3464                         cbuf[0] = 27;
3465                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3466                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3467                     } else {
3468                         char cbuf[2];
3469                         cbuf[0] = '\033';
3470                         cbuf[1] = ch;
3471                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3472                     }
3473                 }
3474                 show_mouseptr(0);
3475             }
3476
3477             /* This is so the ALT-Numpad and dead keys work correctly. */
3478             keys[0] = 0;
3479
3480             return p - output;
3481         }
3482         /* If we're definitly not building up an ALT-54321 then clear it */
3483         if (!left_alt)
3484             keys[0] = 0;
3485         /* If we will be using alt_sum fix the 256s */
3486         else if (keys[0] && (in_utf || dbcs_screenfont))
3487             keys[0] = 10;
3488     }
3489
3490     /*
3491      * ALT alone may or may not want to bring up the System menu.
3492      * If it's not meant to, we return 0 on presses or releases of
3493      * ALT, to show that we've swallowed the keystroke. Otherwise
3494      * we return -1, which means Windows will give the keystroke
3495      * its default handling (i.e. bring up the System menu).
3496      */
3497     if (wParam == VK_MENU && !cfg.alt_only)
3498         return 0;
3499
3500     return -1;
3501 }
3502
3503 void set_title(char *title)
3504 {
3505     sfree(window_name);
3506     window_name = smalloc(1 + strlen(title));
3507     strcpy(window_name, title);
3508     if (cfg.win_name_always || !IsIconic(hwnd))
3509         SetWindowText(hwnd, title);
3510 }
3511
3512 void set_icon(char *title)
3513 {
3514     sfree(icon_name);
3515     icon_name = smalloc(1 + strlen(title));
3516     strcpy(icon_name, title);
3517     if (!cfg.win_name_always && IsIconic(hwnd))
3518         SetWindowText(hwnd, title);
3519 }
3520
3521 void set_sbar(int total, int start, int page)
3522 {
3523     SCROLLINFO si;
3524
3525     if (!cfg.scrollbar)
3526         return;
3527
3528     si.cbSize = sizeof(si);
3529     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3530     si.nMin = 0;
3531     si.nMax = total - 1;
3532     si.nPage = page;
3533     si.nPos = start;
3534     if (hwnd)
3535         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3536 }
3537
3538 Context get_ctx(void)
3539 {
3540     HDC hdc;
3541     if (hwnd) {
3542         hdc = GetDC(hwnd);
3543         if (hdc && pal)
3544             SelectPalette(hdc, pal, FALSE);
3545         return hdc;
3546     } else
3547         return NULL;
3548 }
3549
3550 void free_ctx(Context ctx)
3551 {
3552     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3553     ReleaseDC(hwnd, ctx);
3554 }
3555
3556 static void real_palette_set(int n, int r, int g, int b)
3557 {
3558     if (pal) {
3559         logpal->palPalEntry[n].peRed = r;
3560         logpal->palPalEntry[n].peGreen = g;
3561         logpal->palPalEntry[n].peBlue = b;
3562         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3563         colours[n] = PALETTERGB(r, g, b);
3564         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3565     } else
3566         colours[n] = RGB(r, g, b);
3567 }
3568
3569 void palette_set(int n, int r, int g, int b)
3570 {
3571     static const int first[21] = {
3572         0, 2, 4, 6, 8, 10, 12, 14,
3573         1, 3, 5, 7, 9, 11, 13, 15,
3574         16, 17, 18, 20, 22
3575     };
3576     real_palette_set(first[n], r, g, b);
3577     if (first[n] >= 18)
3578         real_palette_set(first[n] + 1, r, g, b);
3579     if (pal) {
3580         HDC hdc = get_ctx();
3581         UnrealizeObject(pal);
3582         RealizePalette(hdc);
3583         free_ctx(hdc);
3584     }
3585 }
3586
3587 void palette_reset(void)
3588 {
3589     int i;
3590
3591     for (i = 0; i < NCOLOURS; i++) {
3592         if (pal) {
3593             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3594             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3595             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3596             logpal->palPalEntry[i].peFlags = 0;
3597             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3598                                     defpal[i].rgbtGreen,
3599                                     defpal[i].rgbtBlue);
3600         } else
3601             colours[i] = RGB(defpal[i].rgbtRed,
3602                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3603     }
3604
3605     if (pal) {
3606         HDC hdc;
3607         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3608         hdc = get_ctx();
3609         RealizePalette(hdc);
3610         free_ctx(hdc);
3611     }
3612 }
3613
3614 void write_aclip(char *data, int len, int must_deselect)
3615 {
3616     HGLOBAL clipdata;
3617     void *lock;
3618
3619     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3620     if (!clipdata)
3621         return;
3622     lock = GlobalLock(clipdata);
3623     if (!lock)
3624         return;
3625     memcpy(lock, data, len);
3626     ((unsigned char *) lock)[len] = 0;
3627     GlobalUnlock(clipdata);
3628
3629     if (!must_deselect)
3630         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3631
3632     if (OpenClipboard(hwnd)) {
3633         EmptyClipboard();
3634         SetClipboardData(CF_TEXT, clipdata);
3635         CloseClipboard();
3636     } else
3637         GlobalFree(clipdata);
3638
3639     if (!must_deselect)
3640         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3641 }
3642
3643 /*
3644  * Note: unlike write_aclip() this will not append a nul.
3645  */
3646 void write_clip(wchar_t * data, int len, int must_deselect)
3647 {
3648     HGLOBAL clipdata, clipdata2, clipdata3;
3649     int len2;
3650     void *lock, *lock2, *lock3;
3651
3652     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3653
3654     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3655                            len * sizeof(wchar_t));
3656     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3657
3658     if (!clipdata || !clipdata2 || !clipdata3) {
3659         if (clipdata)
3660             GlobalFree(clipdata);
3661         if (clipdata2)
3662             GlobalFree(clipdata2);
3663         if (clipdata3)
3664             GlobalFree(clipdata3);
3665         return;
3666     }
3667     if (!(lock = GlobalLock(clipdata)))
3668         return;
3669     if (!(lock2 = GlobalLock(clipdata2)))
3670         return;
3671
3672     memcpy(lock, data, len * sizeof(wchar_t));
3673     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3674
3675     if (cfg.rtf_paste) {
3676         wchar_t unitab[256];
3677         char *rtf = NULL;
3678         unsigned char *tdata = (unsigned char *)lock2;
3679         wchar_t *udata = (wchar_t *)lock;
3680         int rtflen = 0, uindex = 0, tindex = 0;
3681         int rtfsize = 0;
3682         int multilen, blen, alen, totallen, i;
3683         char before[16], after[4];
3684
3685         get_unitab(CP_ACP, unitab, 0);
3686
3687         rtfsize = 100 + strlen(cfg.font);
3688         rtf = smalloc(rtfsize);
3689         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3690                 GetACP(), cfg.font);
3691         rtflen = strlen(rtf);
3692
3693         /*
3694          * We want to construct a piece of RTF that specifies the
3695          * same Unicode text. To do this we will read back in
3696          * parallel from the Unicode data in `udata' and the
3697          * non-Unicode data in `tdata'. For each character in
3698          * `tdata' which becomes the right thing in `udata' when
3699          * looked up in `unitab', we just copy straight over from
3700          * tdata. For each one that doesn't, we must WCToMB it
3701          * individually and produce a \u escape sequence.
3702          * 
3703          * It would probably be more robust to just bite the bullet
3704          * and WCToMB each individual Unicode character one by one,
3705          * then MBToWC each one back to see if it was an accurate
3706          * translation; but that strikes me as a horrifying number
3707          * of Windows API calls so I want to see if this faster way
3708          * will work. If it screws up badly we can always revert to
3709          * the simple and slow way.
3710          */
3711         while (tindex < len2 && uindex < len &&
3712                tdata[tindex] && udata[uindex]) {
3713             if (tindex + 1 < len2 &&
3714                 tdata[tindex] == '\r' &&
3715                 tdata[tindex+1] == '\n') {
3716                 tindex++;
3717                 uindex++;
3718             }
3719             if (unitab[tdata[tindex]] == udata[uindex]) {
3720                 multilen = 1;
3721                 before[0] = '\0';
3722                 after[0] = '\0';
3723                 blen = alen = 0;
3724             } else {
3725                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3726                                                NULL, 0, NULL, NULL);
3727                 if (multilen != 1) {
3728                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3729                                    udata[uindex]);
3730                     alen = 1; strcpy(after, "}");
3731                 } else {
3732                     blen = sprintf(before, "\\u%d", udata[uindex]);
3733                     alen = 0; after[0] = '\0';
3734                 }
3735             }
3736             assert(tindex + multilen <= len2);
3737             totallen = blen + alen;
3738             for (i = 0; i < multilen; i++) {
3739                 if (tdata[tindex+i] == '\\' ||
3740                     tdata[tindex+i] == '{' ||
3741                     tdata[tindex+i] == '}')
3742                     totallen += 2;
3743                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3744                     totallen += 6;     /* \par\r\n */
3745                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3746                     totallen += 4;
3747                 else
3748                     totallen++;
3749             }
3750
3751             if (rtfsize < rtflen + totallen + 3) {
3752                 rtfsize = rtflen + totallen + 512;
3753                 rtf = srealloc(rtf, rtfsize);
3754             }
3755
3756             strcpy(rtf + rtflen, before); rtflen += blen;
3757             for (i = 0; i < multilen; i++) {
3758                 if (tdata[tindex+i] == '\\' ||
3759                     tdata[tindex+i] == '{' ||
3760                     tdata[tindex+i] == '}') {
3761                     rtf[rtflen++] = '\\';
3762                     rtf[rtflen++] = tdata[tindex+i];
3763                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3764                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3765                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3766                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3767                 } else {
3768                     rtf[rtflen++] = tdata[tindex+i];
3769                 }
3770             }
3771             strcpy(rtf + rtflen, after); rtflen += alen;
3772
3773             tindex += multilen;
3774             uindex++;
3775         }
3776
3777         strcpy(rtf + rtflen, "}");
3778         rtflen += 2;
3779
3780         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3781         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3782             strcpy(lock3, rtf);
3783             GlobalUnlock(clipdata3);
3784         }
3785         sfree(rtf);
3786     } else
3787         clipdata3 = NULL;
3788
3789     GlobalUnlock(clipdata);
3790     GlobalUnlock(clipdata2);
3791
3792     if (!must_deselect)
3793         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3794
3795     if (OpenClipboard(hwnd)) {
3796         EmptyClipboard();
3797         SetClipboardData(CF_UNICODETEXT, clipdata);
3798         SetClipboardData(CF_TEXT, clipdata2);
3799         if (clipdata3)
3800             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3801         CloseClipboard();
3802     } else {
3803         GlobalFree(clipdata);
3804         GlobalFree(clipdata2);
3805     }
3806
3807     if (!must_deselect)
3808         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3809 }
3810
3811 void get_clip(wchar_t ** p, int *len)
3812 {
3813     static HGLOBAL clipdata = NULL;
3814     static wchar_t *converted = 0;
3815     wchar_t *p2;
3816
3817     if (converted) {
3818         sfree(converted);
3819         converted = 0;
3820     }
3821     if (!p) {
3822         if (clipdata)
3823             GlobalUnlock(clipdata);
3824         clipdata = NULL;
3825         return;
3826     } else if (OpenClipboard(NULL)) {
3827         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3828             CloseClipboard();
3829             *p = GlobalLock(clipdata);
3830             if (*p) {
3831                 for (p2 = *p; *p2; p2++);
3832                 *len = p2 - *p;
3833                 return;
3834             }
3835         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3836             char *s;
3837             int i;
3838             CloseClipboard();
3839             s = GlobalLock(clipdata);
3840             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3841             *p = converted = smalloc(i * sizeof(wchar_t));
3842             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3843             *len = i - 1;
3844             return;
3845         } else
3846             CloseClipboard();
3847     }
3848
3849     *p = NULL;
3850     *len = 0;
3851 }
3852
3853 #if 0
3854 /*
3855  * Move `lines' lines from position `from' to position `to' in the
3856  * window.
3857  */
3858 void optimised_move(int to, int from, int lines)
3859 {
3860     RECT r;
3861     int min, max;
3862
3863     min = (to < from ? to : from);
3864     max = to + from - min;
3865
3866     r.left = offset_width;
3867     r.right = offset_width + cols * font_width;
3868     r.top = offset_height + min * font_height;
3869     r.bottom = offset_height + (max + lines) * font_height;
3870     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3871 }
3872 #endif
3873
3874 /*
3875  * Print a message box and perform a fatal exit.
3876  */
3877 void fatalbox(char *fmt, ...)
3878 {
3879     va_list ap;
3880     char stuff[200];
3881
3882     va_start(ap, fmt);
3883     vsprintf(stuff, fmt, ap);
3884     va_end(ap);
3885     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3886     exit(1);
3887 }
3888
3889 /*
3890  * Manage window caption / taskbar flashing, if enabled.
3891  * 0 = stop, 1 = maintain, 2 = start
3892  */
3893 static void flash_window(int mode)
3894 {
3895     static long last_flash = 0;
3896     static int flashing = 0;
3897     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3898         /* stop */
3899         if (flashing) {
3900             FlashWindow(hwnd, FALSE);
3901             flashing = 0;
3902         }
3903
3904     } else if (mode == 2) {
3905         /* start */
3906         if (!flashing) {
3907             last_flash = GetTickCount();
3908             flashing = 1;
3909             FlashWindow(hwnd, TRUE);
3910         }
3911
3912     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3913         /* maintain */
3914         if (flashing) {
3915             long now = GetTickCount();
3916             long fdiff = now - last_flash;
3917             if (fdiff < 0 || fdiff > 450) {
3918                 last_flash = now;
3919                 FlashWindow(hwnd, TRUE);        /* toggle */
3920             }
3921         }
3922     }
3923 }
3924
3925 /*
3926  * Beep.
3927  */
3928 void beep(int mode)
3929 {
3930     if (mode == BELL_DEFAULT) {
3931         /*
3932          * For MessageBeep style bells, we want to be careful of
3933          * timing, because they don't have the nice property of
3934          * PlaySound bells that each one cancels the previous
3935          * active one. So we limit the rate to one per 50ms or so.
3936          */
3937         static long lastbeep = 0;
3938         long beepdiff;
3939
3940         beepdiff = GetTickCount() - lastbeep;
3941         if (beepdiff >= 0 && beepdiff < 50)
3942             return;
3943         MessageBeep(MB_OK);
3944         /*
3945          * The above MessageBeep call takes time, so we record the
3946          * time _after_ it finishes rather than before it starts.
3947          */
3948         lastbeep = GetTickCount();
3949     } else if (mode == BELL_WAVEFILE) {
3950         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3951             char buf[sizeof(cfg.bell_wavefile) + 80];
3952             sprintf(buf, "Unable to play sound file\n%s\n"
3953                     "Using default sound instead", cfg.bell_wavefile);
3954             MessageBox(hwnd, buf, "PuTTY Sound Error",
3955                        MB_OK | MB_ICONEXCLAMATION);
3956             cfg.beep = BELL_DEFAULT;
3957         }
3958     }
3959     /* Otherwise, either visual bell or disabled; do nothing here */
3960     if (!has_focus) {
3961         flash_window(2);               /* start */
3962     }
3963 }
3964
3965 /*
3966  * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3967  * implementation.
3968  * Revised by <wez@thebrainroom.com>
3969  */
3970 static void flip_full_screen(void)
3971 {
3972     WINDOWPLACEMENT wp;
3973     LONG style;
3974
3975     wp.length = sizeof(wp);
3976     GetWindowPlacement(hwnd, &wp);
3977
3978     full_screen = !full_screen;
3979
3980     if (full_screen) {
3981         if (wp.showCmd == SW_SHOWMAXIMIZED) {
3982             /* Ooops it was already 'zoomed' we have to unzoom it before
3983              * everything will work right.
3984              */
3985             wp.showCmd = SW_SHOWNORMAL;
3986             SetWindowPlacement(hwnd, &wp);
3987         }
3988
3989         style = GetWindowLong(hwnd, GWL_STYLE) & ~WS_CAPTION;
3990         style &= ~WS_VSCROLL;
3991         if (cfg.scrollbar_in_fullscreen)
3992             style |= WS_VSCROLL;
3993         SetWindowLong(hwnd, GWL_STYLE, style);
3994
3995         /* This seems to be needed otherwize explorer doesn't notice
3996          * we want to go fullscreen and it's bar is still visible
3997          */
3998         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
3999                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
4000                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4001                      SWP_FRAMECHANGED);
4002
4003         wp.showCmd = SW_SHOWMAXIMIZED;
4004         SetWindowPlacement(hwnd, &wp);
4005     } else {
4006         style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4007         style &= ~WS_VSCROLL;
4008         if (cfg.scrollbar)
4009             style |= WS_VSCROLL;
4010         SetWindowLong(hwnd, GWL_STYLE, style);
4011
4012         /* Don't need to do a SetWindowPos as the resize will force a
4013          * full redraw.
4014          */
4015         wp.showCmd = SW_SHOWNORMAL;
4016         SetWindowPlacement(hwnd, &wp);
4017     }
4018
4019     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4020                   MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4021 }