]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Ensure backend netevent handlers are never reentered -
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <commctrl.h>
3 #include <winsock.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <ctype.h>
7
8 #define PUTTY_DO_GLOBALS                       /* actually _define_ globals */
9 #include "putty.h"
10 #include "win_res.h"
11
12 #define IDM_SHOWLOG   0x0010
13 #define IDM_NEWSESS   0x0020
14 #define IDM_DUPSESS   0x0030
15 #define IDM_RECONF    0x0040
16 #define IDM_CLRSB     0x0050
17 #define IDM_RESET     0x0060
18 #define IDM_TEL_AYT   0x0070
19 #define IDM_TEL_BRK   0x0080
20 #define IDM_TEL_SYNCH 0x0090
21 #define IDM_TEL_EC    0x00a0
22 #define IDM_TEL_EL    0x00b0
23 #define IDM_TEL_GA    0x00c0
24 #define IDM_TEL_NOP   0x00d0
25 #define IDM_TEL_ABORT 0x00e0
26 #define IDM_TEL_AO    0x00f0
27 #define IDM_TEL_IP    0x0100
28 #define IDM_TEL_SUSP  0x0110
29 #define IDM_TEL_EOR   0x0120
30 #define IDM_TEL_EOF   0x0130
31 #define IDM_ABOUT     0x0140
32 #define IDM_SAVEDSESS 0x0150
33
34 #define IDM_SAVED_MIN 0x1000
35 #define IDM_SAVED_MAX 0x2000
36
37 #define WM_IGNORE_SIZE (WM_XUSER + 1)
38 #define WM_IGNORE_CLIP (WM_XUSER + 2)
39 #define WM_IGNORE_KEYMENU (WM_XUSER + 3)
40
41 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
42 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
43 static void cfgtopalette(void);
44 static void init_palette(void);
45 static void init_fonts(int);
46
47 static int extra_width, extra_height;
48
49 static int pending_netevent = 0;
50 static WPARAM pend_netevent_wParam = 0;
51 static LPARAM pend_netevent_lParam = 0;
52 static void enact_pending_netevent(void);
53
54 #define FONT_NORMAL 0
55 #define FONT_BOLD 1
56 #define FONT_UNDERLINE 2
57 #define FONT_BOLDUND 3
58 #define FONT_OEM 4
59 #define FONT_OEMBOLD 5
60 #define FONT_OEMBOLDUND 6
61 #define FONT_OEMUND 7
62 static HFONT fonts[8];
63 static enum {
64     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
65 } bold_mode;
66 static enum {
67     UND_LINE, UND_FONT
68 } und_mode;
69 static int descent;
70
71 #define NCOLOURS 24
72 static COLORREF colours[NCOLOURS];
73 static HPALETTE pal;
74 static LPLOGPALETTE logpal;
75 static RGBTRIPLE defpal[NCOLOURS];
76
77 static HWND hwnd;
78
79 static int dbltime, lasttime, lastact;
80 static Mouse_Button lastbtn;
81
82 static char *window_name, *icon_name;
83
84 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
85     static char appname[] = "PuTTY";
86     WORD winsock_ver;
87     WSADATA wsadata;
88     WNDCLASS wndclass;
89     MSG msg;
90     int guess_width, guess_height;
91
92     putty_inst = inst;
93     flags = FLAG_VERBOSE | FLAG_WINDOWED | FLAG_CONNECTION;
94
95     winsock_ver = MAKEWORD(1, 1);
96     if (WSAStartup(winsock_ver, &wsadata)) {
97         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
98                    MB_OK | MB_ICONEXCLAMATION);
99         return 1;
100     }
101     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
102         MessageBox(NULL, "WinSock version is incompatible with 1.1",
103                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
104         WSACleanup();
105         return 1;
106     }
107     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
108
109     InitCommonControls();
110
111     /*
112      * Process the command line.
113      */
114     {
115         char *p;
116
117         default_protocol = DEFAULT_PROTOCOL;
118         default_port = DEFAULT_PORT;
119
120         do_defaults(NULL);
121
122         p = cmdline;
123         while (*p && isspace(*p)) p++;
124
125         /*
126          * Process command line options first. Yes, this can be
127          * done better, and it will be as soon as I have the
128          * energy...
129          */
130         while (*p == '-') {
131             char *q = p + strcspn(p, " \t");
132             p++;
133             if (q == p + 3 &&
134                 tolower(p[0]) == 's' &&
135                 tolower(p[1]) == 's' &&
136                 tolower(p[2]) == 'h') {
137                 default_protocol = cfg.protocol = PROT_SSH;
138                 default_port = cfg.port = 22;
139             } else if (q == p + 3 &&
140                 tolower(p[0]) == 'l' &&
141                 tolower(p[1]) == 'o' &&
142                 tolower(p[2]) == 'g') {
143                 logfile = "putty.log";
144             }
145             p = q + strspn(q, " \t");
146         }
147
148         /*
149          * An initial @ means to activate a saved session.
150          */
151         if (*p == '@') {
152             do_defaults (p+1);
153             if (!*cfg.host && !do_config()) {
154                 WSACleanup();
155                 return 0;
156             }
157         } else if (*p == '&') {
158             /*
159              * An initial & means we've been given a command line
160              * containing the hex value of a HANDLE for a file
161              * mapping object, which we must then extract as a
162              * config.
163              */
164             HANDLE filemap;
165             Config *cp;
166             if (sscanf(p+1, "%p", &filemap) == 1 &&
167                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
168                                     0, 0, sizeof(Config))) != NULL) {
169                 cfg = *cp;
170                 UnmapViewOfFile(cp);
171                 CloseHandle(filemap);
172             } else if (!do_config()) {
173                 WSACleanup();
174                 return 0;
175             }
176         } else if (*p) {
177             char *q = p;
178             /*
179              * If the hostname starts with "telnet:", set the
180              * protocol to Telnet and process the string as a
181              * Telnet URL.
182              */
183             if (!strncmp(q, "telnet:", 7)) {
184                 char c;
185
186                 q += 7;
187                 if (q[0] == '/' && q[1] == '/')
188                     q += 2;
189                 cfg.protocol = PROT_TELNET;
190                 p = q;
191                 while (*p && *p != ':' && *p != '/') p++;
192                 c = *p;
193                 if (*p)
194                     *p++ = '\0';
195                 if (c == ':')
196                     cfg.port = atoi(p);
197                 else
198                     cfg.port = -1;
199                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
200                 cfg.host[sizeof(cfg.host)-1] = '\0';
201             } else {
202                 while (*p && !isspace(*p)) p++;
203                 if (*p)
204                     *p++ = '\0';
205                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
206                 cfg.host[sizeof(cfg.host)-1] = '\0';
207                 while (*p && isspace(*p)) p++;
208                 if (*p)
209                     cfg.port = atoi(p);
210                 else
211                     cfg.port = -1;
212             }
213         } else {
214             if (!do_config()) {
215                 WSACleanup();
216                 return 0;
217             }
218         }
219     }
220
221     /*
222      * Select protocol. This is farmed out into a table in a
223      * separate file to enable an ssh-free variant.
224      */
225     {
226         int i;
227         back = NULL;
228         for (i = 0; backends[i].backend != NULL; i++)
229             if (backends[i].protocol == cfg.protocol) {
230                 back = backends[i].backend;
231                 break;
232             }
233         if (back == NULL) {
234             MessageBox(NULL, "Unsupported protocol number found",
235                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
236             WSACleanup();
237             return 1;
238         }
239     }
240
241     ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
242
243     if (!prev) {
244         wndclass.style         = 0;
245         wndclass.lpfnWndProc   = WndProc;
246         wndclass.cbClsExtra    = 0;
247         wndclass.cbWndExtra    = 0;
248         wndclass.hInstance     = inst;
249         wndclass.hIcon         = LoadIcon (inst,
250                                            MAKEINTRESOURCE(IDI_MAINICON));
251         wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
252         wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
253         wndclass.lpszMenuName  = NULL;
254         wndclass.lpszClassName = appname;
255
256         RegisterClass (&wndclass);
257     }
258
259     hwnd = NULL;
260
261     savelines = cfg.savelines;
262     term_init();
263
264     cfgtopalette();
265
266     /*
267      * Guess some defaults for the window size. This all gets
268      * updated later, so we don't really care too much. However, we
269      * do want the font width/height guesses to correspond to a
270      * large font rather than a small one...
271      */
272     
273     font_width = 10;
274     font_height = 20;
275     extra_width = 25;
276     extra_height = 28;
277     term_size (cfg.height, cfg.width, cfg.savelines);
278     guess_width = extra_width + font_width * cols;
279     guess_height = extra_height + font_height * rows;
280     {
281         RECT r;
282         HWND w = GetDesktopWindow();
283         GetWindowRect (w, &r);
284         if (guess_width > r.right - r.left)
285             guess_width = r.right - r.left;
286         if (guess_height > r.bottom - r.top)
287             guess_height = r.bottom - r.top;
288     }
289
290     {
291        int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
292        if (!cfg.scrollbar) winmode &= ~(WS_VSCROLL);
293        if (cfg.locksize)   winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
294        hwnd = CreateWindow (appname, appname,
295                             winmode,
296                             CW_USEDEFAULT, CW_USEDEFAULT,
297                             guess_width, guess_height,
298                             NULL, NULL, inst, NULL);
299     }
300
301     /*
302      * Initialise the fonts, simultaneously correcting the guesses
303      * for font_{width,height}.
304      */
305     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
306     und_mode = UND_FONT;
307     init_fonts(0);
308
309     /*
310      * Correct the guesses for extra_{width,height}.
311      */
312     {
313         RECT cr, wr;
314         GetWindowRect (hwnd, &wr);
315         GetClientRect (hwnd, &cr);
316         extra_width = wr.right - wr.left - cr.right + cr.left;
317         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
318     }
319
320     /*
321      * Resize the window, now we know what size we _really_ want it
322      * to be.
323      */
324     guess_width = extra_width + font_width * cols;
325     guess_height = extra_height + font_height * rows;
326     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
327     SetWindowPos (hwnd, NULL, 0, 0, guess_width, guess_height,
328                   SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
329
330     /*
331      * Initialise the scroll bar.
332      */
333     {
334         SCROLLINFO si;
335
336         si.cbSize = sizeof(si);
337         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
338         si.nMin = 0;
339         si.nMax = rows-1;
340         si.nPage = rows;
341         si.nPos = 0;
342         SetScrollInfo (hwnd, SB_VERT, &si, FALSE);
343     }
344
345     /*
346      * Start up the telnet connection.
347      */
348     {
349         char *error;
350         char msg[1024];
351         char *realhost;
352
353         error = back->init (hwnd, cfg.host, cfg.port, &realhost);
354         if (error) {
355             sprintf(msg, "Unable to open connection:\n%s", error);
356             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
357             return 0;
358         }
359         window_name = icon_name = NULL;
360         sprintf(msg, "%s - PuTTY", realhost);
361         set_title (msg);
362         set_icon (msg);
363     }
364
365     session_closed = FALSE;
366
367     /*
368      * Set up the input and output buffers.
369      */
370     inbuf_head = 0;
371     outbuf_reap = outbuf_head = 0;
372
373     /*
374      * Prepare the mouse handler.
375      */
376     lastact = MA_NOTHING;
377     lastbtn = MB_NOTHING;
378     dbltime = GetDoubleClickTime();
379
380     /*
381      * Set up the session-control options on the system menu.
382      */
383     {
384         HMENU m = GetSystemMenu (hwnd, FALSE);
385         HMENU p,s;
386         int i;
387
388         AppendMenu (m, MF_SEPARATOR, 0, 0);
389         if (cfg.protocol == PROT_TELNET) {
390             p = CreateMenu();
391             AppendMenu (p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
392             AppendMenu (p, MF_ENABLED, IDM_TEL_BRK, "Break");
393             AppendMenu (p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
394             AppendMenu (p, MF_SEPARATOR, 0, 0);
395             AppendMenu (p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
396             AppendMenu (p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
397             AppendMenu (p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
398             AppendMenu (p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
399             AppendMenu (p, MF_SEPARATOR, 0, 0);
400             AppendMenu (p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
401             AppendMenu (p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
402             AppendMenu (p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
403             AppendMenu (p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
404             AppendMenu (p, MF_SEPARATOR, 0, 0);
405             AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
406             AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
407             AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
408             AppendMenu (m, MF_SEPARATOR, 0, 0);
409         }
410         AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
411         AppendMenu (m, MF_SEPARATOR, 0, 0);
412         AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session");
413         AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
414         s = CreateMenu();
415         get_sesslist(TRUE);
416         for (i = 1 ; i < ((nsessions < 256) ? nsessions : 256) ; i++)
417           AppendMenu (s, MF_ENABLED, IDM_SAVED_MIN + (16 * i) , sessions[i]);
418         AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
419         AppendMenu (m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings");
420         AppendMenu (m, MF_SEPARATOR, 0, 0);
421         AppendMenu (m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
422         AppendMenu (m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
423         AppendMenu (m, MF_SEPARATOR, 0, 0);
424         AppendMenu (m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
425     }
426
427     /*
428      * Finally show the window!
429      */
430     ShowWindow (hwnd, show);
431
432     /*
433      * Set the palette up.
434      */
435     pal = NULL;
436     logpal = NULL;
437     init_palette();
438
439     has_focus = (GetForegroundWindow() == hwnd);
440     UpdateWindow (hwnd);
441
442     {
443         int timer_id = 0, long_timer = 0;
444
445         while (GetMessage (&msg, NULL, 0, 0) == 1) {
446             /* Sometimes DispatchMessage calls routines that use their own
447              * GetMessage loop, setup this timer so we get some control back.
448              *
449              * Also call term_update() from the timer so that if the host
450              * is sending data flat out we still do redraws.
451              */
452             if(timer_id && long_timer) {
453                 KillTimer(hwnd, timer_id);
454                 long_timer = timer_id = 0;
455             }
456             if(!timer_id)
457                 timer_id = SetTimer(hwnd, 1, 20, NULL);
458             DispatchMessage (&msg);
459
460             /* This is too fast, but I'll leave it for now 'cause it shows
461              * how often term_update is called (far too often at times!)
462              */
463             term_blink(0);
464
465             /* Send the paste buffer if there's anything to send */
466             term_paste();
467
468             /* If there's nothing new in the queue then we can do everything
469              * we've delayed, reading the socket, writing, and repainting
470              * the window.
471              */
472             if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
473                 if (pending_netevent) {
474                     enact_pending_netevent();
475
476                     term_blink(1);
477                 }
478             } else continue;
479             if (!PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) {
480                 if (timer_id) {
481                     KillTimer(hwnd, timer_id);
482                     timer_id = 0;
483                 }
484                 if (inbuf_head)
485                     term_out();
486                 term_update();
487                 if (!has_focus)
488                    timer_id = SetTimer(hwnd, 1, 2000, NULL);
489                 else if (cfg.blinktext)
490                    timer_id = SetTimer(hwnd, 1, 250, NULL);
491                 else
492                    timer_id = SetTimer(hwnd, 1, 500, NULL);
493                 long_timer = 1;
494             }
495         }
496     }
497
498     /*
499      * Clean up.
500      */
501     {
502         int i;
503         for (i=0; i<8; i++)
504             if (fonts[i])
505                 DeleteObject(fonts[i]);
506     }
507     sfree(logpal);
508     if (pal)
509         DeleteObject(pal);
510     WSACleanup();
511
512     if (cfg.protocol == PROT_SSH) {
513         random_save_seed();
514 #ifdef MSCRYPTOAPI
515         crypto_wrapup();
516 #endif
517     }
518
519     return msg.wParam;
520 }
521
522 /*
523  * Actually do the job requested by a WM_NETEVENT
524  */
525 static void enact_pending_netevent(void) {
526     int i;
527     static int reentering = 0;
528
529     if (reentering)
530         return;                        /* don't unpend the pending */
531
532     pending_netevent = FALSE;
533
534     reentering = 1;
535     i = back->msg (pend_netevent_wParam, pend_netevent_lParam);
536     reentering = 0;
537
538     if (i < 0) {
539         char buf[1024];
540         switch (WSABASEERR + (-i) % 10000) {
541           case WSAECONNRESET:
542             sprintf(buf, "Connection reset by peer");
543             break;
544           default:
545             sprintf(buf, "Unexpected network error %d", -i);
546             break;
547         }
548         MessageBox(hwnd, buf, "PuTTY Fatal Error",
549                    MB_ICONERROR | MB_OK);
550         PostQuitMessage(1);
551     } else if (i == 0) {
552         if (cfg.close_on_exit)
553             PostQuitMessage(0);
554         else {
555             session_closed = TRUE;
556             MessageBox(hwnd, "Connection closed by remote host",
557                        "PuTTY", MB_OK | MB_ICONINFORMATION);
558             SetWindowText (hwnd, "PuTTY (inactive)");
559         }
560     }
561 }
562
563 /*
564  * Copy the colour palette from the configuration data into defpal.
565  * This is non-trivial because the colour indices are different.
566  */
567 static void cfgtopalette(void) {
568     int i;
569     static const int ww[] = {
570         6, 7, 8, 9, 10, 11, 12, 13,
571         14, 15, 16, 17, 18, 19, 20, 21,
572         0, 1, 2, 3, 4, 4, 5, 5
573     };
574
575     for (i=0; i<24; i++) {
576         int w = ww[i];
577         defpal[i].rgbtRed = cfg.colours[w][0];
578         defpal[i].rgbtGreen = cfg.colours[w][1];
579         defpal[i].rgbtBlue = cfg.colours[w][2];
580     }
581 }
582
583 /*
584  * Set up the colour palette.
585  */
586 static void init_palette(void) {
587     int i;
588     HDC hdc = GetDC (hwnd);
589     if (hdc) {
590         if (cfg.try_palette &&
591             GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) {
592             logpal = smalloc(sizeof(*logpal)
593                              - sizeof(logpal->palPalEntry)
594                              + NCOLOURS * sizeof(PALETTEENTRY));
595             logpal->palVersion = 0x300;
596             logpal->palNumEntries = NCOLOURS;
597             for (i = 0; i < NCOLOURS; i++) {
598                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
599                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
600                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
601                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
602             }
603             pal = CreatePalette (logpal);
604             if (pal) {
605                 SelectPalette (hdc, pal, FALSE);
606                 RealizePalette (hdc);
607                 SelectPalette (hdc, GetStockObject (DEFAULT_PALETTE),
608                                FALSE);
609             }
610         }
611         ReleaseDC (hwnd, hdc);
612     }
613     if (pal)
614         for (i=0; i<NCOLOURS; i++)
615             colours[i] = PALETTERGB(defpal[i].rgbtRed,
616                                     defpal[i].rgbtGreen,
617                                     defpal[i].rgbtBlue);
618     else
619         for(i=0; i<NCOLOURS; i++)
620             colours[i] = RGB(defpal[i].rgbtRed,
621                              defpal[i].rgbtGreen,
622                              defpal[i].rgbtBlue);
623 }
624
625 /*
626  * Initialise all the fonts we will need. There may be as many as
627  * eight or as few as one. We also:
628  *
629  * - check the font width and height, correcting our guesses if
630  *   necessary.
631  *
632  * - verify that the bold font is the same width as the ordinary
633  *   one, and engage shadow bolding if not.
634  * 
635  * - verify that the underlined font is the same width as the
636  *   ordinary one (manual underlining by means of line drawing can
637  *   be done in a pinch).
638  */
639 static void init_fonts(int pick_width) {
640     TEXTMETRIC tm;
641     int i;
642     int fsize[8];
643     HDC hdc;
644     int fw_dontcare, fw_bold;
645     int firstchar = ' ';
646
647 #ifdef CHECKOEMFONT
648 font_messup:
649 #endif
650     for (i=0; i<8; i++)
651         fonts[i] = NULL;
652
653     if (cfg.fontisbold) {
654         fw_dontcare = FW_BOLD;
655         fw_bold = FW_BLACK;
656    } else {
657         fw_dontcare = FW_DONTCARE;
658         fw_bold = FW_BOLD;
659     }
660
661     hdc = GetDC(hwnd);
662
663     font_height = cfg.fontheight;
664     font_width = pick_width;
665
666 #define f(i,c,w,u) \
667     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
668                            c, OUT_DEFAULT_PRECIS, \
669                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
670                            FIXED_PITCH | FF_DONTCARE, cfg.font)
671
672     if (cfg.vtmode != VT_OEMONLY) {
673         f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
674
675         SelectObject (hdc, fonts[FONT_NORMAL]);
676         GetTextMetrics(hdc, &tm); 
677         font_height = tm.tmHeight;
678         font_width = tm.tmAveCharWidth;
679
680         f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
681
682         if (bold_mode == BOLD_FONT) {
683             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
684             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
685         }
686
687         if (cfg.vtmode == VT_OEMANSI) {
688             f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
689             f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
690
691             if (bold_mode == BOLD_FONT) {
692                 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
693                 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
694             }
695         }
696     }
697     else
698     {
699         f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
700
701         SelectObject (hdc, fonts[FONT_OEM]);
702         GetTextMetrics(hdc, &tm); 
703         font_height = tm.tmHeight;
704         font_width = tm.tmAveCharWidth;
705
706         f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
707
708         if (bold_mode == BOLD_FONT) {
709             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
710             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
711         }
712     }
713 #undef f
714
715     descent = tm.tmAscent + 1;
716     if (descent >= font_height)
717         descent = font_height - 1;
718     firstchar = tm.tmFirstChar;
719
720     for (i=0; i<8; i++) {
721         if (fonts[i]) {
722             if (SelectObject (hdc, fonts[i]) &&
723                 GetTextMetrics(hdc, &tm) )
724                  fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
725             else fsize[i] = -i;
726         }
727         else fsize[i] = -i;
728     }
729
730     ReleaseDC (hwnd, hdc);
731
732     /* ... This is wrong in OEM only mode */
733     if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
734         (bold_mode == BOLD_FONT &&
735          fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
736         und_mode = UND_LINE;
737         DeleteObject (fonts[FONT_UNDERLINE]);
738         if (bold_mode == BOLD_FONT)
739             DeleteObject (fonts[FONT_BOLDUND]);
740     }
741
742     if (bold_mode == BOLD_FONT &&
743         fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
744         bold_mode = BOLD_SHADOW;
745         DeleteObject (fonts[FONT_BOLD]);
746         if (und_mode == UND_FONT)
747             DeleteObject (fonts[FONT_BOLDUND]);
748     }
749
750 #ifdef CHECKOEMFONT
751     /* With the fascist font painting it doesn't matter if the linedraw font
752      * isn't exactly the right size anymore so we don't have to check this.
753      */
754     if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL] ) {
755         if( cfg.fontcharset == OEM_CHARSET )
756         {
757             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
758                    "different sizes. Using OEM-only mode instead",
759                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
760             cfg.vtmode = VT_OEMONLY;
761         }
762         else if( firstchar < ' ' )
763         {
764             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
765                    "different sizes. Using XTerm mode instead",
766                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
767             cfg.vtmode = VT_XWINDOWS;
768         }
769         else
770         {
771             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
772                    "different sizes. Using ISO8859-1 mode instead",
773                    "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
774             cfg.vtmode = VT_POORMAN;
775         }
776
777         for (i=0; i<8; i++)
778             if (fonts[i])
779                 DeleteObject (fonts[i]);
780         goto font_messup;
781     }
782 #endif
783 }
784
785 void request_resize (int w, int h, int refont) {
786     int width, height;
787
788     /* If the window is maximized supress resizing attempts */
789     if(IsZoomed(hwnd)) return;
790     
791 #ifdef CHECKOEMFONT
792     /* Don't do this in OEMANSI, you may get disable messages */
793     if (refont && w != cols && (cols==80 || cols==132)
794           && cfg.vtmode != VT_OEMANSI)
795 #else
796     if (refont && w != cols && (cols==80 || cols==132))
797 #endif
798     {
799        /* If font width too big for screen should we shrink the font more ? */
800         if (w==132)
801             font_width = ((font_width*cols+w/2)/w);
802         else
803             font_width = 0;
804         {
805             int i;
806             for (i=0; i<8; i++)
807                 if (fonts[i])
808                     DeleteObject(fonts[i]);
809         }
810         bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
811         und_mode = UND_FONT;
812         init_fonts(font_width);
813     }
814     else
815     {
816        static int first_time = 1;
817        static RECT ss;
818
819        switch(first_time)
820        {
821        case 1:
822              /* Get the size of the screen */
823              if (GetClientRect(GetDesktopWindow(),&ss))
824                 /* first_time = 0 */;
825              else { first_time = 2; break; }
826        case 0:
827              /* Make sure the values are sane */
828              width  = (ss.right-ss.left-extra_width  ) / font_width;
829              height = (ss.bottom-ss.top-extra_height ) / font_height;
830
831              if (w>width)  w=width;
832              if (h>height) h=height;
833              if (w<15) w = 15;
834              if (h<1) w = 1;
835        }
836     }
837
838     width = extra_width + font_width * w;
839     height = extra_height + font_height * h;
840
841     SetWindowPos (hwnd, NULL, 0, 0, width, height,
842                   SWP_NOACTIVATE | SWP_NOCOPYBITS |
843                   SWP_NOMOVE | SWP_NOZORDER);
844 }
845
846 static void click (Mouse_Button b, int x, int y) {
847     int thistime = GetMessageTime();
848
849     if (lastbtn == b && thistime - lasttime < dbltime) {
850         lastact = (lastact == MA_CLICK ? MA_2CLK :
851                    lastact == MA_2CLK ? MA_3CLK :
852                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
853     } else {
854         lastbtn = b;
855         lastact = MA_CLICK;
856     }
857     if (lastact != MA_NOTHING)
858         term_mouse (b, lastact, x, y);
859     lasttime = thistime;
860 }
861
862 static LRESULT CALLBACK WndProc (HWND hwnd, UINT message,
863                                  WPARAM wParam, LPARAM lParam) {
864     HDC hdc;
865     static int ignore_size = FALSE;
866     static int ignore_clip = FALSE;
867     static int ignore_keymenu = TRUE;
868     static int just_reconfigged = FALSE;
869
870     switch (message) {
871       case WM_TIMER:
872         if (pending_netevent)
873             enact_pending_netevent();
874         if (inbuf_head)
875             term_out();
876         term_update();
877         return 0;
878       case WM_CREATE:
879         break;
880       case WM_CLOSE:
881         if (!cfg.warn_on_close || session_closed ||
882             MessageBox(hwnd, "Are you sure you want to close this session?",
883                        "PuTTY Exit Confirmation",
884                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
885             DestroyWindow(hwnd);
886         return 0;
887       case WM_DESTROY:
888         PostQuitMessage (0);
889         return 0;
890       case WM_SYSCOMMAND:
891         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
892           case SC_KEYMENU:
893             if (ignore_keymenu)
894                 return 0;              /* don't put up system menu on Alt */
895             break;
896           case IDM_SHOWLOG:
897             showeventlog(hwnd);
898             break;
899           case IDM_NEWSESS:
900           case IDM_DUPSESS:
901           case IDM_SAVEDSESS:
902             {
903                 char b[2048];
904                 char c[30], *cl;
905                 int freecl = FALSE;
906                 STARTUPINFO si;
907                 PROCESS_INFORMATION pi;
908                 HANDLE filemap = NULL;
909
910                 if (wParam == IDM_DUPSESS) {
911                     /*
912                      * Allocate a file-mapping memory chunk for the
913                      * config structure.
914                      */
915                     SECURITY_ATTRIBUTES sa;
916                     Config *p;
917
918                     sa.nLength = sizeof(sa);
919                     sa.lpSecurityDescriptor = NULL;
920                     sa.bInheritHandle = TRUE;
921                     filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
922                                                 &sa,
923                                                 PAGE_READWRITE,
924                                                 0,
925                                                 sizeof(Config),
926                                                 NULL);
927                     if (filemap) {
928                         p = (Config *)MapViewOfFile(filemap,
929                                                     FILE_MAP_WRITE,
930                                                     0, 0, sizeof(Config));
931                         if (p) {
932                             *p = cfg;  /* structure copy */
933                             UnmapViewOfFile(p);
934                         }
935                     }
936                     sprintf(c, "putty &%p", filemap);
937                     cl = c;
938                 } else if (wParam == IDM_SAVEDSESS) {
939                     char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
940                     cl = malloc(16 + strlen(session)); /* 8, but play safe */
941                     if (!cl)
942                         cl = NULL;     /* not a very important failure mode */
943                     else {
944                         sprintf(cl, "putty @%s", session);
945                         freecl = TRUE;
946                     }
947                 } else
948                     cl = NULL;
949
950                 GetModuleFileName (NULL, b, sizeof(b)-1);
951                 si.cb = sizeof(si);
952                 si.lpReserved = NULL;
953                 si.lpDesktop = NULL;
954                 si.lpTitle = NULL;
955                 si.dwFlags = 0;
956                 si.cbReserved2 = 0;
957                 si.lpReserved2 = NULL;
958                 CreateProcess (b, cl, NULL, NULL, TRUE,
959                                NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
960
961                 if (filemap)
962                     CloseHandle(filemap);
963                 if (freecl)
964                     free(cl);
965             }
966             break;
967           case IDM_RECONF:
968             if (!do_reconfig(hwnd))
969                 break;
970             just_reconfigged = TRUE;
971             {
972                 int i;
973                 for (i=0; i<8; i++)
974                     if (fonts[i])
975                         DeleteObject(fonts[i]);
976             }
977             bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
978             und_mode = UND_FONT;
979             init_fonts(0);
980             sfree(logpal);
981             /* Telnet will change local echo -> remote if the remote asks */
982             if (cfg.protocol != PROT_TELNET)
983                 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
984             if (pal)
985                 DeleteObject(pal);
986             logpal = NULL;
987             pal = NULL;
988             cfgtopalette();
989             init_palette();
990
991             /* Enable or disable the scroll bar, etc */
992             {
993                 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
994
995                 nflg = flag;
996                 if (cfg.scrollbar) nflg |=  WS_VSCROLL;
997                 else               nflg &= ~WS_VSCROLL;
998                 if (cfg.locksize) 
999                    nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1000                 else              
1001                    nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1002
1003                 if (nflg != flag)
1004                 {
1005                     RECT cr, wr;
1006
1007                     SetWindowLong(hwnd, GWL_STYLE, nflg);
1008                     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1009                     SetWindowPos(hwnd, NULL, 0,0,0,0,
1010                          SWP_NOACTIVATE|SWP_NOCOPYBITS|
1011                          SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1012                          SWP_FRAMECHANGED);
1013
1014                     GetWindowRect (hwnd, &wr);
1015                     GetClientRect (hwnd, &cr);
1016                     extra_width = wr.right - wr.left - cr.right + cr.left;
1017                     extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1018                 }
1019             }
1020
1021             term_size(cfg.height, cfg.width, cfg.savelines);
1022             InvalidateRect(hwnd, NULL, TRUE);
1023             SetWindowPos (hwnd, NULL, 0, 0,
1024                           extra_width + font_width * cfg.width,
1025                           extra_height + font_height * cfg.height,
1026                           SWP_NOACTIVATE | SWP_NOCOPYBITS |
1027                           SWP_NOMOVE | SWP_NOZORDER);
1028             if (IsIconic(hwnd)) {
1029                 SetWindowText (hwnd,
1030                                cfg.win_name_always ? window_name : icon_name);
1031             }
1032             break;
1033           case IDM_CLRSB:
1034             term_clrsb();
1035             break;
1036           case IDM_RESET:
1037             term_pwron();
1038             break;
1039           case IDM_TEL_AYT: back->special (TS_AYT); break;
1040           case IDM_TEL_BRK: back->special (TS_BRK); break;
1041           case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1042           case IDM_TEL_EC: back->special (TS_EC); break;
1043           case IDM_TEL_EL: back->special (TS_EL); break;
1044           case IDM_TEL_GA: back->special (TS_GA); break;
1045           case IDM_TEL_NOP: back->special (TS_NOP); break;
1046           case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1047           case IDM_TEL_AO: back->special (TS_AO); break;
1048           case IDM_TEL_IP: back->special (TS_IP); break;
1049           case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1050           case IDM_TEL_EOR: back->special (TS_EOR); break;
1051           case IDM_TEL_EOF: back->special (TS_EOF); break;
1052           case IDM_ABOUT:
1053             showabout (hwnd);
1054             break;
1055         default:
1056           if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1057             SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1058           }
1059         }
1060         break;
1061
1062 #define X_POS(l) ((int)(short)LOWORD(l))
1063 #define Y_POS(l) ((int)(short)HIWORD(l))
1064
1065 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1066 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1067
1068       case WM_LBUTTONDOWN:
1069         click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1070                TO_CHR_Y(Y_POS(lParam)));
1071         SetCapture(hwnd);
1072         return 0;
1073       case WM_LBUTTONUP:
1074         term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1075                     TO_CHR_Y(Y_POS(lParam)));
1076         ReleaseCapture();
1077         return 0;
1078       case WM_MBUTTONDOWN:
1079         SetCapture(hwnd);
1080         click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1081                TO_CHR_X(X_POS(lParam)),
1082                TO_CHR_Y(Y_POS(lParam)));
1083         return 0;
1084       case WM_MBUTTONUP:
1085         term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1086                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1087                     TO_CHR_Y(Y_POS(lParam)));
1088         ReleaseCapture();
1089         return 0;
1090       case WM_RBUTTONDOWN:
1091         SetCapture(hwnd);
1092         click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1093                TO_CHR_X(X_POS(lParam)),
1094                TO_CHR_Y(Y_POS(lParam)));
1095         return 0;
1096       case WM_RBUTTONUP:
1097         term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1098                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1099                     TO_CHR_Y(Y_POS(lParam)));
1100         ReleaseCapture();
1101         return 0;
1102       case WM_MOUSEMOVE:
1103         /*
1104          * Add the mouse position and message time to the random
1105          * number noise, if we're using ssh.
1106          */
1107         if (cfg.protocol == PROT_SSH)
1108             noise_ultralight(lParam);
1109
1110         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1111             Mouse_Button b;
1112             if (wParam & MK_LBUTTON)
1113                 b = MB_SELECT;
1114             else if (wParam & MK_MBUTTON)
1115                 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1116             else
1117                 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1118             term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1119                         TO_CHR_Y(Y_POS(lParam)));
1120         }
1121         return 0;
1122       case WM_IGNORE_CLIP:
1123         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1124         break;
1125       case WM_IGNORE_KEYMENU:
1126         ignore_keymenu = wParam;       /* do or don't ignore SC_KEYMENU */
1127         break;
1128       case WM_DESTROYCLIPBOARD:
1129         if (!ignore_clip)
1130             term_deselect();
1131         ignore_clip = FALSE;
1132         return 0;
1133       case WM_PAINT:
1134         {
1135             PAINTSTRUCT p;
1136             hdc = BeginPaint (hwnd, &p);
1137             if (pal) {
1138                 SelectPalette (hdc, pal, TRUE);
1139                 RealizePalette (hdc);
1140             }
1141             term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1142                         p.rcPaint.right, p.rcPaint.bottom);
1143             SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1144             SelectObject (hdc, GetStockObject(WHITE_PEN));
1145             EndPaint (hwnd, &p);
1146         }
1147         return 0;
1148       case WM_NETEVENT:
1149         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1150          * but the only one that's likely to try to overload us is FD_READ.
1151          * This means buffering just one is fine.
1152          */
1153         if (pending_netevent)
1154             enact_pending_netevent();
1155
1156         pending_netevent = TRUE;
1157         pend_netevent_wParam=wParam;
1158         pend_netevent_lParam=lParam;
1159         return 0;
1160       case WM_SETFOCUS:
1161         has_focus = TRUE;
1162         term_out();
1163         term_update();
1164         break;
1165       case WM_KILLFOCUS:
1166         has_focus = FALSE;
1167         term_out();
1168         term_update();
1169         break;
1170       case WM_IGNORE_SIZE:
1171         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1172         break;
1173       case WM_ENTERSIZEMOVE:
1174         EnableSizeTip(1);
1175         break;
1176       case WM_EXITSIZEMOVE:
1177         EnableSizeTip(0);
1178         break;
1179       case WM_SIZING:
1180         {
1181             int width, height, w, h, ew, eh;
1182             LPRECT r = (LPRECT)lParam;
1183
1184             width = r->right - r->left - extra_width;
1185             height = r->bottom - r->top - extra_height;
1186             w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1187             h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1188             UpdateSizeTip(hwnd, w, h);
1189             ew = width - w * font_width;
1190             eh = height - h * font_height;
1191             if (ew != 0) {
1192                 if (wParam == WMSZ_LEFT ||
1193                     wParam == WMSZ_BOTTOMLEFT ||
1194                     wParam == WMSZ_TOPLEFT)
1195                     r->left += ew;
1196                 else
1197                     r->right -= ew;
1198             }
1199             if (eh != 0) {
1200                 if (wParam == WMSZ_TOP ||
1201                     wParam == WMSZ_TOPRIGHT ||
1202                     wParam == WMSZ_TOPLEFT)
1203                     r->top += eh;
1204                 else
1205                     r->bottom -= eh;
1206             }
1207             if (ew || eh)
1208                 return 1;
1209             else
1210                 return 0;
1211         }
1212         /* break;  (never reached) */
1213       case WM_SIZE:
1214         if (wParam == SIZE_MINIMIZED) {
1215             SetWindowText (hwnd,
1216                            cfg.win_name_always ? window_name : icon_name);
1217             break;
1218         }
1219         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1220             SetWindowText (hwnd, window_name);
1221         if (!ignore_size) {
1222             int width, height, w, h;
1223 #if 0 /* we have fixed this using WM_SIZING now */
1224             int ew, eh;
1225 #endif
1226
1227             width = LOWORD(lParam);
1228             height = HIWORD(lParam);
1229             w = width / font_width; if (w < 1) w = 1;
1230             h = height / font_height; if (h < 1) h = 1;
1231 #if 0 /* we have fixed this using WM_SIZING now */
1232             ew = width - w * font_width;
1233             eh = height - h * font_height;
1234             if (ew != 0 || eh != 0) {
1235                 RECT r;
1236                 GetWindowRect (hwnd, &r);
1237                 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1238                 SetWindowPos (hwnd, NULL, 0, 0,
1239                               r.right - r.left - ew, r.bottom - r.top - eh,
1240                               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1241             }
1242 #endif
1243             if (w != cols || h != rows || just_reconfigged) {
1244                 term_invalidate();
1245                 term_size (h, w, cfg.savelines);
1246                 back->size();
1247                 just_reconfigged = FALSE;
1248             }
1249         }
1250         ignore_size = FALSE;
1251         return 0;
1252       case WM_VSCROLL:
1253         switch (LOWORD(wParam)) {
1254           case SB_BOTTOM: term_scroll(-1, 0); break;
1255           case SB_TOP: term_scroll(+1, 0); break;
1256           case SB_LINEDOWN: term_scroll (0, +1); break;
1257           case SB_LINEUP: term_scroll (0, -1); break;
1258           case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1259           case SB_PAGEUP: term_scroll (0, -rows/2); break;
1260           case SB_THUMBPOSITION: case SB_THUMBTRACK:
1261             term_scroll (1, HIWORD(wParam)); break;
1262         }
1263         break; 
1264      case WM_PALETTECHANGED:
1265         if ((HWND) wParam != hwnd && pal != NULL) {
1266             HDC hdc = get_ctx();
1267             if (hdc) {
1268                 if (RealizePalette (hdc) > 0)
1269                     UpdateColors (hdc);
1270                 free_ctx (hdc);
1271             }
1272         }
1273         break;
1274       case WM_QUERYNEWPALETTE:
1275         if (pal != NULL) {
1276             HDC hdc = get_ctx();
1277             if (hdc) {
1278                 if (RealizePalette (hdc) > 0)
1279                     UpdateColors (hdc);
1280                 free_ctx (hdc);
1281                 return TRUE;
1282             }
1283         }
1284         return FALSE;
1285       case WM_KEYDOWN:
1286       case WM_SYSKEYDOWN:
1287       case WM_KEYUP:
1288       case WM_SYSKEYUP:
1289         /*
1290          * Add the scan code and keypress timing to the random
1291          * number noise, if we're using ssh.
1292          */
1293         if (cfg.protocol == PROT_SSH)
1294             noise_ultralight(lParam);
1295
1296         /*
1297          * We don't do TranslateMessage since it disassociates the
1298          * resulting CHAR message from the KEYDOWN that sparked it,
1299          * which we occasionally don't want. Instead, we process
1300          * KEYDOWN, and call the Win32 translator functions so that
1301          * we get the translations under _our_ control.
1302          */
1303         {
1304             unsigned char buf[20];
1305             int len;
1306
1307             len = TranslateKey (message, wParam, lParam, buf);
1308             if (len == -1)
1309                 return DefWindowProc (hwnd, message, wParam, lParam);
1310             ldisc->send (buf, len);
1311         }
1312         return 0;
1313       case WM_CHAR:
1314       case WM_SYSCHAR:
1315         /*
1316          * Nevertheless, we are prepared to deal with WM_CHAR
1317          * messages, should they crop up. So if someone wants to
1318          * post the things to us as part of a macro manoeuvre,
1319          * we're ready to cope.
1320          */
1321         {
1322             char c = xlat_kbd2tty((unsigned char)wParam);
1323             ldisc->send (&c, 1);
1324         }
1325         return 0;
1326     }
1327
1328     return DefWindowProc (hwnd, message, wParam, lParam);
1329 }
1330
1331 /*
1332  * Draw a line of text in the window, at given character
1333  * coordinates, in given attributes.
1334  *
1335  * We are allowed to fiddle with the contents of `text'.
1336  */
1337 void do_text (Context ctx, int x, int y, char *text, int len,
1338               unsigned long attr, int lattr) {
1339     COLORREF fg, bg, t;
1340     int nfg, nbg, nfont;
1341     HDC hdc = ctx;
1342     RECT line_box;
1343     int force_manual_underline = 0;
1344     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1345 static int *IpDx = 0, IpDxLEN = 0;;
1346
1347     if (len>IpDxLEN || IpDx[0] != fnt_width) {
1348         int i;
1349         if (len>IpDxLEN) {
1350             sfree(IpDx);
1351             IpDx = smalloc((len+16)*sizeof(int));
1352             IpDxLEN = (len+16);
1353         }
1354         for(i=0; i<IpDxLEN; i++)
1355             IpDx[i] = fnt_width;
1356     }
1357
1358     x *= fnt_width;
1359     y *= font_height;
1360
1361     if (attr & ATTR_ACTCURS) {
1362         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1363         attr ^= ATTR_CUR_XOR;
1364     }
1365
1366     nfont = 0;
1367     if (cfg.vtmode == VT_OEMONLY)
1368         nfont |= FONT_OEM;
1369
1370     /*
1371      * Map high-half characters in order to approximate ISO using
1372      * OEM character set. No characters are missing if the OEM codepage
1373      * is CP850.
1374      */
1375     if (nfont & FONT_OEM) {
1376         int i;
1377         for (i=0; i<len; i++)
1378             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1379 #if 0
1380                 /* This is CP850 ... perfect translation */
1381                 static const char oemhighhalf[] =
1382                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1383                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1384                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1385                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1386                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1387                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1388                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1389                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1390                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1391                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1392                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1393                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1394                     ;
1395 #endif
1396                 /* This is CP437 ... junk translation */
1397                 static const unsigned char oemhighhalf[] = {
1398                     0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1399                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1400                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1401                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1402                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1403                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1404                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1405                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1406                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1407                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1408                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1409                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1410                 };
1411
1412                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1413             }
1414     }
1415
1416     if (attr & ATTR_GBCHR) {
1417         int i;
1418         /*
1419          * GB mapping: map # to pound, and everything else stays
1420          * normal.
1421          */
1422         for (i=0; i<len; i++)
1423             if (text[i] == '#')
1424                 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1425     } else if (attr & ATTR_LINEDRW) {
1426         int i;
1427         /* ISO 8859-1 */
1428         static const char poorman[] =
1429             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1430
1431         /* CP437 */
1432         static const char oemmap_437[] =
1433             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1434             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1435
1436         /* CP850 */
1437         static const char oemmap_850[] =
1438             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1439             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1440
1441         /* Poor windows font ... eg: windows courier */
1442         static const char oemmap[] =
1443             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1444             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1445
1446         /*
1447          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1448          * VT100 line drawing chars; everything else stays normal.
1449          */
1450         switch (cfg.vtmode) {
1451           case VT_XWINDOWS:
1452             for (i=0; i<len; i++)
1453                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1454                     text[i] += '\x01' - '\x60';
1455             break;
1456           case VT_OEMANSI:
1457             /* Make sure we actually have an OEM font */
1458             if (fonts[nfont|FONT_OEM]) { 
1459           case VT_OEMONLY:
1460                 nfont |= FONT_OEM;
1461                 for (i=0; i<len; i++)
1462                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1463                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1464                 break;
1465             }
1466           case VT_POORMAN:
1467             for (i=0; i<len; i++)
1468                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1469                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1470             break;
1471         }
1472     }
1473
1474     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1475     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1476     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1477         nfont |= FONT_BOLD;
1478     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1479         nfont |= FONT_UNDERLINE;
1480     if (!fonts[nfont]) 
1481     {
1482         if (nfont&FONT_UNDERLINE)
1483             force_manual_underline = 1;
1484         /* Don't do the same for manual bold, it could be bad news. */
1485
1486         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1487     }
1488     if (attr & ATTR_REVERSE) {
1489         t = nfg; nfg = nbg; nbg = t;
1490     }
1491     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1492         nfg++;
1493     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1494         nbg++;
1495     fg = colours[nfg];
1496     bg = colours[nbg];
1497     SelectObject (hdc, fonts[nfont]);
1498     SetTextColor (hdc, fg);
1499     SetBkColor (hdc, bg);
1500     SetBkMode (hdc, OPAQUE);
1501     line_box.left   = x;
1502     line_box.top    = y;
1503     line_box.right  = x+fnt_width*len;
1504     line_box.bottom = y+font_height;
1505     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1506     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1507         SetBkMode (hdc, TRANSPARENT);
1508
1509        /* GRR: This draws the character outside it's box and can leave
1510         * 'droppings' even with the clip box! I suppose I could loop it
1511         * one character at a time ... yuk. 
1512         * 
1513         * Or ... I could do a test print with "W", and use +1 or -1 for this
1514         * shift depending on if the leftmost column is blank...
1515         */
1516         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1517     }
1518     if (force_manual_underline || 
1519             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1520         HPEN oldpen;
1521         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1522         MoveToEx (hdc, x, y+descent, NULL);
1523         LineTo (hdc, x+len*fnt_width, y+descent);
1524         oldpen = SelectObject (hdc, oldpen);
1525         DeleteObject (oldpen);
1526     }
1527     if (attr & ATTR_PASCURS) {
1528         POINT pts[5];
1529         HPEN oldpen;
1530         pts[0].x = pts[1].x = pts[4].x = x;
1531         pts[2].x = pts[3].x = x+fnt_width-1;
1532         pts[0].y = pts[3].y = pts[4].y = y;
1533         pts[1].y = pts[2].y = y+font_height-1;
1534         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1535         Polyline (hdc, pts, 5);
1536         oldpen = SelectObject (hdc, oldpen);
1537         DeleteObject (oldpen);
1538     }
1539 }
1540
1541 static int check_compose(int first, int second) {
1542
1543     static char * composetbl[] = {
1544        "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1545        "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1546        "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1547        "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1548        "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1549        "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1550        "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1551        "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1552        "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1553        "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1554        "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1555        "\"uü", "'yý", "htþ", "\"yÿ",
1556     0};
1557
1558     char ** c;
1559     static int recurse = 0;
1560     int nc = -1;
1561
1562     if(0)
1563     {
1564         char buf[256];
1565         char * p;
1566         sprintf(buf, "cc(%d,%d)", first, second);
1567         for(p=buf; *p; p++)
1568             c_write1(*p);
1569     }
1570
1571     for(c=composetbl; *c; c++) {
1572         if( (*c)[0] == first && (*c)[1] == second)
1573         {
1574             return (*c)[2] & 0xFF;
1575         }
1576     }
1577
1578     if(recurse==0)
1579     {
1580         recurse=1;
1581         nc = check_compose(second, first);
1582         if(nc == -1)
1583             nc = check_compose(toupper(first), toupper(second));
1584         if(nc == -1)
1585             nc = check_compose(toupper(second), toupper(first));
1586         recurse=0;
1587     }
1588     return nc;
1589 }
1590
1591
1592 /*
1593  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1594  * codes. Returns number of bytes used or zero to drop the message
1595  * or -1 to forward the message to windows.
1596  */
1597 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1598     BYTE keystate[256];
1599     int  scan, left_alt = 0, key_down, shift_state;
1600     int  r, i, code;
1601     unsigned char * p = output;
1602
1603 static WORD keys[3];
1604 static int compose_state = 0;
1605 static int compose_char = 0;
1606 static WPARAM compose_key = 0;
1607
1608     r = GetKeyboardState(keystate);
1609     if (!r) memset(keystate, 0, sizeof(keystate));
1610     else
1611     {
1612         /* Note if AltGr was pressed and if it was used as a compose key */
1613         if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1614         {
1615             keystate[VK_RMENU] = keystate[VK_MENU];
1616             if (!compose_state) compose_key = wParam;
1617         }
1618         if (wParam == VK_APPS && !compose_state)
1619             compose_key = wParam;
1620
1621         if (wParam == compose_key)
1622         {
1623             if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1624                compose_state = 1;
1625             else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1626                compose_state = 2;
1627             else
1628                compose_state = 0;
1629         }
1630         else if (compose_state==1 && wParam != VK_CONTROL)
1631            compose_state = 0;
1632
1633         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1634         if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1635               && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1636
1637             wParam = VK_EXECUTE;
1638
1639             /* UnToggle NUMLock */
1640             if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1641                 keystate[VK_NUMLOCK] ^= 1;
1642         }
1643
1644         /* And write back the 'adjusted' state */
1645         SetKeyboardState (keystate);
1646     }
1647
1648     /* Disable Auto repeat if required */
1649     if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1650        return 0;
1651
1652     if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1653         left_alt = 1;
1654
1655     key_down = ((HIWORD(lParam)&KF_UP)==0);
1656
1657     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1658     if (left_alt && (keystate[VK_CONTROL]&0x80))
1659         keystate[VK_MENU] = 0;
1660
1661     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1662     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1663                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1664
1665     /* 
1666      * Record that we pressed key so the scroll window can be reset, but
1667      * be careful to avoid Shift-UP/Down
1668      */
1669     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1670         seen_key_event = 1; 
1671     }
1672
1673     /* Make sure we're not pasting */
1674     if (key_down) term_nopaste();
1675
1676     if (compose_state>1 && left_alt) compose_state = 0;
1677
1678     /* Sanitize the number pad if not using a PC NumPad */
1679     if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1680           || cfg.nethack_keypad || compose_state )
1681     {
1682         if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1683         {
1684             int nParam = 0;
1685             switch(wParam)
1686             {
1687             case VK_INSERT:     nParam = VK_NUMPAD0; break;
1688             case VK_END:        nParam = VK_NUMPAD1; break;
1689             case VK_DOWN:       nParam = VK_NUMPAD2; break;
1690             case VK_NEXT:       nParam = VK_NUMPAD3; break;
1691             case VK_LEFT:       nParam = VK_NUMPAD4; break;
1692             case VK_CLEAR:      nParam = VK_NUMPAD5; break;
1693             case VK_RIGHT:      nParam = VK_NUMPAD6; break;
1694             case VK_HOME:       nParam = VK_NUMPAD7; break;
1695             case VK_UP:         nParam = VK_NUMPAD8; break;
1696             case VK_PRIOR:      nParam = VK_NUMPAD9; break;
1697             case VK_DELETE:     nParam = VK_DECIMAL; break;
1698             }
1699             if (nParam)
1700             {
1701                 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1702                 wParam = nParam;
1703             }
1704         }
1705     }
1706
1707     /* If a key is pressed and AltGr is not active */
1708     if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1709     {
1710         /* Okay, prepare for most alts then ...*/
1711         if (left_alt) *p++ = '\033';
1712
1713         /* Lets see if it's a pattern we know all about ... */
1714         if (wParam == VK_PRIOR && shift_state == 1) {
1715             SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1716             return 0;
1717         }
1718         if (wParam == VK_NEXT && shift_state == 1) {
1719             SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1720             return 0;
1721         }
1722         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1723             return -1;
1724         }
1725         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1726             
1727             SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
1728             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1729             SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0);
1730             return -1;
1731         }
1732
1733         /* Nethack keypad */
1734         if (cfg.nethack_keypad && !left_alt) {
1735            switch(wParam) {
1736                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1737                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1738                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1739                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1740                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1741                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1742                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1743                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1744                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1745            }
1746         }
1747
1748         /* Application Keypad */
1749         if (!left_alt) {
1750            int xkey = 0;
1751
1752            if ( cfg.funky_type == 0 ||
1753               ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1754                case VK_EXECUTE: xkey = 'P'; break;
1755                case VK_DIVIDE:  xkey = 'Q'; break;
1756                case VK_MULTIPLY:xkey = 'R'; break;
1757                case VK_SUBTRACT:xkey = 'S'; break;
1758            }
1759            if(app_keypad_keys) switch(wParam) {
1760                case VK_NUMPAD0: xkey = 'p'; break;
1761                case VK_NUMPAD1: xkey = 'q'; break;
1762                case VK_NUMPAD2: xkey = 'r'; break;
1763                case VK_NUMPAD3: xkey = 's'; break;
1764                case VK_NUMPAD4: xkey = 't'; break;
1765                case VK_NUMPAD5: xkey = 'u'; break;
1766                case VK_NUMPAD6: xkey = 'v'; break;
1767                case VK_NUMPAD7: xkey = 'w'; break;
1768                case VK_NUMPAD8: xkey = 'x'; break;
1769                case VK_NUMPAD9: xkey = 'y'; break;
1770
1771                case VK_DECIMAL: xkey = 'n'; break;
1772                case VK_ADD:     if(shift_state) xkey = 'm'; 
1773                                 else            xkey = 'l';
1774                                 break;
1775                case VK_RETURN:
1776                                 if (HIWORD(lParam)&KF_EXTENDED)
1777                                     xkey = 'M';
1778                                 break;
1779             }
1780             if(xkey)
1781             {
1782                 if (vt52_mode)
1783                 {
1784                     if (xkey>='P' && xkey<='S')
1785                         p += sprintf((char *)p, "\x1B%c", xkey); 
1786                     else
1787                         p += sprintf((char *)p, "\x1B?%c", xkey); 
1788                 }
1789                 else 
1790                     p += sprintf((char *)p, "\x1BO%c", xkey); 
1791                 return p - output;
1792             }
1793         }
1794
1795         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
1796         {
1797             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1798             return p-output;
1799         }
1800         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
1801         {
1802             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1803         }
1804         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
1805         {
1806             *p++ = 0; return p - output;
1807         }
1808         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
1809         {
1810             *p++ = 160; return p - output;
1811         }
1812         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
1813         {
1814             *p++ = 3; return p - output;
1815         }
1816         /* Control-2 to Control-8 are special */
1817         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1818         {
1819             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1820             return p - output;
1821         }
1822         if (shift_state == 2 && wParam == 0xBD) {
1823             *p++ = 0x1F;
1824             return p - output;
1825         }
1826         if (shift_state == 2 && wParam == 0xDF) {
1827             *p++ = 0x1C;
1828             return p - output;
1829         }
1830         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1831             *p++ = '\r'; *p++ = '\n';
1832             return p - output;
1833         }
1834
1835         /*
1836          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1837          * for integer decimal nn.)
1838          *
1839          * We also deal with the weird ones here. Linux VCs replace F1
1840          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1841          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1842          * respectively.
1843          */
1844         code = 0;
1845         switch (wParam) {
1846           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1847           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1848           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1849           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1850           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1851           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1852           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1853           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1854           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1855           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1856           case VK_F11: code = 23; break;
1857           case VK_F12: code = 24; break;
1858           case VK_F13: code = 25; break;
1859           case VK_F14: code = 26; break;
1860           case VK_F15: code = 28; break;
1861           case VK_F16: code = 29; break;
1862           case VK_F17: code = 31; break;
1863           case VK_F18: code = 32; break;
1864           case VK_F19: code = 33; break;
1865           case VK_F20: code = 34; break;
1866           case VK_HOME: code = 1; break;
1867           case VK_INSERT: code = 2; break;
1868           case VK_DELETE: code = 3; break;
1869           case VK_END: code = 4; break;
1870           case VK_PRIOR: code = 5; break;
1871           case VK_NEXT: code = 6; break;
1872         }
1873         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1874             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1875             return p - output;
1876         }
1877         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1878             p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1879             return p - output;
1880         }
1881         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1882             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1883             return p - output;
1884         }
1885         if (code) {
1886             p += sprintf((char *)p, "\x1B[%d~", code);
1887             return p - output;
1888         }
1889
1890         /*
1891          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1892          * some reason seems to send VK_CLEAR to Windows...).
1893          */
1894         {
1895             char xkey = 0;
1896             switch (wParam) {
1897                 case VK_UP:     xkey = 'A'; break;
1898                 case VK_DOWN:   xkey = 'B'; break;
1899                 case VK_RIGHT:  xkey = 'C'; break;
1900                 case VK_LEFT:   xkey = 'D'; break;
1901                 case VK_CLEAR:  xkey = 'G'; break;
1902             }
1903             if (xkey)
1904             {
1905                 if (vt52_mode)
1906                     p += sprintf((char *)p, "\x1B%c", xkey);
1907                 else if (app_cursor_keys)
1908                     p += sprintf((char *)p, "\x1BO%c", xkey);
1909                 else
1910                     p += sprintf((char *)p, "\x1B[%c", xkey);
1911                 return p - output;
1912             }
1913         }
1914     }
1915
1916     /* Okay we've done everything interesting; let windows deal with 
1917      * the boring stuff */
1918     {
1919         BOOL capsOn=keystate[VK_CAPITAL] !=0;
1920
1921         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
1922         if(cfg.xlat_capslockcyr)
1923             keystate[VK_CAPITAL] = 0;
1924
1925         r = ToAscii (wParam, scan, keystate, keys, 0);
1926         if(r>0)
1927         {
1928             p = output;
1929             for(i=0; i<r; i++)
1930             {
1931                 unsigned char ch = (unsigned char)keys[i];
1932
1933                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
1934                     compose_char = ch;
1935                     compose_state ++;
1936                     continue;
1937                 }
1938                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
1939                     int nc;
1940                     compose_state = 0;
1941
1942                     if ((nc=check_compose(compose_char,ch)) == -1)
1943                     {
1944                         c_write1('\007');
1945                         return 0;
1946                     }
1947                     *p++ = xlat_kbd2tty((unsigned char)nc);
1948                     return p-output;
1949                 }
1950
1951                 compose_state = 0;
1952
1953                 if( left_alt && key_down ) *p++ = '\033';
1954                 if (!key_down)
1955                     *p++ = ch;
1956                 else
1957                 {
1958                     if(capsOn)
1959                         ch = xlat_latkbd2win(ch);
1960                     *p++ = xlat_kbd2tty(ch);
1961                 }
1962             }
1963
1964             /* This is so the ALT-Numpad and dead keys work correctly. */
1965             keys[0] = 0;
1966
1967             return p-output;
1968         }
1969     }
1970
1971     /* This stops ALT press-release doing a 'COMMAND MENU' function */
1972 #if 0
1973     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
1974     {
1975         keystate[VK_MENU] = 0;
1976         return 0;
1977     }
1978 #endif
1979
1980     return -1;
1981 }
1982
1983 void set_title (char *title) {
1984     sfree (window_name);
1985     window_name = smalloc(1+strlen(title));
1986     strcpy (window_name, title);
1987     if (cfg.win_name_always || !IsIconic(hwnd))
1988         SetWindowText (hwnd, title);
1989 }
1990
1991 void set_icon (char *title) {
1992     sfree (icon_name);
1993     icon_name = smalloc(1+strlen(title));
1994     strcpy (icon_name, title);
1995     if (!cfg.win_name_always && IsIconic(hwnd))
1996         SetWindowText (hwnd, title);
1997 }
1998
1999 void set_sbar (int total, int start, int page) {
2000     SCROLLINFO si;
2001
2002     if (!cfg.scrollbar) return;
2003
2004     si.cbSize = sizeof(si);
2005     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2006     si.nMin = 0;
2007     si.nMax = total - 1;
2008     si.nPage = page;
2009     si.nPos = start;
2010     if (hwnd)
2011         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2012 }
2013
2014 Context get_ctx(void) {
2015     HDC hdc;
2016     if (hwnd) {
2017         hdc = GetDC (hwnd);
2018         if (hdc && pal)
2019             SelectPalette (hdc, pal, FALSE);
2020         return hdc;
2021     } else
2022         return NULL;
2023 }
2024
2025 void free_ctx (Context ctx) {
2026     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2027     ReleaseDC (hwnd, ctx);
2028 }
2029
2030 static void real_palette_set (int n, int r, int g, int b) {
2031     if (pal) {
2032         logpal->palPalEntry[n].peRed = r;
2033         logpal->palPalEntry[n].peGreen = g;
2034         logpal->palPalEntry[n].peBlue = b;
2035         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2036         colours[n] = PALETTERGB(r, g, b);
2037         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2038     } else
2039         colours[n] = RGB(r, g, b);
2040 }
2041
2042 void palette_set (int n, int r, int g, int b) {
2043     static const int first[21] = {
2044         0, 2, 4, 6, 8, 10, 12, 14,
2045         1, 3, 5, 7, 9, 11, 13, 15,
2046         16, 17, 18, 20, 22
2047     };
2048     real_palette_set (first[n], r, g, b);
2049     if (first[n] >= 18)
2050         real_palette_set (first[n]+1, r, g, b);
2051     if (pal) {
2052         HDC hdc = get_ctx();
2053         UnrealizeObject (pal);
2054         RealizePalette (hdc);
2055         free_ctx (hdc);
2056     }
2057 }
2058
2059 void palette_reset (void) {
2060     int i;
2061
2062     for (i = 0; i < NCOLOURS; i++) {
2063         if (pal) {
2064             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2065             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2066             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2067             logpal->palPalEntry[i].peFlags = 0;
2068             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2069                                     defpal[i].rgbtGreen,
2070                                     defpal[i].rgbtBlue);
2071         } else
2072             colours[i] = RGB(defpal[i].rgbtRed,
2073                              defpal[i].rgbtGreen,
2074                              defpal[i].rgbtBlue);
2075     }
2076
2077     if (pal) {
2078         HDC hdc;
2079         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2080         hdc = get_ctx();
2081         RealizePalette (hdc);
2082         free_ctx (hdc);
2083     }
2084 }
2085
2086 void write_clip (void *data, int len) {
2087     HGLOBAL clipdata;
2088     void *lock;
2089
2090     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2091     if (!clipdata)
2092         return;
2093     lock = GlobalLock (clipdata);
2094     if (!lock)
2095         return;
2096     memcpy (lock, data, len);
2097     ((unsigned char *) lock) [len] = 0;
2098     GlobalUnlock (clipdata);
2099
2100     SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2101     if (OpenClipboard (hwnd)) {
2102         EmptyClipboard();
2103         SetClipboardData (CF_TEXT, clipdata);
2104         CloseClipboard();
2105     } else
2106         GlobalFree (clipdata);
2107     SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2108 }
2109
2110 void get_clip (void **p, int *len) {
2111     static HGLOBAL clipdata = NULL;
2112
2113     if (!p) {
2114         if (clipdata)
2115             GlobalUnlock (clipdata);
2116         clipdata = NULL;
2117         return;
2118     } else {
2119         if (OpenClipboard (NULL)) {
2120             clipdata = GetClipboardData (CF_TEXT);
2121             CloseClipboard();
2122             if (clipdata) {
2123                 *p = GlobalLock (clipdata);
2124                 if (*p) {
2125                     *len = strlen(*p);
2126                     return;
2127                 }
2128             }
2129         }
2130     }
2131
2132     *p = NULL;
2133     *len = 0;
2134 }
2135
2136 /*
2137  * Move `lines' lines from position `from' to position `to' in the
2138  * window.
2139  */
2140 void optimised_move (int to, int from, int lines) {
2141     RECT r;
2142     int min, max;
2143
2144     min = (to < from ? to : from);
2145     max = to + from - min;
2146
2147     r.left = 0; r.right = cols * font_width;
2148     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2149     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2150 }
2151
2152 /*
2153  * Print a message box and perform a fatal exit.
2154  */
2155 void fatalbox(char *fmt, ...) {
2156     va_list ap;
2157     char stuff[200];
2158
2159     va_start(ap, fmt);
2160     vsprintf(stuff, fmt, ap);
2161     va_end(ap);
2162     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2163     exit(1);
2164 }
2165
2166 /*
2167  * Beep.
2168  */
2169 void beep(int errorbeep) {
2170     static long last_beep = 0;
2171     long now, beep_diff;
2172
2173     now = GetTickCount();
2174     beep_diff = now-last_beep;
2175
2176     /* Make sure we only respond to one beep per packet or so */
2177     if (beep_diff>=0 && beep_diff<50)
2178         return;
2179
2180     if(errorbeep)
2181        MessageBeep(MB_ICONHAND);
2182     else
2183        MessageBeep(MB_OK);
2184
2185     last_beep = GetTickCount();
2186 }