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