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