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