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