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