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