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