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