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