]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
9a36562636a59a3203f2950049f0dcdcfbfe6ad5
[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 (wParam == VK_INSERT && shift_state == 1) {
1803             term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1804             term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1805             return 0;
1806         }
1807         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1808             return -1;
1809         }
1810         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1811             
1812             SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
1813             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1814             SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0);
1815             return -1;
1816         }
1817
1818         /* Nethack keypad */
1819         if (cfg.nethack_keypad && !left_alt) {
1820            switch(wParam) {
1821                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1822                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1823                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1824                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1825                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1826                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1827                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1828                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1829                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1830            }
1831         }
1832
1833         /* Application Keypad */
1834         if (!left_alt) {
1835            int xkey = 0;
1836
1837            if ( cfg.funky_type == 0 ||
1838               ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1839                case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1840                case VK_DIVIDE:  xkey = 'Q'; break;
1841                case VK_MULTIPLY:xkey = 'R'; break;
1842                case VK_SUBTRACT:xkey = 'S'; break;
1843            }
1844            if(app_keypad_keys) switch(wParam) {
1845                case VK_NUMPAD0: xkey = 'p'; break;
1846                case VK_NUMPAD1: xkey = 'q'; break;
1847                case VK_NUMPAD2: xkey = 'r'; break;
1848                case VK_NUMPAD3: xkey = 's'; break;
1849                case VK_NUMPAD4: xkey = 't'; break;
1850                case VK_NUMPAD5: xkey = 'u'; break;
1851                case VK_NUMPAD6: xkey = 'v'; break;
1852                case VK_NUMPAD7: xkey = 'w'; break;
1853                case VK_NUMPAD8: xkey = 'x'; break;
1854                case VK_NUMPAD9: xkey = 'y'; break;
1855
1856                case VK_DECIMAL: xkey = 'n'; break;
1857                case VK_ADD:     if(shift_state) xkey = 'm'; 
1858                                 else            xkey = 'l';
1859                                 break;
1860                case VK_RETURN:
1861                                 if (HIWORD(lParam)&KF_EXTENDED)
1862                                     xkey = 'M';
1863                                 break;
1864             }
1865             if(xkey)
1866             {
1867                 if (vt52_mode)
1868                 {
1869                     if (xkey>='P' && xkey<='S')
1870                         p += sprintf((char *)p, "\x1B%c", xkey); 
1871                     else
1872                         p += sprintf((char *)p, "\x1B?%c", xkey); 
1873                 }
1874                 else 
1875                     p += sprintf((char *)p, "\x1BO%c", xkey); 
1876                 return p - output;
1877             }
1878         }
1879
1880         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
1881         {
1882             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1883             return p-output;
1884         }
1885         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
1886         {
1887             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1888         }
1889         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
1890         {
1891             *p++ = 0; return p - output;
1892         }
1893         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
1894         {
1895             *p++ = 160; return p - output;
1896         }
1897         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
1898         {
1899             *p++ = 3; return p - output;
1900         }
1901         /* Control-2 to Control-8 are special */
1902         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1903         {
1904             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1905             return p - output;
1906         }
1907         if (shift_state == 2 && wParam == 0xBD) {
1908             *p++ = 0x1F;
1909             return p - output;
1910         }
1911         if (shift_state == 2 && wParam == 0xDF) {
1912             *p++ = 0x1C;
1913             return p - output;
1914         }
1915         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1916             *p++ = '\r'; *p++ = '\n';
1917             return p - output;
1918         }
1919
1920         /*
1921          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1922          * for integer decimal nn.)
1923          *
1924          * We also deal with the weird ones here. Linux VCs replace F1
1925          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1926          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1927          * respectively.
1928          */
1929         code = 0;
1930         switch (wParam) {
1931           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1932           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1933           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1934           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1935           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1936           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1937           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1938           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1939           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1940           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1941           case VK_F11: code = 23; break;
1942           case VK_F12: code = 24; break;
1943           case VK_F13: code = 25; break;
1944           case VK_F14: code = 26; break;
1945           case VK_F15: code = 28; break;
1946           case VK_F16: code = 29; break;
1947           case VK_F17: code = 31; break;
1948           case VK_F18: code = 32; break;
1949           case VK_F19: code = 33; break;
1950           case VK_F20: code = 34; break;
1951           case VK_HOME: code = 1; break;
1952           case VK_INSERT: code = 2; break;
1953           case VK_DELETE: code = 3; break;
1954           case VK_END: code = 4; break;
1955           case VK_PRIOR: code = 5; break;
1956           case VK_NEXT: code = 6; break;
1957         }
1958         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1959             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1960             return p - output;
1961         }
1962         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1963             p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1964             return p - output;
1965         }
1966         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1967             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1968             return p - output;
1969         }
1970         if (code) {
1971             p += sprintf((char *)p, "\x1B[%d~", code);
1972             return p - output;
1973         }
1974
1975         /*
1976          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1977          * some reason seems to send VK_CLEAR to Windows...).
1978          */
1979         {
1980             char xkey = 0;
1981             switch (wParam) {
1982                 case VK_UP:     xkey = 'A'; break;
1983                 case VK_DOWN:   xkey = 'B'; break;
1984                 case VK_RIGHT:  xkey = 'C'; break;
1985                 case VK_LEFT:   xkey = 'D'; break;
1986                 case VK_CLEAR:  xkey = 'G'; break;
1987             }
1988             if (xkey)
1989             {
1990                 if (vt52_mode)
1991                     p += sprintf((char *)p, "\x1B%c", xkey);
1992                 else if (app_cursor_keys)
1993                     p += sprintf((char *)p, "\x1BO%c", xkey);
1994                 else
1995                     p += sprintf((char *)p, "\x1B[%c", xkey);
1996                 return p - output;
1997             }
1998         }
1999     }
2000
2001     /* Okay we've done everything interesting; let windows deal with 
2002      * the boring stuff */
2003     {
2004         BOOL capsOn=keystate[VK_CAPITAL] !=0;
2005
2006         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2007         if(cfg.xlat_capslockcyr)
2008             keystate[VK_CAPITAL] = 0;
2009
2010         r = ToAscii (wParam, scan, keystate, keys, 0);
2011         if(r>0)
2012         {
2013             p = output;
2014             for(i=0; i<r; i++)
2015             {
2016                 unsigned char ch = (unsigned char)keys[i];
2017
2018                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2019                     compose_char = ch;
2020                     compose_state ++;
2021                     continue;
2022                 }
2023                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2024                     int nc;
2025                     compose_state = 0;
2026
2027                     if ((nc=check_compose(compose_char,ch)) == -1)
2028                     {
2029                         c_write1('\007');
2030                         return 0;
2031                     }
2032                     *p++ = xlat_kbd2tty((unsigned char)nc);
2033                     return p-output;
2034                 }
2035
2036                 compose_state = 0;
2037
2038                 if( left_alt && key_down ) *p++ = '\033';
2039                 if (!key_down)
2040                     *p++ = ch;
2041                 else
2042                 {
2043                     if(capsOn)
2044                         ch = xlat_latkbd2win(ch);
2045                     *p++ = xlat_kbd2tty(ch);
2046                 }
2047             }
2048
2049             /* This is so the ALT-Numpad and dead keys work correctly. */
2050             keys[0] = 0;
2051
2052             return p-output;
2053         }
2054     }
2055
2056     /* This stops ALT press-release doing a 'COMMAND MENU' function */
2057 #if 0
2058     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
2059     {
2060         keystate[VK_MENU] = 0;
2061         return 0;
2062     }
2063 #endif
2064
2065     return -1;
2066 }
2067
2068 void set_title (char *title) {
2069     sfree (window_name);
2070     window_name = smalloc(1+strlen(title));
2071     strcpy (window_name, title);
2072     if (cfg.win_name_always || !IsIconic(hwnd))
2073         SetWindowText (hwnd, title);
2074 }
2075
2076 void set_icon (char *title) {
2077     sfree (icon_name);
2078     icon_name = smalloc(1+strlen(title));
2079     strcpy (icon_name, title);
2080     if (!cfg.win_name_always && IsIconic(hwnd))
2081         SetWindowText (hwnd, title);
2082 }
2083
2084 void set_sbar (int total, int start, int page) {
2085     SCROLLINFO si;
2086
2087     if (!cfg.scrollbar) return;
2088
2089     si.cbSize = sizeof(si);
2090     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2091     si.nMin = 0;
2092     si.nMax = total - 1;
2093     si.nPage = page;
2094     si.nPos = start;
2095     if (hwnd)
2096         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2097 }
2098
2099 Context get_ctx(void) {
2100     HDC hdc;
2101     if (hwnd) {
2102         hdc = GetDC (hwnd);
2103         if (hdc && pal)
2104             SelectPalette (hdc, pal, FALSE);
2105         return hdc;
2106     } else
2107         return NULL;
2108 }
2109
2110 void free_ctx (Context ctx) {
2111     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2112     ReleaseDC (hwnd, ctx);
2113 }
2114
2115 static void real_palette_set (int n, int r, int g, int b) {
2116     if (pal) {
2117         logpal->palPalEntry[n].peRed = r;
2118         logpal->palPalEntry[n].peGreen = g;
2119         logpal->palPalEntry[n].peBlue = b;
2120         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2121         colours[n] = PALETTERGB(r, g, b);
2122         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2123     } else
2124         colours[n] = RGB(r, g, b);
2125 }
2126
2127 void palette_set (int n, int r, int g, int b) {
2128     static const int first[21] = {
2129         0, 2, 4, 6, 8, 10, 12, 14,
2130         1, 3, 5, 7, 9, 11, 13, 15,
2131         16, 17, 18, 20, 22
2132     };
2133     real_palette_set (first[n], r, g, b);
2134     if (first[n] >= 18)
2135         real_palette_set (first[n]+1, r, g, b);
2136     if (pal) {
2137         HDC hdc = get_ctx();
2138         UnrealizeObject (pal);
2139         RealizePalette (hdc);
2140         free_ctx (hdc);
2141     }
2142 }
2143
2144 void palette_reset (void) {
2145     int i;
2146
2147     for (i = 0; i < NCOLOURS; i++) {
2148         if (pal) {
2149             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2150             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2151             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2152             logpal->palPalEntry[i].peFlags = 0;
2153             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2154                                     defpal[i].rgbtGreen,
2155                                     defpal[i].rgbtBlue);
2156         } else
2157             colours[i] = RGB(defpal[i].rgbtRed,
2158                              defpal[i].rgbtGreen,
2159                              defpal[i].rgbtBlue);
2160     }
2161
2162     if (pal) {
2163         HDC hdc;
2164         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2165         hdc = get_ctx();
2166         RealizePalette (hdc);
2167         free_ctx (hdc);
2168     }
2169 }
2170
2171 void write_clip (void *data, int len) {
2172     HGLOBAL clipdata;
2173     void *lock;
2174
2175     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2176     if (!clipdata)
2177         return;
2178     lock = GlobalLock (clipdata);
2179     if (!lock)
2180         return;
2181     memcpy (lock, data, len);
2182     ((unsigned char *) lock) [len] = 0;
2183     GlobalUnlock (clipdata);
2184
2185     SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2186     if (OpenClipboard (hwnd)) {
2187         EmptyClipboard();
2188         SetClipboardData (CF_TEXT, clipdata);
2189         CloseClipboard();
2190     } else
2191         GlobalFree (clipdata);
2192     SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2193 }
2194
2195 void get_clip (void **p, int *len) {
2196     static HGLOBAL clipdata = NULL;
2197
2198     if (!p) {
2199         if (clipdata)
2200             GlobalUnlock (clipdata);
2201         clipdata = NULL;
2202         return;
2203     } else {
2204         if (OpenClipboard (NULL)) {
2205             clipdata = GetClipboardData (CF_TEXT);
2206             CloseClipboard();
2207             if (clipdata) {
2208                 *p = GlobalLock (clipdata);
2209                 if (*p) {
2210                     *len = strlen(*p);
2211                     return;
2212                 }
2213             }
2214         }
2215     }
2216
2217     *p = NULL;
2218     *len = 0;
2219 }
2220
2221 /*
2222  * Move `lines' lines from position `from' to position `to' in the
2223  * window.
2224  */
2225 void optimised_move (int to, int from, int lines) {
2226     RECT r;
2227     int min, max;
2228
2229     min = (to < from ? to : from);
2230     max = to + from - min;
2231
2232     r.left = 0; r.right = cols * font_width;
2233     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2234     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2235 }
2236
2237 /*
2238  * Print a message box and perform a fatal exit.
2239  */
2240 void fatalbox(char *fmt, ...) {
2241     va_list ap;
2242     char stuff[200];
2243
2244     va_start(ap, fmt);
2245     vsprintf(stuff, fmt, ap);
2246     va_end(ap);
2247     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2248     exit(1);
2249 }
2250
2251 /*
2252  * Beep.
2253  */
2254 void beep(int errorbeep) {
2255     static long last_beep = 0;
2256     long now, beep_diff;
2257
2258     now = GetTickCount();
2259     beep_diff = now-last_beep;
2260
2261     /* Make sure we only respond to one beep per packet or so */
2262     if (beep_diff>=0 && beep_diff<50)
2263         return;
2264
2265     if(errorbeep)
2266        MessageBeep(MB_ICONHAND);
2267     else
2268        MessageBeep(MB_OK);
2269
2270     last_beep = GetTickCount();
2271 }