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