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