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