]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Avoid rapid-fire resize events during NT opaque drags
[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     static int resizing = FALSE;
948
949     switch (message) {
950       case WM_TIMER:
951         if (pending_netevent)
952             enact_pending_netevent();
953         if (inbuf_head)
954             term_out();
955         term_update();
956         return 0;
957       case WM_CREATE:
958         break;
959       case WM_CLOSE:
960         if (!cfg.warn_on_close || session_closed ||
961             MessageBox(hwnd, "Are you sure you want to close this session?",
962                        "PuTTY Exit Confirmation",
963                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
964             DestroyWindow(hwnd);
965         return 0;
966       case WM_DESTROY:
967         PostQuitMessage (0);
968         return 0;
969       case WM_SYSCOMMAND:
970         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
971           case SC_KEYMENU:
972             if (ignore_keymenu)
973                 return 0;              /* don't put up system menu on Alt */
974             break;
975           case IDM_SHOWLOG:
976             showeventlog(hwnd);
977             break;
978           case IDM_NEWSESS:
979           case IDM_DUPSESS:
980           case IDM_SAVEDSESS:
981             {
982                 char b[2048];
983                 char c[30], *cl;
984                 int freecl = FALSE;
985                 STARTUPINFO si;
986                 PROCESS_INFORMATION pi;
987                 HANDLE filemap = NULL;
988
989                 if (wParam == IDM_DUPSESS) {
990                     /*
991                      * Allocate a file-mapping memory chunk for the
992                      * config structure.
993                      */
994                     SECURITY_ATTRIBUTES sa;
995                     Config *p;
996
997                     sa.nLength = sizeof(sa);
998                     sa.lpSecurityDescriptor = NULL;
999                     sa.bInheritHandle = TRUE;
1000                     filemap = CreateFileMapping((HANDLE)0xFFFFFFFF,
1001                                                 &sa,
1002                                                 PAGE_READWRITE,
1003                                                 0,
1004                                                 sizeof(Config),
1005                                                 NULL);
1006                     if (filemap) {
1007                         p = (Config *)MapViewOfFile(filemap,
1008                                                     FILE_MAP_WRITE,
1009                                                     0, 0, sizeof(Config));
1010                         if (p) {
1011                             *p = cfg;  /* structure copy */
1012                             UnmapViewOfFile(p);
1013                         }
1014                     }
1015                     sprintf(c, "putty &%p", filemap);
1016                     cl = c;
1017                 } else if (wParam == IDM_SAVEDSESS) {
1018                     char *session = sessions[(lParam - IDM_SAVED_MIN) / 16];
1019                     cl = malloc(16 + strlen(session)); /* 8, but play safe */
1020                     if (!cl)
1021                         cl = NULL;     /* not a very important failure mode */
1022                     else {
1023                         sprintf(cl, "putty @%s", session);
1024                         freecl = TRUE;
1025                     }
1026                 } else
1027                     cl = NULL;
1028
1029                 GetModuleFileName (NULL, b, sizeof(b)-1);
1030                 si.cb = sizeof(si);
1031                 si.lpReserved = NULL;
1032                 si.lpDesktop = NULL;
1033                 si.lpTitle = NULL;
1034                 si.dwFlags = 0;
1035                 si.cbReserved2 = 0;
1036                 si.lpReserved2 = NULL;
1037                 CreateProcess (b, cl, NULL, NULL, TRUE,
1038                                NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1039
1040                 if (filemap)
1041                     CloseHandle(filemap);
1042                 if (freecl)
1043                     free(cl);
1044             }
1045             break;
1046           case IDM_RECONF:
1047             if (!do_reconfig(hwnd))
1048                 break;
1049             just_reconfigged = TRUE;
1050             {
1051                 int i;
1052                 for (i=0; i<8; i++)
1053                     if (fonts[i])
1054                         DeleteObject(fonts[i]);
1055             }
1056             bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1057             und_mode = UND_FONT;
1058             init_fonts(0);
1059             sfree(logpal);
1060             /* Telnet will change local echo -> remote if the remote asks */
1061             if (cfg.protocol != PROT_TELNET)
1062                 ldisc = (cfg.ldisc_term ? &ldisc_term : &ldisc_simple);
1063             if (pal)
1064                 DeleteObject(pal);
1065             logpal = NULL;
1066             pal = NULL;
1067             cfgtopalette();
1068             init_palette();
1069
1070             /* Enable or disable the scroll bar, etc */
1071             {
1072                 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1073
1074                 nflg = flag;
1075                 if (cfg.scrollbar) nflg |=  WS_VSCROLL;
1076                 else               nflg &= ~WS_VSCROLL;
1077                 if (cfg.locksize) 
1078                    nflg &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
1079                 else              
1080                    nflg |= (WS_THICKFRAME|WS_MAXIMIZEBOX);
1081
1082                 if (nflg != flag)
1083                 {
1084                     RECT cr, wr;
1085
1086                     SetWindowLong(hwnd, GWL_STYLE, nflg);
1087                     SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1088                     SetWindowPos(hwnd, NULL, 0,0,0,0,
1089                          SWP_NOACTIVATE|SWP_NOCOPYBITS|
1090                          SWP_NOMOVE|SWP_NOSIZE| SWP_NOZORDER|
1091                          SWP_FRAMECHANGED);
1092
1093                     GetWindowRect (hwnd, &wr);
1094                     GetClientRect (hwnd, &cr);
1095                     extra_width = wr.right - wr.left - cr.right + cr.left;
1096                     extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1097                 }
1098             }
1099
1100             term_size(cfg.height, cfg.width, cfg.savelines);
1101             InvalidateRect(hwnd, NULL, TRUE);
1102             SetWindowPos (hwnd, NULL, 0, 0,
1103                           extra_width + font_width * cfg.width,
1104                           extra_height + font_height * cfg.height,
1105                           SWP_NOACTIVATE | SWP_NOCOPYBITS |
1106                           SWP_NOMOVE | SWP_NOZORDER);
1107             if (IsIconic(hwnd)) {
1108                 SetWindowText (hwnd,
1109                                cfg.win_name_always ? window_name : icon_name);
1110             }
1111             break;
1112           case IDM_CLRSB:
1113             term_clrsb();
1114             break;
1115           case IDM_RESET:
1116             term_pwron();
1117             break;
1118           case IDM_TEL_AYT: back->special (TS_AYT); break;
1119           case IDM_TEL_BRK: back->special (TS_BRK); break;
1120           case IDM_TEL_SYNCH: back->special (TS_SYNCH); break;
1121           case IDM_TEL_EC: back->special (TS_EC); break;
1122           case IDM_TEL_EL: back->special (TS_EL); break;
1123           case IDM_TEL_GA: back->special (TS_GA); break;
1124           case IDM_TEL_NOP: back->special (TS_NOP); break;
1125           case IDM_TEL_ABORT: back->special (TS_ABORT); break;
1126           case IDM_TEL_AO: back->special (TS_AO); break;
1127           case IDM_TEL_IP: back->special (TS_IP); break;
1128           case IDM_TEL_SUSP: back->special (TS_SUSP); break;
1129           case IDM_TEL_EOR: back->special (TS_EOR); break;
1130           case IDM_TEL_EOF: back->special (TS_EOF); break;
1131           case IDM_ABOUT:
1132             showabout (hwnd);
1133             break;
1134         default:
1135           if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1136             SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1137           }
1138         }
1139         break;
1140
1141 #define X_POS(l) ((int)(short)LOWORD(l))
1142 #define Y_POS(l) ((int)(short)HIWORD(l))
1143
1144 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1145 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1146
1147       case WM_LBUTTONDOWN:
1148         click (MB_SELECT, TO_CHR_X(X_POS(lParam)),
1149                TO_CHR_Y(Y_POS(lParam)));
1150         SetCapture(hwnd);
1151         return 0;
1152       case WM_LBUTTONUP:
1153         term_mouse (MB_SELECT, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1154                     TO_CHR_Y(Y_POS(lParam)));
1155         ReleaseCapture();
1156         return 0;
1157       case WM_MBUTTONDOWN:
1158         SetCapture(hwnd);
1159         click (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1160                TO_CHR_X(X_POS(lParam)),
1161                TO_CHR_Y(Y_POS(lParam)));
1162         return 0;
1163       case WM_MBUTTONUP:
1164         term_mouse (cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND,
1165                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1166                     TO_CHR_Y(Y_POS(lParam)));
1167         ReleaseCapture();
1168         return 0;
1169       case WM_RBUTTONDOWN:
1170         SetCapture(hwnd);
1171         click (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1172                TO_CHR_X(X_POS(lParam)),
1173                TO_CHR_Y(Y_POS(lParam)));
1174         return 0;
1175       case WM_RBUTTONUP:
1176         term_mouse (cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE,
1177                     MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1178                     TO_CHR_Y(Y_POS(lParam)));
1179         ReleaseCapture();
1180         return 0;
1181       case WM_MOUSEMOVE:
1182         /*
1183          * Add the mouse position and message time to the random
1184          * number noise, if we're using ssh.
1185          */
1186         if (cfg.protocol == PROT_SSH)
1187             noise_ultralight(lParam);
1188
1189         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1190             Mouse_Button b;
1191             if (wParam & MK_LBUTTON)
1192                 b = MB_SELECT;
1193             else if (wParam & MK_MBUTTON)
1194                 b = cfg.mouse_is_xterm ? MB_PASTE : MB_EXTEND;
1195             else
1196                 b = cfg.mouse_is_xterm ? MB_EXTEND : MB_PASTE;
1197             term_mouse (b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1198                         TO_CHR_Y(Y_POS(lParam)));
1199         }
1200         return 0;
1201       case WM_IGNORE_CLIP:
1202         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1203         break;
1204       case WM_IGNORE_KEYMENU:
1205         ignore_keymenu = wParam;       /* do or don't ignore SC_KEYMENU */
1206         break;
1207       case WM_DESTROYCLIPBOARD:
1208         if (!ignore_clip)
1209             term_deselect();
1210         ignore_clip = FALSE;
1211         return 0;
1212       case WM_PAINT:
1213         {
1214             PAINTSTRUCT p;
1215             hdc = BeginPaint (hwnd, &p);
1216             if (pal) {
1217                 SelectPalette (hdc, pal, TRUE);
1218                 RealizePalette (hdc);
1219             }
1220             term_paint (hdc, p.rcPaint.left, p.rcPaint.top,
1221                         p.rcPaint.right, p.rcPaint.bottom);
1222             SelectObject (hdc, GetStockObject(SYSTEM_FONT));
1223             SelectObject (hdc, GetStockObject(WHITE_PEN));
1224             EndPaint (hwnd, &p);
1225         }
1226         return 0;
1227       case WM_NETEVENT:
1228         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1229          * but the only one that's likely to try to overload us is FD_READ.
1230          * This means buffering just one is fine.
1231          */
1232         if (pending_netevent)
1233             enact_pending_netevent();
1234
1235         pending_netevent = TRUE;
1236         pend_netevent_wParam=wParam;
1237         pend_netevent_lParam=lParam;
1238         return 0;
1239       case WM_SETFOCUS:
1240         has_focus = TRUE;
1241         term_out();
1242         term_update();
1243         break;
1244       case WM_KILLFOCUS:
1245         has_focus = FALSE;
1246         term_out();
1247         term_update();
1248         break;
1249       case WM_IGNORE_SIZE:
1250         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1251         break;
1252       case WM_ENTERSIZEMOVE:
1253         EnableSizeTip(1);
1254         resizing = TRUE;
1255         break;
1256       case WM_EXITSIZEMOVE:
1257         EnableSizeTip(0);
1258         resizing = FALSE;
1259         back->size();
1260         break;
1261       case WM_SIZING:
1262         {
1263             int width, height, w, h, ew, eh;
1264             LPRECT r = (LPRECT)lParam;
1265
1266             width = r->right - r->left - extra_width;
1267             height = r->bottom - r->top - extra_height;
1268             w = (width + font_width/2) / font_width; if (w < 1) w = 1;
1269             h = (height + font_height/2) / font_height; if (h < 1) h = 1;
1270             UpdateSizeTip(hwnd, w, h);
1271             ew = width - w * font_width;
1272             eh = height - h * font_height;
1273             if (ew != 0) {
1274                 if (wParam == WMSZ_LEFT ||
1275                     wParam == WMSZ_BOTTOMLEFT ||
1276                     wParam == WMSZ_TOPLEFT)
1277                     r->left += ew;
1278                 else
1279                     r->right -= ew;
1280             }
1281             if (eh != 0) {
1282                 if (wParam == WMSZ_TOP ||
1283                     wParam == WMSZ_TOPRIGHT ||
1284                     wParam == WMSZ_TOPLEFT)
1285                     r->top += eh;
1286                 else
1287                     r->bottom -= eh;
1288             }
1289             if (ew || eh)
1290                 return 1;
1291             else
1292                 return 0;
1293         }
1294         /* break;  (never reached) */
1295       case WM_SIZE:
1296         if (wParam == SIZE_MINIMIZED) {
1297             SetWindowText (hwnd,
1298                            cfg.win_name_always ? window_name : icon_name);
1299             break;
1300         }
1301         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1302             SetWindowText (hwnd, window_name);
1303         if (!ignore_size) {
1304             int width, height, w, h;
1305 #if 0 /* we have fixed this using WM_SIZING now */
1306             int ew, eh;
1307 #endif
1308
1309             width = LOWORD(lParam);
1310             height = HIWORD(lParam);
1311             w = width / font_width; if (w < 1) w = 1;
1312             h = height / font_height; if (h < 1) h = 1;
1313 #if 0 /* we have fixed this using WM_SIZING now */
1314             ew = width - w * font_width;
1315             eh = height - h * font_height;
1316             if (ew != 0 || eh != 0) {
1317                 RECT r;
1318                 GetWindowRect (hwnd, &r);
1319                 SendMessage (hwnd, WM_IGNORE_SIZE, 0, 0);
1320                 SetWindowPos (hwnd, NULL, 0, 0,
1321                               r.right - r.left - ew, r.bottom - r.top - eh,
1322                               SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1323             }
1324 #endif
1325             if (w != cols || h != rows || just_reconfigged) {
1326                 term_invalidate();
1327                 term_size (h, w, cfg.savelines);
1328                 /*
1329                  * Don't call back->size in mid-resize. (To prevent
1330                  * massive numbers of resize events getting sent
1331                  * down the connection during an NT opaque drag.)
1332                  */
1333                 if (!resizing)
1334                     back->size();
1335                 just_reconfigged = FALSE;
1336             }
1337         }
1338         ignore_size = FALSE;
1339         return 0;
1340       case WM_VSCROLL:
1341         switch (LOWORD(wParam)) {
1342           case SB_BOTTOM: term_scroll(-1, 0); break;
1343           case SB_TOP: term_scroll(+1, 0); break;
1344           case SB_LINEDOWN: term_scroll (0, +1); break;
1345           case SB_LINEUP: term_scroll (0, -1); break;
1346           case SB_PAGEDOWN: term_scroll (0, +rows/2); break;
1347           case SB_PAGEUP: term_scroll (0, -rows/2); break;
1348           case SB_THUMBPOSITION: case SB_THUMBTRACK:
1349             term_scroll (1, HIWORD(wParam)); break;
1350         }
1351         break; 
1352      case WM_PALETTECHANGED:
1353         if ((HWND) wParam != hwnd && pal != NULL) {
1354             HDC hdc = get_ctx();
1355             if (hdc) {
1356                 if (RealizePalette (hdc) > 0)
1357                     UpdateColors (hdc);
1358                 free_ctx (hdc);
1359             }
1360         }
1361         break;
1362       case WM_QUERYNEWPALETTE:
1363         if (pal != NULL) {
1364             HDC hdc = get_ctx();
1365             if (hdc) {
1366                 if (RealizePalette (hdc) > 0)
1367                     UpdateColors (hdc);
1368                 free_ctx (hdc);
1369                 return TRUE;
1370             }
1371         }
1372         return FALSE;
1373       case WM_KEYDOWN:
1374       case WM_SYSKEYDOWN:
1375       case WM_KEYUP:
1376       case WM_SYSKEYUP:
1377         /*
1378          * Add the scan code and keypress timing to the random
1379          * number noise, if we're using ssh.
1380          */
1381         if (cfg.protocol == PROT_SSH)
1382             noise_ultralight(lParam);
1383
1384         /*
1385          * We don't do TranslateMessage since it disassociates the
1386          * resulting CHAR message from the KEYDOWN that sparked it,
1387          * which we occasionally don't want. Instead, we process
1388          * KEYDOWN, and call the Win32 translator functions so that
1389          * we get the translations under _our_ control.
1390          */
1391         {
1392             unsigned char buf[20];
1393             int len;
1394
1395             len = TranslateKey (message, wParam, lParam, buf);
1396             if (len == -1)
1397                 return DefWindowProc (hwnd, message, wParam, lParam);
1398             ldisc->send (buf, len);
1399         }
1400         return 0;
1401       case WM_CHAR:
1402       case WM_SYSCHAR:
1403         /*
1404          * Nevertheless, we are prepared to deal with WM_CHAR
1405          * messages, should they crop up. So if someone wants to
1406          * post the things to us as part of a macro manoeuvre,
1407          * we're ready to cope.
1408          */
1409         {
1410             char c = xlat_kbd2tty((unsigned char)wParam);
1411             ldisc->send (&c, 1);
1412         }
1413         return 0;
1414     }
1415
1416     return DefWindowProc (hwnd, message, wParam, lParam);
1417 }
1418
1419 /*
1420  * Draw a line of text in the window, at given character
1421  * coordinates, in given attributes.
1422  *
1423  * We are allowed to fiddle with the contents of `text'.
1424  */
1425 void do_text (Context ctx, int x, int y, char *text, int len,
1426               unsigned long attr, int lattr) {
1427     COLORREF fg, bg, t;
1428     int nfg, nbg, nfont;
1429     HDC hdc = ctx;
1430     RECT line_box;
1431     int force_manual_underline = 0;
1432     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1433     static int *IpDx = 0, IpDxLEN = 0;;
1434
1435     if (len>IpDxLEN || IpDx[0] != fnt_width) {
1436         int i;
1437         if (len>IpDxLEN) {
1438             sfree(IpDx);
1439             IpDx = smalloc((len+16)*sizeof(int));
1440             IpDxLEN = (len+16);
1441         }
1442         for(i=0; i<IpDxLEN; i++)
1443             IpDx[i] = fnt_width;
1444     }
1445
1446     x *= fnt_width;
1447     y *= font_height;
1448
1449     if (attr & ATTR_ACTCURS) {
1450         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1451         attr ^= ATTR_CUR_XOR;
1452     }
1453
1454     nfont = 0;
1455     if (cfg.vtmode == VT_OEMONLY)
1456         nfont |= FONT_OEM;
1457
1458     /*
1459      * Map high-half characters in order to approximate ISO using
1460      * OEM character set. No characters are missing if the OEM codepage
1461      * is CP850.
1462      */
1463     if (nfont & FONT_OEM) {
1464         int i;
1465         for (i=0; i<len; i++)
1466             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1467 #if 0
1468                 /* This is CP850 ... perfect translation */
1469                 static const char oemhighhalf[] =
1470                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1471                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1472                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1473                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1474                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1475                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1476                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1477                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1478                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1479                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1480                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1481                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1482                     ;
1483 #endif
1484                 /* This is CP437 ... junk translation */
1485                 static const unsigned char oemhighhalf[] = {
1486                     0xff, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1487                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1488                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1489                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1490                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1491                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1492                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1493                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1494                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1495                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1496                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1497                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1498                 };
1499
1500                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1501             }
1502     }
1503
1504     if (attr & ATTR_GBCHR) {
1505         int i;
1506         /*
1507          * GB mapping: map # to pound, and everything else stays
1508          * normal.
1509          */
1510         for (i=0; i<len; i++)
1511             if (text[i] == '#')
1512                 text[i] = cfg.vtmode == VT_OEMONLY ? '\x9C' : '\xA3';
1513     } else if (attr & ATTR_LINEDRW) {
1514         int i;
1515         /* ISO 8859-1 */
1516         static const char poorman[] =
1517             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1518
1519         /* CP437 */
1520         static const char oemmap_437[] =
1521             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1522             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1523
1524         /* CP850 */
1525         static const char oemmap_850[] =
1526             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1527             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1528
1529         /* Poor windows font ... eg: windows courier */
1530         static const char oemmap[] =
1531             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1532             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1533
1534         /*
1535          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1536          * VT100 line drawing chars; everything else stays normal.
1537          */
1538         switch (cfg.vtmode) {
1539           case VT_XWINDOWS:
1540             for (i=0; i<len; i++)
1541                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1542                     text[i] += '\x01' - '\x60';
1543             break;
1544           case VT_OEMANSI:
1545             /* Make sure we actually have an OEM font */
1546             if (fonts[nfont|FONT_OEM]) { 
1547           case VT_OEMONLY:
1548                 nfont |= FONT_OEM;
1549                 for (i=0; i<len; i++)
1550                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1551                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1552                 break;
1553             }
1554           case VT_POORMAN:
1555             for (i=0; i<len; i++)
1556                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1557                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1558             break;
1559         }
1560     }
1561
1562     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1563     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1564     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1565         nfont |= FONT_BOLD;
1566     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1567         nfont |= FONT_UNDERLINE;
1568     if (!fonts[nfont]) 
1569     {
1570         if (nfont&FONT_UNDERLINE)
1571             force_manual_underline = 1;
1572         /* Don't do the same for manual bold, it could be bad news. */
1573
1574         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1575     }
1576     if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1577         force_manual_underline = 1;
1578     if (attr & ATTR_REVERSE) {
1579         t = nfg; nfg = nbg; nbg = t;
1580     }
1581     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1582         nfg++;
1583     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1584         nbg++;
1585     fg = colours[nfg];
1586     bg = colours[nbg];
1587     SelectObject (hdc, fonts[nfont]);
1588     SetTextColor (hdc, fg);
1589     SetBkColor (hdc, bg);
1590     SetBkMode (hdc, OPAQUE);
1591     line_box.left   = x;
1592     line_box.top    = y;
1593     line_box.right  = x+fnt_width*len;
1594     line_box.bottom = y+font_height;
1595     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1596     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1597         SetBkMode (hdc, TRANSPARENT);
1598
1599        /* GRR: This draws the character outside it's box and can leave
1600         * 'droppings' even with the clip box! I suppose I could loop it
1601         * one character at a time ... yuk. 
1602         * 
1603         * Or ... I could do a test print with "W", and use +1 or -1 for this
1604         * shift depending on if the leftmost column is blank...
1605         */
1606         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1607     }
1608     if (force_manual_underline || 
1609             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1610         HPEN oldpen;
1611         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1612         MoveToEx (hdc, x, y+descent, NULL);
1613         LineTo (hdc, x+len*fnt_width, y+descent);
1614         oldpen = SelectObject (hdc, oldpen);
1615         DeleteObject (oldpen);
1616     }
1617     if (attr & ATTR_PASCURS) {
1618         POINT pts[5];
1619         HPEN oldpen;
1620         pts[0].x = pts[1].x = pts[4].x = x;
1621         pts[2].x = pts[3].x = x+fnt_width-1;
1622         pts[0].y = pts[3].y = pts[4].y = y;
1623         pts[1].y = pts[2].y = y+font_height-1;
1624         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1625         Polyline (hdc, pts, 5);
1626         oldpen = SelectObject (hdc, oldpen);
1627         DeleteObject (oldpen);
1628     }
1629 }
1630
1631 static int check_compose(int first, int second) {
1632
1633     static char * composetbl[] = {
1634        "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1635        "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1636        "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1637        "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1638        "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1639        "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1640        "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1641        "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1642        "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1643        "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1644        "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1645        "\"uü", "'yý", "htþ", "\"yÿ",
1646     0};
1647
1648     char ** c;
1649     static int recurse = 0;
1650     int nc = -1;
1651
1652     if(0)
1653     {
1654         char buf[256];
1655         char * p;
1656         sprintf(buf, "cc(%d,%d)", first, second);
1657         for(p=buf; *p; p++)
1658             c_write1(*p);
1659     }
1660
1661     for(c=composetbl; *c; c++) {
1662         if( (*c)[0] == first && (*c)[1] == second)
1663         {
1664             return (*c)[2] & 0xFF;
1665         }
1666     }
1667
1668     if(recurse==0)
1669     {
1670         recurse=1;
1671         nc = check_compose(second, first);
1672         if(nc == -1)
1673             nc = check_compose(toupper(first), toupper(second));
1674         if(nc == -1)
1675             nc = check_compose(toupper(second), toupper(first));
1676         recurse=0;
1677     }
1678     return nc;
1679 }
1680
1681
1682 /*
1683  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1684  * codes. Returns number of bytes used or zero to drop the message
1685  * or -1 to forward the message to windows.
1686  */
1687 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output) {
1688     BYTE keystate[256];
1689     int  scan, left_alt = 0, key_down, shift_state;
1690     int  r, i, code;
1691     unsigned char * p = output;
1692
1693 static WORD keys[3];
1694 static int compose_state = 0;
1695 static int compose_char = 0;
1696 static WPARAM compose_key = 0;
1697
1698     r = GetKeyboardState(keystate);
1699     if (!r) memset(keystate, 0, sizeof(keystate));
1700     else
1701     {
1702         /* Note if AltGr was pressed and if it was used as a compose key */
1703         if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
1704         {
1705             keystate[VK_RMENU] = keystate[VK_MENU];
1706             if (!compose_state) compose_key = wParam;
1707         }
1708         if (wParam == VK_APPS && !compose_state)
1709             compose_key = wParam;
1710
1711         if (wParam == compose_key)
1712         {
1713             if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1714                compose_state = 1;
1715             else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
1716                compose_state = 2;
1717             else
1718                compose_state = 0;
1719         }
1720         else if (compose_state==1 && wParam != VK_CONTROL)
1721            compose_state = 0;
1722
1723         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
1724         if ( (cfg.funky_type == 0 || (cfg.funky_type == 1 && app_keypad_keys))
1725               && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
1726
1727             wParam = VK_EXECUTE;
1728
1729             /* UnToggle NUMLock */
1730             if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
1731                 keystate[VK_NUMLOCK] ^= 1;
1732         }
1733
1734         /* And write back the 'adjusted' state */
1735         SetKeyboardState (keystate);
1736     }
1737
1738     /* Disable Auto repeat if required */
1739     if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
1740        return 0;
1741
1742     if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
1743         left_alt = 1;
1744
1745     key_down = ((HIWORD(lParam)&KF_UP)==0);
1746
1747     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii */
1748     if (left_alt && (keystate[VK_CONTROL]&0x80))
1749         keystate[VK_MENU] = 0;
1750
1751     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
1752     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
1753                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
1754
1755     /* 
1756      * Record that we pressed key so the scroll window can be reset, but
1757      * be careful to avoid Shift-UP/Down
1758      */
1759     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
1760         seen_key_event = 1; 
1761     }
1762
1763     /* Make sure we're not pasting */
1764     if (key_down) term_nopaste();
1765
1766     if (compose_state>1 && left_alt) compose_state = 0;
1767
1768     /* Sanitize the number pad if not using a PC NumPad */
1769     if( left_alt || (app_keypad_keys && cfg.funky_type != 2)
1770           || cfg.nethack_keypad || compose_state )
1771     {
1772         if ((HIWORD(lParam)&KF_EXTENDED) == 0)
1773         {
1774             int nParam = 0;
1775             switch(wParam)
1776             {
1777             case VK_INSERT:     nParam = VK_NUMPAD0; break;
1778             case VK_END:        nParam = VK_NUMPAD1; break;
1779             case VK_DOWN:       nParam = VK_NUMPAD2; break;
1780             case VK_NEXT:       nParam = VK_NUMPAD3; break;
1781             case VK_LEFT:       nParam = VK_NUMPAD4; break;
1782             case VK_CLEAR:      nParam = VK_NUMPAD5; break;
1783             case VK_RIGHT:      nParam = VK_NUMPAD6; break;
1784             case VK_HOME:       nParam = VK_NUMPAD7; break;
1785             case VK_UP:         nParam = VK_NUMPAD8; break;
1786             case VK_PRIOR:      nParam = VK_NUMPAD9; break;
1787             case VK_DELETE:     nParam = VK_DECIMAL; break;
1788             }
1789             if (nParam)
1790             {
1791                 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
1792                 wParam = nParam;
1793             }
1794         }
1795     }
1796
1797     /* If a key is pressed and AltGr is not active */
1798     if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
1799     {
1800         /* Okay, prepare for most alts then ...*/
1801         if (left_alt) *p++ = '\033';
1802
1803         /* Lets see if it's a pattern we know all about ... */
1804         if (wParam == VK_PRIOR && shift_state == 1) {
1805             SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
1806             return 0;
1807         }
1808         if (wParam == VK_NEXT && shift_state == 1) {
1809             SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
1810             return 0;
1811         }
1812         if (wParam == VK_INSERT && shift_state == 1) {
1813             term_mouse (MB_PASTE, MA_CLICK, 0, 0);
1814             term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
1815             return 0;
1816         }
1817         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
1818             return -1;
1819         }
1820         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
1821             
1822             SendMessage (hwnd, WM_IGNORE_KEYMENU, FALSE, 0);
1823             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
1824             SendMessage (hwnd, WM_IGNORE_KEYMENU, TRUE, 0);
1825             return -1;
1826         }
1827
1828         /* Nethack keypad */
1829         if (cfg.nethack_keypad && !left_alt) {
1830            switch(wParam) {
1831                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
1832                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
1833                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
1834                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
1835                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
1836                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
1837                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
1838                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
1839                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
1840            }
1841         }
1842
1843         /* Application Keypad */
1844         if (!left_alt) {
1845            int xkey = 0;
1846
1847            if ( cfg.funky_type == 0 ||
1848               ( cfg.funky_type == 1 && app_keypad_keys)) switch(wParam) {
1849                case VK_EXECUTE: if (app_keypad_keys) xkey = 'P'; break;
1850                case VK_DIVIDE:  xkey = 'Q'; break;
1851                case VK_MULTIPLY:xkey = 'R'; break;
1852                case VK_SUBTRACT:xkey = 'S'; break;
1853            }
1854            if(app_keypad_keys) switch(wParam) {
1855                case VK_NUMPAD0: xkey = 'p'; break;
1856                case VK_NUMPAD1: xkey = 'q'; break;
1857                case VK_NUMPAD2: xkey = 'r'; break;
1858                case VK_NUMPAD3: xkey = 's'; break;
1859                case VK_NUMPAD4: xkey = 't'; break;
1860                case VK_NUMPAD5: xkey = 'u'; break;
1861                case VK_NUMPAD6: xkey = 'v'; break;
1862                case VK_NUMPAD7: xkey = 'w'; break;
1863                case VK_NUMPAD8: xkey = 'x'; break;
1864                case VK_NUMPAD9: xkey = 'y'; break;
1865
1866                case VK_DECIMAL: xkey = 'n'; break;
1867                case VK_ADD:     if(shift_state) xkey = 'm'; 
1868                                 else            xkey = 'l';
1869                                 break;
1870                case VK_RETURN:
1871                                 if (HIWORD(lParam)&KF_EXTENDED)
1872                                     xkey = 'M';
1873                                 break;
1874             }
1875             if(xkey)
1876             {
1877                 if (vt52_mode)
1878                 {
1879                     if (xkey>='P' && xkey<='S')
1880                         p += sprintf((char *)p, "\x1B%c", xkey); 
1881                     else
1882                         p += sprintf((char *)p, "\x1B?%c", xkey); 
1883                 }
1884                 else 
1885                     p += sprintf((char *)p, "\x1BO%c", xkey); 
1886                 return p - output;
1887             }
1888         }
1889
1890         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
1891         {
1892             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
1893             return p-output;
1894         }
1895         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
1896         {
1897             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
1898         }
1899         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
1900         {
1901             *p++ = 0; return p - output;
1902         }
1903         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
1904         {
1905             *p++ = 160; return p - output;
1906         }
1907         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
1908         {
1909             *p++ = 3; return p - output;
1910         }
1911         /* Control-2 to Control-8 are special */
1912         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
1913         {
1914             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
1915             return p - output;
1916         }
1917         if (shift_state == 2 && wParam == 0xBD) {
1918             *p++ = 0x1F;
1919             return p - output;
1920         }
1921         if (shift_state == 2 && wParam == 0xDF) {
1922             *p++ = 0x1C;
1923             return p - output;
1924         }
1925         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
1926             *p++ = '\r'; *p++ = '\n';
1927             return p - output;
1928         }
1929
1930         /*
1931          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
1932          * for integer decimal nn.)
1933          *
1934          * We also deal with the weird ones here. Linux VCs replace F1
1935          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
1936          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
1937          * respectively.
1938          */
1939         code = 0;
1940         switch (wParam) {
1941           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
1942           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
1943           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
1944           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
1945           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
1946           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
1947           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
1948           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
1949           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
1950           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
1951           case VK_F11: code = 23; break;
1952           case VK_F12: code = 24; break;
1953           case VK_F13: code = 25; break;
1954           case VK_F14: code = 26; break;
1955           case VK_F15: code = 28; break;
1956           case VK_F16: code = 29; break;
1957           case VK_F17: code = 31; break;
1958           case VK_F18: code = 32; break;
1959           case VK_F19: code = 33; break;
1960           case VK_F20: code = 34; break;
1961           case VK_HOME: code = 1; break;
1962           case VK_INSERT: code = 2; break;
1963           case VK_DELETE: code = 3; break;
1964           case VK_END: code = 4; break;
1965           case VK_PRIOR: code = 5; break;
1966           case VK_NEXT: code = 6; break;
1967         }
1968         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
1969             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
1970             return p - output;
1971         }
1972         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
1973             p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
1974             return p - output;
1975         }
1976         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
1977             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
1978             return p - output;
1979         }
1980         if (code) {
1981             p += sprintf((char *)p, "\x1B[%d~", code);
1982             return p - output;
1983         }
1984
1985         /*
1986          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
1987          * some reason seems to send VK_CLEAR to Windows...).
1988          */
1989         {
1990             char xkey = 0;
1991             switch (wParam) {
1992                 case VK_UP:     xkey = 'A'; break;
1993                 case VK_DOWN:   xkey = 'B'; break;
1994                 case VK_RIGHT:  xkey = 'C'; break;
1995                 case VK_LEFT:   xkey = 'D'; break;
1996                 case VK_CLEAR:  xkey = 'G'; break;
1997             }
1998             if (xkey)
1999             {
2000                 if (vt52_mode)
2001                     p += sprintf((char *)p, "\x1B%c", xkey);
2002                 else if (app_cursor_keys)
2003                     p += sprintf((char *)p, "\x1BO%c", xkey);
2004                 else
2005                     p += sprintf((char *)p, "\x1B[%c", xkey);
2006                 return p - output;
2007             }
2008         }
2009     }
2010
2011     /* Okay we've done everything interesting; let windows deal with 
2012      * the boring stuff */
2013     {
2014         BOOL capsOn=keystate[VK_CAPITAL] !=0;
2015
2016         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2017         if(cfg.xlat_capslockcyr)
2018             keystate[VK_CAPITAL] = 0;
2019
2020         r = ToAscii (wParam, scan, keystate, keys, 0);
2021         if(r>0)
2022         {
2023             p = output;
2024             for(i=0; i<r; i++)
2025             {
2026                 unsigned char ch = (unsigned char)keys[i];
2027
2028                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2029                     compose_char = ch;
2030                     compose_state ++;
2031                     continue;
2032                 }
2033                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2034                     int nc;
2035                     compose_state = 0;
2036
2037                     if ((nc=check_compose(compose_char,ch)) == -1)
2038                     {
2039                         c_write1('\007');
2040                         return 0;
2041                     }
2042                     *p++ = xlat_kbd2tty((unsigned char)nc);
2043                     return p-output;
2044                 }
2045
2046                 compose_state = 0;
2047
2048                 if( left_alt && key_down ) *p++ = '\033';
2049                 if (!key_down)
2050                     *p++ = ch;
2051                 else
2052                 {
2053                     if(capsOn)
2054                         ch = xlat_latkbd2win(ch);
2055                     *p++ = xlat_kbd2tty(ch);
2056                 }
2057             }
2058
2059             /* This is so the ALT-Numpad and dead keys work correctly. */
2060             keys[0] = 0;
2061
2062             return p-output;
2063         }
2064     }
2065
2066     /* This stops ALT press-release doing a 'COMMAND MENU' function */
2067 #if 0
2068     if (message == WM_SYSKEYUP && wParam == VK_MENU) 
2069     {
2070         keystate[VK_MENU] = 0;
2071         return 0;
2072     }
2073 #endif
2074
2075     return -1;
2076 }
2077
2078 void set_title (char *title) {
2079     sfree (window_name);
2080     window_name = smalloc(1+strlen(title));
2081     strcpy (window_name, title);
2082     if (cfg.win_name_always || !IsIconic(hwnd))
2083         SetWindowText (hwnd, title);
2084 }
2085
2086 void set_icon (char *title) {
2087     sfree (icon_name);
2088     icon_name = smalloc(1+strlen(title));
2089     strcpy (icon_name, title);
2090     if (!cfg.win_name_always && IsIconic(hwnd))
2091         SetWindowText (hwnd, title);
2092 }
2093
2094 void set_sbar (int total, int start, int page) {
2095     SCROLLINFO si;
2096
2097     if (!cfg.scrollbar) return;
2098
2099     si.cbSize = sizeof(si);
2100     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2101     si.nMin = 0;
2102     si.nMax = total - 1;
2103     si.nPage = page;
2104     si.nPos = start;
2105     if (hwnd)
2106         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2107 }
2108
2109 Context get_ctx(void) {
2110     HDC hdc;
2111     if (hwnd) {
2112         hdc = GetDC (hwnd);
2113         if (hdc && pal)
2114             SelectPalette (hdc, pal, FALSE);
2115         return hdc;
2116     } else
2117         return NULL;
2118 }
2119
2120 void free_ctx (Context ctx) {
2121     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2122     ReleaseDC (hwnd, ctx);
2123 }
2124
2125 static void real_palette_set (int n, int r, int g, int b) {
2126     if (pal) {
2127         logpal->palPalEntry[n].peRed = r;
2128         logpal->palPalEntry[n].peGreen = g;
2129         logpal->palPalEntry[n].peBlue = b;
2130         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2131         colours[n] = PALETTERGB(r, g, b);
2132         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2133     } else
2134         colours[n] = RGB(r, g, b);
2135 }
2136
2137 void palette_set (int n, int r, int g, int b) {
2138     static const int first[21] = {
2139         0, 2, 4, 6, 8, 10, 12, 14,
2140         1, 3, 5, 7, 9, 11, 13, 15,
2141         16, 17, 18, 20, 22
2142     };
2143     real_palette_set (first[n], r, g, b);
2144     if (first[n] >= 18)
2145         real_palette_set (first[n]+1, r, g, b);
2146     if (pal) {
2147         HDC hdc = get_ctx();
2148         UnrealizeObject (pal);
2149         RealizePalette (hdc);
2150         free_ctx (hdc);
2151     }
2152 }
2153
2154 void palette_reset (void) {
2155     int i;
2156
2157     for (i = 0; i < NCOLOURS; i++) {
2158         if (pal) {
2159             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2160             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2161             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2162             logpal->palPalEntry[i].peFlags = 0;
2163             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2164                                     defpal[i].rgbtGreen,
2165                                     defpal[i].rgbtBlue);
2166         } else
2167             colours[i] = RGB(defpal[i].rgbtRed,
2168                              defpal[i].rgbtGreen,
2169                              defpal[i].rgbtBlue);
2170     }
2171
2172     if (pal) {
2173         HDC hdc;
2174         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2175         hdc = get_ctx();
2176         RealizePalette (hdc);
2177         free_ctx (hdc);
2178     }
2179 }
2180
2181 void write_clip (void *data, int len) {
2182     HGLOBAL clipdata;
2183     void *lock;
2184
2185     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2186     if (!clipdata)
2187         return;
2188     lock = GlobalLock (clipdata);
2189     if (!lock)
2190         return;
2191     memcpy (lock, data, len);
2192     ((unsigned char *) lock) [len] = 0;
2193     GlobalUnlock (clipdata);
2194
2195     SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2196     if (OpenClipboard (hwnd)) {
2197         EmptyClipboard();
2198         SetClipboardData (CF_TEXT, clipdata);
2199         CloseClipboard();
2200     } else
2201         GlobalFree (clipdata);
2202     SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2203 }
2204
2205 void get_clip (void **p, int *len) {
2206     static HGLOBAL clipdata = NULL;
2207
2208     if (!p) {
2209         if (clipdata)
2210             GlobalUnlock (clipdata);
2211         clipdata = NULL;
2212         return;
2213     } else {
2214         if (OpenClipboard (NULL)) {
2215             clipdata = GetClipboardData (CF_TEXT);
2216             CloseClipboard();
2217             if (clipdata) {
2218                 *p = GlobalLock (clipdata);
2219                 if (*p) {
2220                     *len = strlen(*p);
2221                     return;
2222                 }
2223             }
2224         }
2225     }
2226
2227     *p = NULL;
2228     *len = 0;
2229 }
2230
2231 /*
2232  * Move `lines' lines from position `from' to position `to' in the
2233  * window.
2234  */
2235 void optimised_move (int to, int from, int lines) {
2236     RECT r;
2237     int min, max;
2238
2239     min = (to < from ? to : from);
2240     max = to + from - min;
2241
2242     r.left = 0; r.right = cols * font_width;
2243     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2244     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2245 }
2246
2247 /*
2248  * Print a message box and perform a fatal exit.
2249  */
2250 void fatalbox(char *fmt, ...) {
2251     va_list ap;
2252     char stuff[200];
2253
2254     va_start(ap, fmt);
2255     vsprintf(stuff, fmt, ap);
2256     va_end(ap);
2257     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2258     exit(1);
2259 }
2260
2261 /*
2262  * Beep.
2263  */
2264 void beep(int errorbeep) {
2265     static long last_beep = 0;
2266     long now, beep_diff;
2267
2268     now = GetTickCount();
2269     beep_diff = now-last_beep;
2270
2271     /* Make sure we only respond to one beep per packet or so */
2272     if (beep_diff>=0 && beep_diff<50)
2273         return;
2274
2275     if(errorbeep)
2276        MessageBeep(MB_ICONHAND);
2277     else
2278        MessageBeep(MB_OK);
2279
2280     last_beep = GetTickCount();
2281 }