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