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