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