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