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