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