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