]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
4c83e9bd574800cb5822da95fa6fef43ffa3c1c3
[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 "storage.h"
17 #include "win_res.h"
18
19 #define IDM_SHOWLOG   0x0010
20 #define IDM_NEWSESS   0x0020
21 #define IDM_DUPSESS   0x0030
22 #define IDM_RECONF    0x0040
23 #define IDM_CLRSB     0x0050
24 #define IDM_RESET     0x0060
25 #define IDM_TEL_AYT   0x0070
26 #define IDM_TEL_BRK   0x0080
27 #define IDM_TEL_SYNCH 0x0090
28 #define IDM_TEL_EC    0x00a0
29 #define IDM_TEL_EL    0x00b0
30 #define IDM_TEL_GA    0x00c0
31 #define IDM_TEL_NOP   0x00d0
32 #define IDM_TEL_ABORT 0x00e0
33 #define IDM_TEL_AO    0x00f0
34 #define IDM_TEL_IP    0x0100
35 #define IDM_TEL_SUSP  0x0110
36 #define IDM_TEL_EOR   0x0120
37 #define IDM_TEL_EOF   0x0130
38 #define IDM_ABOUT     0x0140
39 #define IDM_SAVEDSESS 0x0150
40
41 #define IDM_SAVED_MIN 0x1000
42 #define IDM_SAVED_MAX 0x2000
43
44 #define WM_IGNORE_SIZE (WM_XUSER + 1)
45 #define WM_IGNORE_CLIP (WM_XUSER + 2)
46
47 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
48 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
49 static void cfgtopalette(void);
50 static void init_palette(void);
51 static void init_fonts(int);
52
53 static int extra_width, extra_height;
54
55 static int pending_netevent = 0;
56 static WPARAM pend_netevent_wParam = 0;
57 static LPARAM pend_netevent_lParam = 0;
58 static void enact_pending_netevent(void);
59
60 #define FONT_NORMAL 0
61 #define FONT_BOLD 1
62 #define FONT_UNDERLINE 2
63 #define FONT_BOLDUND 3
64 #define FONT_OEM 4
65 #define FONT_OEMBOLD 5
66 #define FONT_OEMBOLDUND 6
67 #define FONT_OEMUND 7
68 static HFONT fonts[8];
69 static int font_needs_hand_underlining;
70 static enum {
71     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
72 } bold_mode;
73 static enum {
74     UND_LINE, UND_FONT
75 } und_mode;
76 static int descent;
77
78 #define NCOLOURS 24
79 static COLORREF colours[NCOLOURS];
80 static HPALETTE pal;
81 static LPLOGPALETTE logpal;
82 static RGBTRIPLE defpal[NCOLOURS];
83
84 static HWND hwnd;
85
86 static int dbltime, lasttime, lastact;
87 static Mouse_Button lastbtn;
88
89 static char *window_name, *icon_name;
90
91 static Ldisc *real_ldisc;
92
93 void begin_session(void) {
94     ldisc = real_ldisc;
95 }
96
97 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
98     static char appname[] = "PuTTY";
99     WORD winsock_ver;
100     WSADATA wsadata;
101     WNDCLASS wndclass;
102     MSG msg;
103     int guess_width, guess_height;
104
105     putty_inst = inst;
106     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
107
108     winsock_ver = MAKEWORD(1, 1);
109     if (WSAStartup(winsock_ver, &wsadata)) {
110         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
111                    MB_OK | MB_ICONEXCLAMATION);
112         return 1;
113     }
114     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
115         MessageBox(NULL, "WinSock version is incompatible with 1.1",
116                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
117         WSACleanup();
118         return 1;
119     }
120     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
121
122     InitCommonControls();
123
124     /*
125      * Process the command line.
126      */
127     {
128         char *p;
129
130         default_protocol = DEFAULT_PROTOCOL;
131         default_port = DEFAULT_PORT;
132
133         do_defaults(NULL);
134
135         p = cmdline;
136         while (*p && isspace(*p)) p++;
137
138         /*
139          * Process command line options first. Yes, this can be
140          * done better, and it will be as soon as I have the
141          * energy...
142          */
143         while (*p == '-') {
144             char *q = p + strcspn(p, " \t");
145             p++;
146             if (q == p + 3 &&
147                 tolower(p[0]) == 's' &&
148                 tolower(p[1]) == 's' &&
149                 tolower(p[2]) == 'h') {
150                 default_protocol = cfg.protocol = PROT_SSH;
151                 default_port = cfg.port = 22;
152             } else if (q == p + 3 &&
153                 tolower(p[0]) == 'l' &&
154                 tolower(p[1]) == 'o' &&
155                 tolower(p[2]) == 'g') {
156                 logfile = "putty.log";
157             } else if (q == p + 7 &&
158                 tolower(p[0]) == 'c' &&
159                 tolower(p[1]) == 'l' &&
160                 tolower(p[2]) == 'e' &&
161                 tolower(p[3]) == 'a' &&
162                 tolower(p[4]) == 'n' &&
163                 tolower(p[5]) == 'u' &&
164                 tolower(p[6]) == 'p') {
165                 /*
166                  * `putty -cleanup'. Remove all registry entries
167                  * associated with PuTTY, and also find and delete
168                  * the random seed file.
169                  */
170                 if (MessageBox(NULL,
171                                "This procedure will remove ALL Registry\n"
172                                "entries associated with PuTTY, and will\n"
173                                "also remove the PuTTY random seed file.\n"
174                                "\n"
175                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
176                                "SESSIONS. Are you really sure you want\n"
177                                "to continue?",
178                                "PuTTY Warning",
179                                MB_YESNO | MB_ICONWARNING) == IDYES) {
180                     cleanup_all();
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         CreateCaret(hwnd, NULL, font_width, font_height);
1273         term_out();
1274         term_update();
1275         break;
1276       case WM_KILLFOCUS:
1277         has_focus = FALSE;
1278         DestroyCaret();
1279         term_out();
1280         term_update();
1281         break;
1282       case WM_IGNORE_SIZE:
1283         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1284         break;
1285       case WM_ENTERSIZEMOVE:
1286         EnableSizeTip(1);
1287         resizing = TRUE;
1288         break;
1289       case WM_EXITSIZEMOVE:
1290         EnableSizeTip(0);
1291         resizing = FALSE;
1292         back->size();
1293         break;
1294       case WM_SIZING:
1295         {
1296             int width, height, w, h, ew, eh;
1297             LPRECT r = (LPRECT)lParam;
1298
1299             width = r->right - r->left - extra_width;
1300             height = r->bottom - r->top - extra_height;
1301             w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1302             h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1303             UpdateSizeTip(hwnd, w, h);
1304             ew = width - w * font_width;
1305             eh = height - h * font_height;
1306             if (ew != 0) {
1307                 if (wParam == WMSZ_LEFT ||
1308                     wParam == WMSZ_BOTTOMLEFT ||
1309                     wParam == WMSZ_TOPLEFT)
1310                     r->left += ew;
1311                 else
1312                     r->right -= ew;
1313             }
1314             if (eh != 0) {
1315                 if (wParam == WMSZ_TOP ||
1316                     wParam == WMSZ_TOPRIGHT ||
1317                     wParam == WMSZ_TOPLEFT)
1318                     r->top += eh;
1319                 else
1320                     r->bottom -= eh;
1321             }
1322             if (ew || eh)
1323                 return 1;
1324             else
1325                 return 0;
1326         }
1327         /* break;  (never reached) */
1328       case WM_SIZE:
1329         if (wParam == SIZE_MINIMIZED) {
1330             SetWindowText (hwnd,
1331                            cfg.win_name_always ? window_name : icon_name);
1332             break;
1333         }
1334         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1335             SetWindowText (hwnd, window_name);
1336         if (!ignore_size) {
1337             int width, height, w, h;
1338 #if 0 /* we have fixed this using WM_SIZING now */
1339             int ew, eh;
1340 #endif
1341
1342             width = LOWORD(lParam);
1343             height = HIWORD(lParam);
1344             w = width / font_width; if (w < 1) w = 1;
1345             h = height / font_height; if (h < 1) h = 1;
1346 #if 0 /* we have fixed this using WM_SIZING now */
1347             ew = width - w * font_width;
1348             eh = height - h * font_height;
1349             if (ew != 0 || eh != 0) {
1350                 RECT r;
1351                 GetWindowRect (hwnd, &r);
1352                 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1353                 SetWindowPos (hwnd, NULL, 0, 0,
1354                               r.right - r.left - ew, r.bottom - r.top - eh,
1355                               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1356             }
1357 #endif
1358             if (w != cols || h != rows || just_reconfigged) {
1359                 term_invalidate();
1360                 term_size (h, w, cfg.savelines);
1361                 /*
1362                  * Don't call back->size in mid-resize. (To prevent
1363                  * massive numbers of resize events getting sent
1364                  * down the connection during an NT opaque drag.)
1365                  */
1366                 if (!resizing)
1367                     back->size();
1368                 just_reconfigged = FALSE;
1369             }
1370         }
1371         ignore_size = FALSE;
1372         return 0;
1373       case WM_VSCROLL:
1374         switch (LOWORD(wParam)) {
1375           case SB_BOTTOM: term_scroll(-1, 0); break;
1376           case SB_TOP: term_scroll(+1, 0); break;
1377           case SB_LINEDOWN: term_scroll (0, +1); break;
1378           case SB_LINEUP: term_scroll (0, -1); break;
1379           case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1380           case SB_PAGEUP: term_scroll (0, -rows/2); break;
1381           case SB_THUMBPOSITION: case SB_THUMBTRACK:
1382             term_scroll (1, HIWORD(wParam)); break;
1383         }
1384         break; 
1385      case WM_PALETTECHANGED:
1386         if ((HWND) wParam != hwnd && pal != NULL) {
1387             HDC hdc = get_ctx();
1388             if (hdc) {
1389                 if (RealizePalette (hdc) > 0)
1390                     UpdateColors (hdc);
1391                 free_ctx (hdc);
1392             }
1393         }
1394         break;
1395       case WM_QUERYNEWPALETTE:
1396         if (pal != NULL) {
1397             HDC hdc = get_ctx();
1398             if (hdc) {
1399                 if (RealizePalette (hdc) > 0)
1400                     UpdateColors (hdc);
1401                 free_ctx (hdc);
1402                 return TRUE;
1403             }
1404         }
1405         return FALSE;
1406       case WM_KEYDOWN:
1407       case WM_SYSKEYDOWN:
1408       case WM_KEYUP:
1409       case WM_SYSKEYUP:
1410         /*
1411          * Add the scan code and keypress timing to the random
1412          * number noise, if we're using ssh.
1413          */
1414         if (cfg.protocol == PROT_SSH)
1415             noise_ultralight(lParam);
1416
1417         /*
1418          * We don't do TranslateMessage since it disassociates the
1419          * resulting CHAR message from the KEYDOWN that sparked it,
1420          * which we occasionally don't want. Instead, we process
1421          * KEYDOWN, and call the Win32 translator functions so that
1422          * we get the translations under _our_ control.
1423          */
1424         {
1425             unsigned char buf[20];
1426             int len;
1427
1428             len = TranslateKey (message, wParam, lParam, buf);
1429             if (len == -1)
1430                 return DefWindowProc (hwnd, message, wParam, lParam);
1431             ldisc->send (buf, len);
1432         }
1433         return 0;
1434       case WM_CHAR:
1435       case WM_SYSCHAR:
1436         /*
1437          * Nevertheless, we are prepared to deal with WM_CHAR
1438          * messages, should they crop up. So if someone wants to
1439          * post the things to us as part of a macro manoeuvre,
1440          * we're ready to cope.
1441          */
1442         {
1443             char c = xlat_kbd2tty((unsigned char)wParam);
1444             ldisc->send (&c, 1);
1445         }
1446         return 0;
1447     }
1448
1449     return DefWindowProc (hwnd, message, wParam, lParam);
1450 }
1451
1452 /*
1453  * Move the system caret. (We maintain one, even though it's
1454  * invisible, for the benefit of blind people: apparently some
1455  * helper software tracks the system caret, so we should arrange to
1456  * have one.)
1457  */
1458 void sys_cursor(int x, int y) {
1459     SetCaretPos(x * font_width, y * font_height);
1460 }
1461
1462 /*
1463  * Draw a line of text in the window, at given character
1464  * coordinates, in given attributes.
1465  *
1466  * We are allowed to fiddle with the contents of `text'.
1467  */
1468 void do_text (Context ctx, int x, int y, char *text, int len,
1469               unsigned long attr, int lattr) {
1470     COLORREF fg, bg, t;
1471     int nfg, nbg, nfont;
1472     HDC hdc = ctx;
1473     RECT line_box;
1474     int force_manual_underline = 0;
1475     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1476     static int *IpDx = 0, IpDxLEN = 0;;
1477
1478     if (len>IpDxLEN || IpDx[0] != fnt_width) {
1479         int i;
1480         if (len>IpDxLEN) {
1481             sfree(IpDx);
1482             IpDx = smalloc((len+16)*sizeof(int));
1483             IpDxLEN = (len+16);
1484         }
1485         for(i=0; i<IpDxLEN; i++)
1486             IpDx[i] = fnt_width;
1487     }
1488
1489     x *= fnt_width;
1490     y *= font_height;
1491
1492     if (attr & ATTR_ACTCURS) {
1493         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1494         attr ^= ATTR_CUR_XOR;
1495     }
1496
1497     nfont = 0;
1498     if (cfg.vtmode == VT_OEMONLY)
1499         nfont |= FONT_OEM;
1500
1501     /*
1502      * Map high-half characters in order to approximate ISO using
1503      * OEM character set. No characters are missing if the OEM codepage
1504      * is CP850.
1505      */
1506     if (nfont & FONT_OEM) {
1507         int i;
1508         for (i=0; i<len; i++)
1509             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1510 #if 0
1511                 /* This is CP850 ... perfect translation */
1512                 static const char oemhighhalf[] =
1513                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1514                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1515                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1516                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1517                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1518                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1519                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1520                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1521                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1522                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1523                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1524                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1525                     ;
1526 #endif
1527                 /* This is CP437 ... junk translation */
1528                 static const unsigned char oemhighhalf[] = {
1529                     0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1530                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1531                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1532                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1533                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1534                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1535                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1536                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1537                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1538                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1539                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1540                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1541                 };
1542
1543                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1544             }
1545     }
1546
1547     if (attr & ATTR_GBCHR) {
1548         int i;
1549         /*
1550          * GB mapping: map # to pound, and everything else stays
1551          * normal.
1552          */
1553         for (i=0; i<len; i++)
1554             if (text[i] == '#')
1555                 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1556     } else if (attr & ATTR_LINEDRW) {
1557         int i;
1558         /* ISO 8859-1 */
1559         static const char poorman[] =
1560             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1561
1562         /* CP437 */
1563         static const char oemmap_437[] =
1564             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1565             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1566
1567         /* CP850 */
1568         static const char oemmap_850[] =
1569             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1570             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1571
1572         /* Poor windows font ... eg: windows courier */
1573         static const char oemmap[] =
1574             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1575             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1576
1577         /*
1578          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1579          * VT100 line drawing chars; everything else stays normal.
1580          */
1581         switch (cfg.vtmode) {
1582           case VT_XWINDOWS:
1583             for (i=0; i<len; i++)
1584                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1585                     text[i] += '\x01' - '\x60';
1586             break;
1587           case VT_OEMANSI:
1588             /* Make sure we actually have an OEM font */
1589             if (fonts[nfont|FONT_OEM]) { 
1590           case VT_OEMONLY:
1591                 nfont |= FONT_OEM;
1592                 for (i=0; i<len; i++)
1593                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1594                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1595                 break;
1596             }
1597           case VT_POORMAN:
1598             for (i=0; i<len; i++)
1599                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1600                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1601             break;
1602         }
1603     }
1604
1605     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1606     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1607     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1608         nfont |= FONT_BOLD;
1609     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1610         nfont |= FONT_UNDERLINE;
1611     if (!fonts[nfont]) 
1612     {
1613         if (nfont&FONT_UNDERLINE)
1614             force_manual_underline = 1;
1615         /* Don't do the same for manual bold, it could be bad news. */
1616
1617         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1618     }
1619     if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1620         force_manual_underline = 1;
1621     if (attr & ATTR_REVERSE) {
1622         t = nfg; nfg = nbg; nbg = t;
1623     }
1624     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1625         nfg++;
1626     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1627         nbg++;
1628     fg = colours[nfg];
1629     bg = colours[nbg];
1630     SelectObject (hdc, fonts[nfont]);
1631     SetTextColor (hdc, fg);
1632     SetBkColor (hdc, bg);
1633     SetBkMode (hdc, OPAQUE);
1634     line_box.left   = x;
1635     line_box.top    = y;
1636     line_box.right  = x+fnt_width*len;
1637     line_box.bottom = y+font_height;
1638     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1639     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1640         SetBkMode (hdc, TRANSPARENT);
1641
1642        /* GRR: This draws the character outside it's box and can leave
1643         * 'droppings' even with the clip box! I suppose I could loop it
1644         * one character at a time ... yuk. 
1645         * 
1646         * Or ... I could do a test print with "W", and use +1 or -1 for this
1647         * shift depending on if the leftmost column is blank...
1648         */
1649         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1650     }
1651     if (force_manual_underline || 
1652             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1653         HPEN oldpen;
1654         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1655         MoveToEx (hdc, x, y+descent, NULL);
1656         LineTo (hdc, x+len*fnt_width, y+descent);
1657         oldpen = SelectObject (hdc, oldpen);
1658         DeleteObject (oldpen);
1659     }
1660     if (attr & ATTR_PASCURS) {
1661         POINT pts[5];
1662         HPEN oldpen;
1663         pts[0].x = pts[1].x = pts[4].x = x;
1664         pts[2].x = pts[3].x = x+fnt_width-1;
1665         pts[0].y = pts[3].y = pts[4].y = y;
1666         pts[1].y = pts[2].y = y+font_height-1;
1667         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1668         Polyline (hdc, pts, 5);
1669         oldpen = SelectObject (hdc, oldpen);
1670         DeleteObject (oldpen);
1671     }
1672 }
1673
1674 static int check_compose(int first, int second) {
1675
1676     static char * composetbl[] = {
1677        "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1678        "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1679        "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1680        "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1681        "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1682        "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1683        "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1684        "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1685        "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1686        "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1687        "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1688        "\"uü", "'yý", "htþ", "\"yÿ",
1689     0};
1690
1691     char ** c;
1692     static int recurse = 0;
1693     int nc = -1;
1694
1695     if(0)
1696     {
1697         char buf[256];
1698         char * p;
1699         sprintf(buf, "cc(%d,%d)", first, second);
1700         for(p=buf; *p; p++)
1701             c_write1(*p);
1702     }
1703
1704     for(c=composetbl; *c; c++) {
1705         if( (*c)[0] == first && (*c)[1] == second)
1706         {
1707             return (*c)[2] & 0xFF;
1708         }
1709     }
1710
1711     if(recurse==0)
1712     {
1713         recurse=1;
1714         nc = check_compose(second, first);
1715         if(nc == -1)
1716             nc = check_compose(toupper(first), toupper(second));
1717         if(nc == -1)
1718             nc = check_compose(toupper(second), toupper(first));
1719         recurse=0;
1720     }
1721     return nc;
1722 }
1723
1724
1725 /*
1726  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1727  * codes. Returns number of bytes used or zero to drop the message
1728  * or -1 to forward the message to windows.
1729  */
1730 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1731     BYTE keystate[256];
1732     int  scan, left_alt = 0, key_down, shift_state;
1733     int  r, i, code;
1734     unsigned char * p = output;
1735
1736 static WORD keys[3];
1737 static int compose_state = 0;
1738 static int compose_char = 0;
1739 static WPARAM compose_key = 0;
1740
1741     r = GetKeyboardState(keystate);
1742     if (!r) memset(keystate, 0, sizeof(keystate));
1743     else
1744     {
1745         /* Note if AltGr was pressed and if it was used as a compose key */
1746         if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1747         {
1748             keystate[VK_RMENU] = keystate[VK_MENU];
1749             if (!compose_state) compose_key = wParam;
1750         }
1751         if (wParam == VK_APPS && !compose_state)
1752             compose_key = wParam;
1753
1754         if (wParam == compose_key)
1755         {
1756             if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1757                compose_state = 1;
1758             else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1759                compose_state = 2;
1760             else
1761                compose_state = 0;
1762         }
1763         else if (compose_state==1 && wParam != VK_CONTROL)
1764            compose_state = 0;
1765
1766         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1767         if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1768               && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1769
1770             wParam = VK_EXECUTE;
1771
1772             /* UnToggle NUMLock */
1773             if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1774                 keystate[VK_NUMLOCK] ^= 1;
1775         }
1776
1777         /* And write back the 'adjusted' state */
1778         SetKeyboardState (keystate);
1779     }
1780
1781     /* Disable Auto repeat if required */
1782     if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1783        return 0;
1784
1785     if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1786         left_alt = 1;
1787
1788     key_down = ((HIWORD(lParam)&KF_UP)==0);
1789
1790     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1791     if (left_alt && (keystate[VK_CONTROL]&0x80))
1792         keystate[VK_MENU] = 0;
1793
1794     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1795     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1796                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1797
1798     /* 
1799      * Record that we pressed key so the scroll window can be reset, but
1800      * be careful to avoid Shift-UP/Down
1801      */
1802     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1803         seen_key_event = 1; 
1804     }
1805
1806     /* Make sure we're not pasting */
1807     if (key_down) term_nopaste();
1808
1809     if (compose_state>1 && left_alt) compose_state = 0;
1810
1811     /* Sanitize the number pad if not using a PC NumPad */
1812     if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1813           || cfg.nethack_keypad || compose_state )
1814     {
1815         if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1816         {
1817             int nParam = 0;
1818             switch(wParam)
1819             {
1820             case VK_INSERT:     nParam = VK_NUMPAD0; break;
1821             case VK_END:        nParam = VK_NUMPAD1; break;
1822             case VK_DOWN:       nParam = VK_NUMPAD2; break;
1823             case VK_NEXT:       nParam = VK_NUMPAD3; break;
1824             case VK_LEFT:       nParam = VK_NUMPAD4; break;
1825             case VK_CLEAR:      nParam = VK_NUMPAD5; break;
1826             case VK_RIGHT:      nParam = VK_NUMPAD6; break;
1827             case VK_HOME:       nParam = VK_NUMPAD7; break;
1828             case VK_UP:         nParam = VK_NUMPAD8; break;
1829             case VK_PRIOR:      nParam = VK_NUMPAD9; break;
1830             case VK_DELETE:     nParam = VK_DECIMAL; break;
1831             }
1832             if (nParam)
1833             {
1834                 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1835                 wParam = nParam;
1836             }
1837         }
1838     }
1839
1840     /* If a key is pressed and AltGr is not active */
1841     if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1842     {
1843         /* Okay, prepare for most alts then ...*/
1844         if (left_alt) *p++ = '\033';
1845
1846         /* Lets see if it's a pattern we know all about ... */
1847         if (wParam == VK_PRIOR && shift_state == 1) {
1848             SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1849             return 0;
1850         }
1851         if (wParam == VK_NEXT && shift_state == 1) {
1852             SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1853             return 0;
1854         }
1855         if (wParam == VK_INSERT && shift_state == 1) {
1856             term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1857             term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1858             return 0;
1859         }
1860         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1861             return -1;
1862         }
1863         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1864             
1865             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1866             return -1;
1867         }
1868
1869         /* Nethack keypad */
1870         if (cfg.nethack_keypad && !left_alt) {
1871            switch(wParam) {
1872                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1873                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1874                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1875                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1876                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1877                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1878                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1879                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1880                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1881            }
1882         }
1883
1884         /* Application Keypad */
1885         if (!left_alt) {
1886            int xkey = 0;
1887
1888            if ( cfg.funky_type == 0 ||
1889               ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1890                case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1891                case VK_DIVIDE:  xkey = 'Q'; break;
1892                case VK_MULTIPLY:xkey = 'R'; break;
1893                case VK_SUBTRACT:xkey = 'S'; break;
1894            }
1895            if(app_keypad_keys) switch(wParam) {
1896                case VK_NUMPAD0: xkey = 'p'; break;
1897                case VK_NUMPAD1: xkey = 'q'; break;
1898                case VK_NUMPAD2: xkey = 'r'; break;
1899                case VK_NUMPAD3: xkey = 's'; break;
1900                case VK_NUMPAD4: xkey = 't'; break;
1901                case VK_NUMPAD5: xkey = 'u'; break;
1902                case VK_NUMPAD6: xkey = 'v'; break;
1903                case VK_NUMPAD7: xkey = 'w'; break;
1904                case VK_NUMPAD8: xkey = 'x'; break;
1905                case VK_NUMPAD9: xkey = 'y'; break;
1906
1907                case VK_DECIMAL: xkey = 'n'; break;
1908                case VK_ADD:     if(shift_state) xkey = 'm'; 
1909                                 else            xkey = 'l';
1910                                 break;
1911                case VK_RETURN:
1912                                 if (HIWORD(lParam)&KF_EXTENDED)
1913                                     xkey = 'M';
1914                                 break;
1915             }
1916             if(xkey)
1917             {
1918                 if (vt52_mode)
1919                 {
1920                     if (xkey>='P' && xkey<='S')
1921                         p += sprintf((char *)p, "\x1B%c", xkey); 
1922                     else
1923                         p += sprintf((char *)p, "\x1B?%c", xkey); 
1924                 }
1925                 else 
1926                     p += sprintf((char *)p, "\x1BO%c", xkey); 
1927                 return p - output;
1928             }
1929         }
1930
1931         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
1932         {
1933             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1934             return p-output;
1935         }
1936         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
1937         {
1938             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1939         }
1940         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
1941         {
1942             *p++ = 0; return p - output;
1943         }
1944         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
1945         {
1946             *p++ = 160; return p - output;
1947         }
1948         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
1949         {
1950             *p++ = 3; return p - output;
1951         }
1952         /* Control-2 to Control-8 are special */
1953         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1954         {
1955             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1956             return p - output;
1957         }
1958         if (shift_state == 2 && wParam == 0xBD) {
1959             *p++ = 0x1F;
1960             return p - output;
1961         }
1962         if (shift_state == 2 && wParam == 0xDF) {
1963             *p++ = 0x1C;
1964             return p - output;
1965         }
1966         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1967             *p++ = '\r'; *p++ = '\n';
1968             return p - output;
1969         }
1970
1971         /*
1972          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1973          * for integer decimal nn.)
1974          *
1975          * We also deal with the weird ones here. Linux VCs replace F1
1976          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1977          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1978          * respectively.
1979          */
1980         code = 0;
1981         switch (wParam) {
1982           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1983           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1984           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1985           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1986           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1987           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1988           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1989           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1990           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1991           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1992           case VK_F11: code = 23; break;
1993           case VK_F12: code = 24; break;
1994           case VK_F13: code = 25; break;
1995           case VK_F14: code = 26; break;
1996           case VK_F15: code = 28; break;
1997           case VK_F16: code = 29; break;
1998           case VK_F17: code = 31; break;
1999           case VK_F18: code = 32; break;
2000           case VK_F19: code = 33; break;
2001           case VK_F20: code = 34; break;
2002           case VK_HOME: code = 1; break;
2003           case VK_INSERT: code = 2; break;
2004           case VK_DELETE: code = 3; break;
2005           case VK_END: code = 4; break;
2006           case VK_PRIOR: code = 5; break;
2007           case VK_NEXT: code = 6; break;
2008         }
2009         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2010             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2011             return p - output;
2012         }
2013         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2014             p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2015             return p - output;
2016         }
2017         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2018             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2019             return p - output;
2020         }
2021         if (code) {
2022             p += sprintf((char *)p, "\x1B[%d~", code);
2023             return p - output;
2024         }
2025
2026         /*
2027          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2028          * some reason seems to send VK_CLEAR to Windows...).
2029          */
2030         {
2031             char xkey = 0;
2032             switch (wParam) {
2033                 case VK_UP:     xkey = 'A'; break;
2034                 case VK_DOWN:   xkey = 'B'; break;
2035                 case VK_RIGHT:  xkey = 'C'; break;
2036                 case VK_LEFT:   xkey = 'D'; break;
2037                 case VK_CLEAR:  xkey = 'G'; break;
2038             }
2039             if (xkey)
2040             {
2041                 if (vt52_mode)
2042                     p += sprintf((char *)p, "\x1B%c", xkey);
2043                 else if (app_cursor_keys)
2044                     p += sprintf((char *)p, "\x1BO%c", xkey);
2045                 else
2046                     p += sprintf((char *)p, "\x1B[%c", xkey);
2047                 return p - output;
2048             }
2049         }
2050     }
2051
2052     /* Okay we've done everything interesting; let windows deal with 
2053      * the boring stuff */
2054     {
2055         BOOL capsOn=keystate[VK_CAPITAL] !=0;
2056
2057         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2058         if(cfg.xlat_capslockcyr)
2059             keystate[VK_CAPITAL] = 0;
2060
2061         r = ToAscii (wParam, scan, keystate, keys, 0);
2062         if(r>0)
2063         {
2064             p = output;
2065             for(i=0; i<r; i++)
2066             {
2067                 unsigned char ch = (unsigned char)keys[i];
2068
2069                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2070                     compose_char = ch;
2071                     compose_state ++;
2072                     continue;
2073                 }
2074                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2075                     int nc;
2076                     compose_state = 0;
2077
2078                     if ((nc=check_compose(compose_char,ch)) == -1)
2079                     {
2080                         c_write1('\007');
2081                         return 0;
2082                     }
2083                     *p++ = xlat_kbd2tty((unsigned char)nc);
2084                     return p-output;
2085                 }
2086
2087                 compose_state = 0;
2088
2089                 if( left_alt && key_down ) *p++ = '\033';
2090                 if (!key_down)
2091                     *p++ = ch;
2092                 else
2093                 {
2094                     if(capsOn)
2095                         ch = xlat_latkbd2win(ch);
2096                     *p++ = xlat_kbd2tty(ch);
2097                 }
2098             }
2099
2100             /* This is so the ALT-Numpad and dead keys work correctly. */
2101             keys[0] = 0;
2102
2103             return p-output;
2104         }
2105     }
2106
2107     /* This stops ALT press-release doing a 'COMMAND MENU' function */
2108     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
2109     {
2110         keystate[VK_MENU] = 0;
2111         return 0;
2112     }
2113
2114     return -1;
2115 }
2116
2117 void set_title (char *title) {
2118     sfree (window_name);
2119     window_name = smalloc(1+strlen(title));
2120     strcpy (window_name, title);
2121     if (cfg.win_name_always || !IsIconic(hwnd))
2122         SetWindowText (hwnd, title);
2123 }
2124
2125 void set_icon (char *title) {
2126     sfree (icon_name);
2127     icon_name = smalloc(1+strlen(title));
2128     strcpy (icon_name, title);
2129     if (!cfg.win_name_always && IsIconic(hwnd))
2130         SetWindowText (hwnd, title);
2131 }
2132
2133 void set_sbar (int total, int start, int page) {
2134     SCROLLINFO si;
2135
2136     if (!cfg.scrollbar) return;
2137
2138     si.cbSize = sizeof(si);
2139     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2140     si.nMin = 0;
2141     si.nMax = total - 1;
2142     si.nPage = page;
2143     si.nPos = start;
2144     if (hwnd)
2145         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2146 }
2147
2148 Context get_ctx(void) {
2149     HDC hdc;
2150     if (hwnd) {
2151         hdc = GetDC (hwnd);
2152         if (hdc && pal)
2153             SelectPalette (hdc, pal, FALSE);
2154         return hdc;
2155     } else
2156         return NULL;
2157 }
2158
2159 void free_ctx (Context ctx) {
2160     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2161     ReleaseDC (hwnd, ctx);
2162 }
2163
2164 static void real_palette_set (int n, int r, int g, int b) {
2165     if (pal) {
2166         logpal->palPalEntry[n].peRed = r;
2167         logpal->palPalEntry[n].peGreen = g;
2168         logpal->palPalEntry[n].peBlue = b;
2169         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2170         colours[n] = PALETTERGB(r, g, b);
2171         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2172     } else
2173         colours[n] = RGB(r, g, b);
2174 }
2175
2176 void palette_set (int n, int r, int g, int b) {
2177     static const int first[21] = {
2178         0, 2, 4, 6, 8, 10, 12, 14,
2179         1, 3, 5, 7, 9, 11, 13, 15,
2180         16, 17, 18, 20, 22
2181     };
2182     real_palette_set (first[n], r, g, b);
2183     if (first[n] >= 18)
2184         real_palette_set (first[n]+1, r, g, b);
2185     if (pal) {
2186         HDC hdc = get_ctx();
2187         UnrealizeObject (pal);
2188         RealizePalette (hdc);
2189         free_ctx (hdc);
2190     }
2191 }
2192
2193 void palette_reset (void) {
2194     int i;
2195
2196     for (i = 0; i < NCOLOURS; i++) {
2197         if (pal) {
2198             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2199             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2200             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2201             logpal->palPalEntry[i].peFlags = 0;
2202             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2203                                     defpal[i].rgbtGreen,
2204                                     defpal[i].rgbtBlue);
2205         } else
2206             colours[i] = RGB(defpal[i].rgbtRed,
2207                              defpal[i].rgbtGreen,
2208                              defpal[i].rgbtBlue);
2209     }
2210
2211     if (pal) {
2212         HDC hdc;
2213         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2214         hdc = get_ctx();
2215         RealizePalette (hdc);
2216         free_ctx (hdc);
2217     }
2218 }
2219
2220 void write_clip (void *data, int len) {
2221     HGLOBAL clipdata;
2222     void *lock;
2223
2224     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2225     if (!clipdata)
2226         return;
2227     lock = GlobalLock (clipdata);
2228     if (!lock)
2229         return;
2230     memcpy (lock, data, len);
2231     ((unsigned char *) lock) [len] = 0;
2232     GlobalUnlock (clipdata);
2233
2234     SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2235     if (OpenClipboard (hwnd)) {
2236         EmptyClipboard();
2237         SetClipboardData (CF_TEXT, clipdata);
2238         CloseClipboard();
2239     } else
2240         GlobalFree (clipdata);
2241     SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2242 }
2243
2244 void get_clip (void **p, int *len) {
2245     static HGLOBAL clipdata = NULL;
2246
2247     if (!p) {
2248         if (clipdata)
2249             GlobalUnlock (clipdata);
2250         clipdata = NULL;
2251         return;
2252     } else {
2253         if (OpenClipboard (NULL)) {
2254             clipdata = GetClipboardData (CF_TEXT);
2255             CloseClipboard();
2256             if (clipdata) {
2257                 *p = GlobalLock (clipdata);
2258                 if (*p) {
2259                     *len = strlen(*p);
2260                     return;
2261                 }
2262             }
2263         }
2264     }
2265
2266     *p = NULL;
2267     *len = 0;
2268 }
2269
2270 /*
2271  * Move `lines' lines from position `from' to position `to' in the
2272  * window.
2273  */
2274 void optimised_move (int to, int from, int lines) {
2275     RECT r;
2276     int min, max;
2277
2278     min = (to < from ? to : from);
2279     max = to + from - min;
2280
2281     r.left = 0; r.right = cols * font_width;
2282     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2283     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2284 }
2285
2286 /*
2287  * Print a message box and perform a fatal exit.
2288  */
2289 void fatalbox(char *fmt, ...) {
2290     va_list ap;
2291     char stuff[200];
2292
2293     va_start(ap, fmt);
2294     vsprintf(stuff, fmt, ap);
2295     va_end(ap);
2296     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2297     exit(1);
2298 }
2299
2300 /*
2301  * Beep.
2302  */
2303 void beep(int errorbeep) {
2304     static long last_beep = 0;
2305     long now, beep_diff;
2306
2307     now = GetTickCount();
2308     beep_diff = now-last_beep;
2309
2310     /* Make sure we only respond to one beep per packet or so */
2311     if (beep_diff>=0 && beep_diff<50)
2312         return;
2313
2314     if(errorbeep)
2315        MessageBeep(MB_ICONHAND);
2316     else
2317        MessageBeep(MB_OK);
2318
2319     last_beep = GetTickCount();
2320 }