]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Jacob's patch to make passphrase boxes work more sanely
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <mmsystem.h>
5 #ifndef AUTO_WINSOCK
6 #ifdef WINSOCK_TWO
7 #include <winsock2.h>
8 #else
9 #include <winsock.h>
10 #endif
11 #endif
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <time.h>
16
17 #define PUTTY_DO_GLOBALS                       /* actually _define_ globals */
18 #include "putty.h"
19 #include "winstuff.h"
20 #include "storage.h"
21 #include "win_res.h"
22
23 #define IDM_SHOWLOG   0x0010
24 #define IDM_NEWSESS   0x0020
25 #define IDM_DUPSESS   0x0030
26 #define IDM_RECONF    0x0040
27 #define IDM_CLRSB     0x0050
28 #define IDM_RESET     0x0060
29 #define IDM_TEL_AYT   0x0070
30 #define IDM_TEL_BRK   0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC    0x00a0
33 #define IDM_TEL_EL    0x00b0
34 #define IDM_TEL_GA    0x00c0
35 #define IDM_TEL_NOP   0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO    0x00f0
38 #define IDM_TEL_IP    0x0100
39 #define IDM_TEL_SUSP  0x0110
40 #define IDM_TEL_EOR   0x0120
41 #define IDM_TEL_EOF   0x0130
42 #define IDM_ABOUT     0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL   0x0160
45
46 #define IDM_SESSLGP   0x0250  /* log type printable */
47 #define IDM_SESSLGA   0x0260  /* log type all chars */
48 #define IDM_SESSLGE   0x0270  /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
51
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54
55 /* Needed for Chinese support and apparently not always defined. */
56 #ifndef VK_PROCESSKEY
57 #define VK_PROCESSKEY 0xE5
58 #endif
59
60 static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output);
62 static void cfgtopalette(void);
63 static void init_palette(void);
64 static void init_fonts(int);
65
66 static int extra_width, extra_height;
67
68 static int pending_netevent = 0;
69 static WPARAM pend_netevent_wParam = 0;
70 static LPARAM pend_netevent_lParam = 0;
71 static void enact_pending_netevent(void);
72
73 static time_t last_movement = 0;
74
75 #define FONT_NORMAL 0
76 #define FONT_BOLD 1
77 #define FONT_UNDERLINE 2
78 #define FONT_BOLDUND 3
79 #define FONT_OEM 4
80 #define FONT_OEMBOLD 5
81 #define FONT_OEMBOLDUND 6
82 #define FONT_OEMUND 7
83 static HFONT fonts[8];
84 static int font_needs_hand_underlining;
85 static enum {
86     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
87 } bold_mode;
88 static enum {
89     UND_LINE, UND_FONT
90 } und_mode;
91 static int descent;
92
93 #define NCOLOURS 24
94 static COLORREF colours[NCOLOURS];
95 static HPALETTE pal;
96 static LPLOGPALETTE logpal;
97 static RGBTRIPLE defpal[NCOLOURS];
98
99 static HWND hwnd;
100
101 static HBITMAP caretbm;
102
103 static int dbltime, lasttime, lastact;
104 static Mouse_Button lastbtn;
105
106 static char *window_name, *icon_name;
107
108 static int compose_state = 0;
109
110 /* Dummy routine, only required in plink. */
111 void ldisc_update(int echo, int edit) {}
112
113 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) {
114     static char appname[] = "PuTTY";
115     WORD winsock_ver;
116     WSADATA wsadata;
117     WNDCLASS wndclass;
118     MSG msg;
119     int guess_width, guess_height;
120
121     hinst = inst;
122     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
123
124     winsock_ver = MAKEWORD(1, 1);
125     if (WSAStartup(winsock_ver, &wsadata)) {
126         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
127                    MB_OK | MB_ICONEXCLAMATION);
128         return 1;
129     }
130     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
131         MessageBox(NULL, "WinSock version is incompatible with 1.1",
132                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
133         WSACleanup();
134         return 1;
135     }
136     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
137     sk_init();
138
139     InitCommonControls();
140
141     /* Ensure a Maximize setting in Explorer doesn't maximise the
142      * config box. */
143     defuse_showwindow();
144
145     /*
146      * Process the command line.
147      */
148     {
149         char *p;
150
151         default_protocol = DEFAULT_PROTOCOL;
152         default_port = DEFAULT_PORT;
153         cfg.logtype = LGTYP_NONE;
154
155         do_defaults(NULL, &cfg);
156
157         p = cmdline;
158         while (*p && isspace(*p)) p++;
159
160         /*
161          * Process command line options first. Yes, this can be
162          * done better, and it will be as soon as I have the
163          * energy...
164          */
165         while (*p == '-') {
166             char *q = p + strcspn(p, " \t");
167             p++;
168             if (q == p + 3 &&
169                 tolower(p[0]) == 's' &&
170                 tolower(p[1]) == 's' &&
171                 tolower(p[2]) == 'h') {
172                 default_protocol = cfg.protocol = PROT_SSH;
173                 default_port = cfg.port = 22;
174             } else if (q == p + 7 &&
175                 tolower(p[0]) == 'c' &&
176                 tolower(p[1]) == 'l' &&
177                 tolower(p[2]) == 'e' &&
178                 tolower(p[3]) == 'a' &&
179                 tolower(p[4]) == 'n' &&
180                 tolower(p[5]) == 'u' &&
181                 tolower(p[6]) == 'p') {
182                 /*
183                  * `putty -cleanup'. Remove all registry entries
184                  * associated with PuTTY, and also find and delete
185                  * the random seed file.
186                  */
187                 if (MessageBox(NULL,
188                                "This procedure will remove ALL Registry\n"
189                                "entries associated with PuTTY, and will\n"
190                                "also remove the PuTTY random seed file.\n"
191                                "\n"
192                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
193                                "SESSIONS. Are you really sure you want\n"
194                                "to continue?",
195                                "PuTTY Warning",
196                                MB_YESNO | MB_ICONWARNING) == IDYES) {
197                     cleanup_all();
198                 }
199                 exit(0);
200             }
201             p = q + strspn(q, " \t");
202         }
203
204         /*
205          * An initial @ means to activate a saved session.
206          */
207         if (*p == '@') {
208             int i = strlen(p);
209             while (i > 1 && isspace(p[i-1]))
210                 i--;
211             p[i] = '\0';
212             do_defaults (p+1, &cfg);
213             if (!*cfg.host && !do_config()) {
214                 WSACleanup();
215                 return 0;
216             }
217         } else if (*p == '&') {
218             /*
219              * An initial & means we've been given a command line
220              * containing the hex value of a HANDLE for a file
221              * mapping object, which we must then extract as a
222              * config.
223              */
224             HANDLE filemap;
225             Config *cp;
226             if (sscanf(p+1, "%p", &filemap) == 1 &&
227                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
228                                     0, 0, sizeof(Config))) != NULL) {
229                 cfg = *cp;
230                 UnmapViewOfFile(cp);
231                 CloseHandle(filemap);
232             } else if (!do_config()) {
233                 WSACleanup();
234                 return 0;
235             }
236         } else if (*p) {
237             char *q = p;
238             /*
239              * If the hostname starts with "telnet:", set the
240              * protocol to Telnet and process the string as a
241              * Telnet URL.
242              */
243             if (!strncmp(q, "telnet:", 7)) {
244                 char c;
245
246                 q += 7;
247                 if (q[0] == '/' && q[1] == '/')
248                     q += 2;
249                 cfg.protocol = PROT_TELNET;
250                 p = q;
251                 while (*p && *p != ':' && *p != '/') p++;
252                 c = *p;
253                 if (*p)
254                     *p++ = '\0';
255                 if (c == ':')
256                     cfg.port = atoi(p);
257                 else
258                     cfg.port = -1;
259                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
260                 cfg.host[sizeof(cfg.host)-1] = '\0';
261             } else {
262                 while (*p && !isspace(*p)) p++;
263                 if (*p)
264                     *p++ = '\0';
265                 strncpy (cfg.host, q, sizeof(cfg.host)-1);
266                 cfg.host[sizeof(cfg.host)-1] = '\0';
267                 while (*p && isspace(*p)) p++;
268                 if (*p)
269                     cfg.port = atoi(p);
270                 else
271                     cfg.port = -1;
272             }
273         } else {
274             if (!do_config()) {
275                 WSACleanup();
276                 return 0;
277             }
278         }
279
280         /* See if host is of the form user@host */
281         if (cfg.host[0] != '\0') {
282             char *atsign = strchr(cfg.host, '@');
283             /* Make sure we're not overflowing the user field */
284             if (atsign) {
285                 if (atsign-cfg.host < sizeof cfg.username) {
286                     strncpy (cfg.username, cfg.host, atsign-cfg.host);
287                     cfg.username[atsign-cfg.host] = '\0';
288                 }
289                 memmove(cfg.host, atsign+1, 1+strlen(atsign+1));
290             }
291         }
292     }
293
294     /*
295      * Select protocol. This is farmed out into a table in a
296      * separate file to enable an ssh-free variant.
297      */
298     {
299         int i;
300         back = NULL;
301         for (i = 0; backends[i].backend != NULL; i++)
302             if (backends[i].protocol == cfg.protocol) {
303                 back = backends[i].backend;
304                 break;
305             }
306         if (back == NULL) {
307             MessageBox(NULL, "Unsupported protocol number found",
308                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
309             WSACleanup();
310             return 1;
311         }
312     }
313
314     /* Check for invalid Port number (i.e. zero) */
315     if (cfg.port == 0) {
316         MessageBox(NULL, "Invalid Port Number",
317                    "PuTTY Internal Error", MB_OK |MB_ICONEXCLAMATION);
318         WSACleanup();
319         return 1;
320     }
321
322     if (!prev) {
323         wndclass.style         = 0;
324         wndclass.lpfnWndProc   = WndProc;
325         wndclass.cbClsExtra    = 0;
326         wndclass.cbWndExtra    = 0;
327         wndclass.hInstance     = inst;
328         wndclass.hIcon         = LoadIcon (inst,
329                                            MAKEINTRESOURCE(IDI_MAINICON));
330         wndclass.hCursor       = LoadCursor (NULL, IDC_IBEAM);
331         wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
332         wndclass.lpszMenuName  = NULL;
333         wndclass.lpszClassName = appname;
334
335         RegisterClass (&wndclass);
336     }
337
338     hwnd = NULL;
339
340     savelines = cfg.savelines;
341     term_init();
342
343     cfgtopalette();
344
345     /*
346      * Guess some defaults for the window size. This all gets
347      * updated later, so we don't really care too much. However, we
348      * do want the font width/height guesses to correspond to a
349      * large font rather than a small one...
350      */
351     
352     font_width = 10;
353     font_height = 20;
354     extra_width = 25;
355     extra_height = 28;
356     term_size (cfg.height, cfg.width, cfg.savelines);
357     guess_width = extra_width + font_width * cols;
358     guess_height = extra_height + font_height * rows;
359     {
360         RECT r;
361         HWND w = GetDesktopWindow();
362         GetWindowRect (w, &r);
363         if (guess_width > r.right - r.left)
364             guess_width = r.right - r.left;
365         if (guess_height > r.bottom - r.top)
366             guess_height = r.bottom - r.top;
367     }
368
369     {
370         int winmode = WS_OVERLAPPEDWINDOW|WS_VSCROLL;
371         int exwinmode = 0;
372         if (!cfg.scrollbar)  winmode &= ~(WS_VSCROLL);
373         if (cfg.locksize)    winmode &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX);
374         if (cfg.alwaysontop) exwinmode = WS_EX_TOPMOST;
375         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     if (has_focus)
1669         SetCaretPos(x * font_width, y * font_height);
1670 }
1671
1672 /*
1673  * Draw a line of text in the window, at given character
1674  * coordinates, in given attributes.
1675  *
1676  * We are allowed to fiddle with the contents of `text'.
1677  */
1678 void do_text (Context ctx, int x, int y, char *text, int len,
1679               unsigned long attr, int lattr) {
1680     COLORREF fg, bg, t;
1681     int nfg, nbg, nfont;
1682     HDC hdc = ctx;
1683     RECT line_box;
1684     int force_manual_underline = 0;
1685     int fnt_width = font_width*(1+(lattr!=LATTR_NORM));
1686     static int *IpDx = 0, IpDxLEN = 0;;
1687
1688     if (len>IpDxLEN || IpDx[0] != fnt_width) {
1689         int i;
1690         if (len>IpDxLEN) {
1691             sfree(IpDx);
1692             IpDx = smalloc((len+16)*sizeof(int));
1693             IpDxLEN = (len+16);
1694         }
1695         for(i=0; i<IpDxLEN; i++)
1696             IpDx[i] = fnt_width;
1697     }
1698
1699     x *= fnt_width;
1700     y *= font_height;
1701
1702     if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1703         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1704         attr ^= ATTR_CUR_XOR;
1705     }
1706
1707     nfont = 0;
1708     if (cfg.vtmode == VT_OEMONLY)
1709         nfont |= FONT_OEM;
1710
1711     /*
1712      * Map high-half characters in order to approximate ISO using
1713      * OEM character set. No characters are missing if the OEM codepage
1714      * is CP850.
1715      */
1716     if (nfont & FONT_OEM) {
1717         int i;
1718         for (i=0; i<len; i++)
1719             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1720 #if 0
1721                 /* This is CP850 ... perfect translation */
1722                 static const char oemhighhalf[] =
1723                     "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5" /* A0-A7 */
1724                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE" /* A8-AF */
1725                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA" /* B0-B7 */
1726                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8" /* B8-BF */
1727                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80" /* C0-C7 */
1728                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8" /* C8-CF */
1729                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E" /* D0-D7 */
1730                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1" /* D8-DF */
1731                     "\x85\xA0\x83\xC6\x84\x86\x91\x87" /* E0-E7 */
1732                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B" /* E8-EF */
1733                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6" /* F0-F7 */
1734                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98" /* F8-FF */
1735                     ;
1736 #endif
1737                 /* This is CP437 ... junk translation */
1738                 static const unsigned char oemhighhalf[] = {
1739                     0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1740                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1741                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1742                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1743                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1744                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1745                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1746                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1747                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1748                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1749                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1750                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1751                 };
1752
1753                 text[i] = oemhighhalf[(unsigned char)text[i] - 0xA0];
1754             }
1755     }
1756
1757     if (attr & ATTR_LINEDRW) {
1758         int i;
1759         /* ISO 8859-1 */
1760         static const char poorman[] =
1761             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1762
1763         /* CP437 */
1764         static const char oemmap_437[] =
1765             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1766             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1767
1768         /* CP850 */
1769         static const char oemmap_850[] =
1770             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1771             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1772
1773         /* Poor windows font ... eg: windows courier */
1774         static const char oemmap[] =
1775             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1776             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1777
1778         /*
1779          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1780          * VT100 line drawing chars; everything else stays normal.
1781          *
1782          * Actually '_' maps to space too, but that's done before.
1783          */
1784         switch (cfg.vtmode) {
1785           case VT_XWINDOWS:
1786             for (i=0; i<len; i++)
1787                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1788                     text[i] += '\x01' - '\x60';
1789             break;
1790           case VT_OEMANSI:
1791             /* Make sure we actually have an OEM font */
1792             if (fonts[nfont|FONT_OEM]) { 
1793           case VT_OEMONLY:
1794                 nfont |= FONT_OEM;
1795                 for (i=0; i<len; i++)
1796                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1797                         text[i] = oemmap[(unsigned char)text[i] - 0x60];
1798                 break;
1799             }
1800           case VT_POORMAN:
1801             for (i=0; i<len; i++)
1802                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1803                     text[i] = poorman[(unsigned char)text[i] - 0x60];
1804             break;
1805         }
1806     }
1807
1808     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
1809     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
1810     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
1811         nfont |= FONT_BOLD;
1812     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
1813         nfont |= FONT_UNDERLINE;
1814     if (!fonts[nfont]) 
1815     {
1816         if (nfont&FONT_UNDERLINE)
1817             force_manual_underline = 1;
1818         /* Don't do the same for manual bold, it could be bad news. */
1819
1820         nfont &= ~(FONT_BOLD|FONT_UNDERLINE);
1821     }
1822     if (font_needs_hand_underlining && (attr & ATTR_UNDER))
1823         force_manual_underline = 1;
1824     if (attr & ATTR_REVERSE) {
1825         t = nfg; nfg = nbg; nbg = t;
1826     }
1827     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
1828         nfg++;
1829     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
1830         nbg++;
1831     fg = colours[nfg];
1832     bg = colours[nbg];
1833     SelectObject (hdc, fonts[nfont]);
1834     SetTextColor (hdc, fg);
1835     SetBkColor (hdc, bg);
1836     SetBkMode (hdc, OPAQUE);
1837     line_box.left   = x;
1838     line_box.top    = y;
1839     line_box.right  = x+fnt_width*len;
1840     line_box.bottom = y+font_height;
1841     ExtTextOut (hdc, x, y, ETO_CLIPPED|ETO_OPAQUE, &line_box, text, len, IpDx);
1842     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
1843         SetBkMode (hdc, TRANSPARENT);
1844
1845        /* GRR: This draws the character outside it's box and can leave
1846         * 'droppings' even with the clip box! I suppose I could loop it
1847         * one character at a time ... yuk. 
1848         * 
1849         * Or ... I could do a test print with "W", and use +1 or -1 for this
1850         * shift depending on if the leftmost column is blank...
1851         */
1852         ExtTextOut (hdc, x-1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
1853     }
1854     if (force_manual_underline || 
1855             (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
1856         HPEN oldpen;
1857         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, fg));
1858         MoveToEx (hdc, x, y+descent, NULL);
1859         LineTo (hdc, x+len*fnt_width, y+descent);
1860         oldpen = SelectObject (hdc, oldpen);
1861         DeleteObject (oldpen);
1862     }
1863     if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
1864         POINT pts[5];
1865         HPEN oldpen;
1866         pts[0].x = pts[1].x = pts[4].x = x;
1867         pts[2].x = pts[3].x = x+fnt_width-1;
1868         pts[0].y = pts[3].y = pts[4].y = y;
1869         pts[1].y = pts[2].y = y+font_height-1;
1870         oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1871         Polyline (hdc, pts, 5);
1872         oldpen = SelectObject (hdc, oldpen);
1873         DeleteObject (oldpen);
1874     }
1875     if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
1876         int startx, starty, dx, dy, length, i;
1877         if (cfg.cursor_type == 1) {
1878             startx = x; starty = y+descent;
1879             dx = 1; dy = 0; length = fnt_width;
1880         } else {
1881             int xadjust = 0;
1882             if (attr & ATTR_RIGHTCURS)
1883                 xadjust = fnt_width-1;
1884             startx = x+xadjust; starty = y;
1885             dx = 0; dy = 1; length = font_height;
1886         }
1887         if (attr & ATTR_ACTCURS) {
1888             HPEN oldpen;
1889             oldpen = SelectObject (hdc, CreatePen(PS_SOLID, 0, colours[23]));
1890             MoveToEx (hdc, startx, starty, NULL);
1891             LineTo (hdc, startx+dx*length, starty+dy*length);
1892             oldpen = SelectObject (hdc, oldpen);
1893             DeleteObject (oldpen);
1894         } else {
1895             for (i = 0; i < length; i++) {
1896                 if (i % 2 == 0) {
1897                     SetPixel(hdc, startx, starty, colours[23]);
1898                 }
1899                 startx += dx; starty += dy;
1900             }
1901         }
1902     }
1903 }
1904
1905 static int check_compose(int first, int second) {
1906
1907     static char * composetbl[] = {
1908        "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡", "C/¢",
1909        "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§", "S!§",
1910        "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
1911        "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·", ",,¸",
1912        "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ", "^AÂ",
1913        "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
1914        "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
1915        "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
1916        "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ", ",cç",
1917        "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð", "~nñ",
1918        "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú", "^uû",
1919        "\"uü", "'yý", "htþ", "\"yÿ",
1920     0};
1921
1922     char ** c;
1923     static int recurse = 0;
1924     int nc = -1;
1925
1926     for(c=composetbl; *c; c++) {
1927         if( (*c)[0] == first && (*c)[1] == second)
1928         {
1929             return (*c)[2] & 0xFF;
1930         }
1931     }
1932
1933     if(recurse==0)
1934     {
1935         recurse=1;
1936         nc = check_compose(second, first);
1937         if(nc == -1)
1938             nc = check_compose(toupper(first), toupper(second));
1939         if(nc == -1)
1940             nc = check_compose(toupper(second), toupper(first));
1941         recurse=0;
1942     }
1943     return nc;
1944 }
1945
1946
1947 /*
1948  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
1949  * codes. Returns number of bytes used or zero to drop the message
1950  * or -1 to forward the message to windows.
1951  */
1952 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
1953                         unsigned char *output) {
1954     BYTE keystate[256];
1955     int  scan, left_alt = 0, key_down, shift_state;
1956     int  r, i, code;
1957     unsigned char * p = output;
1958     static int alt_state = 0;
1959
1960     HKL kbd_layout = GetKeyboardLayout(0);
1961
1962     static WORD keys[3];
1963     static int compose_char = 0;
1964     static WPARAM compose_key = 0;
1965     
1966     r = GetKeyboardState(keystate);
1967     if (!r) memset(keystate, 0, sizeof(keystate));
1968     else
1969     {
1970 #if 0
1971        {  /* Tell us all about key events */
1972           static BYTE oldstate[256];
1973           static int first = 1;
1974           static int scan;
1975           int ch;
1976           if(first) memcpy(oldstate, keystate, sizeof(oldstate));
1977           first=0;
1978
1979           if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT) {
1980              debug(("+"));
1981           } else if ((HIWORD(lParam)&KF_UP) && scan==(HIWORD(lParam) & 0xFF) ) {
1982              debug((". U"));
1983           } else {
1984              debug((".\n"));
1985              if (wParam >= VK_F1 && wParam <= VK_F20 )
1986                 debug(("K_F%d", wParam+1-VK_F1));
1987              else switch(wParam)
1988              {
1989              case VK_SHIFT:   debug(("SHIFT")); break;
1990              case VK_CONTROL: debug(("CTRL")); break;
1991              case VK_MENU:    debug(("ALT")); break;
1992              default:         debug(("VK_%02x", wParam));
1993              }
1994              if(message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
1995                 debug(("*"));
1996              debug((", S%02x", scan=(HIWORD(lParam) & 0xFF) ));
1997
1998              ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
1999              if (ch>=' ' && ch<='~') debug((", '%c'", ch));
2000              else if (ch)            debug((", $%02x", ch));
2001
2002              if (keys[0]) debug((", KB0=%02x", keys[0]));
2003              if (keys[1]) debug((", KB1=%02x", keys[1]));
2004              if (keys[2]) debug((", KB2=%02x", keys[2]));
2005
2006              if ( (keystate[VK_SHIFT]&0x80)!=0)     debug((", S"));
2007              if ( (keystate[VK_CONTROL]&0x80)!=0)   debug((", C"));
2008              if ( (HIWORD(lParam)&KF_EXTENDED) )    debug((", E"));
2009              if ( (HIWORD(lParam)&KF_UP) )          debug((", U"));
2010           }
2011
2012           if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2013              ;
2014           else if ( (HIWORD(lParam)&KF_UP) ) 
2015              oldstate[wParam&0xFF] ^= 0x80;
2016           else
2017              oldstate[wParam&0xFF] ^= 0x81;
2018
2019           for(ch=0; ch<256; ch++)
2020              if (oldstate[ch] != keystate[ch])
2021                 debug((", M%02x=%02x", ch, keystate[ch]));
2022
2023           memcpy(oldstate, keystate, sizeof(oldstate));
2024        }
2025 #endif
2026
2027         if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED)) {
2028             keystate[VK_RMENU] = keystate[VK_MENU];
2029         }
2030
2031
2032         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2033         if ( (cfg.funky_type == 3 ||
2034               (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2035               && wParam==VK_NUMLOCK && !(keystate[VK_SHIFT]&0x80)) {
2036
2037             wParam = VK_EXECUTE;
2038
2039             /* UnToggle NUMLock */
2040             if ((HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2041                 keystate[VK_NUMLOCK] ^= 1;
2042         }
2043
2044         /* And write back the 'adjusted' state */
2045         SetKeyboardState (keystate);
2046     }
2047
2048     /* Disable Auto repeat if required */
2049     if (repeat_off && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==KF_REPEAT)
2050        return 0;
2051
2052     if ((HIWORD(lParam)&KF_ALTDOWN) && (keystate[VK_RMENU]&0x80) == 0)
2053         left_alt = 1;
2054
2055     key_down = ((HIWORD(lParam)&KF_UP)==0);
2056
2057     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2058     if (left_alt && (keystate[VK_CONTROL]&0x80)) {
2059         if (cfg.ctrlaltkeys)
2060             keystate[VK_MENU] = 0;
2061         else {
2062             keystate[VK_RMENU] = 0x80;
2063             left_alt = 0;
2064         }
2065     }
2066
2067     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2068     shift_state = ((keystate[VK_SHIFT]&0x80)!=0)
2069                 + ((keystate[VK_CONTROL]&0x80)!=0)*2;
2070
2071     /* Note if AltGr was pressed and if it was used as a compose key */
2072     if (!compose_state) {
2073         compose_key = 0x100;
2074         if (cfg.compose_key) {
2075             if (wParam == VK_MENU && (HIWORD(lParam)&KF_EXTENDED))
2076                 compose_key = wParam;
2077         }
2078         if (wParam == VK_APPS)
2079             compose_key = wParam;
2080     }
2081
2082     if (wParam == compose_key)
2083     {
2084         if (compose_state == 0 && (HIWORD(lParam)&(KF_UP|KF_REPEAT))==0)
2085             compose_state = 1;
2086         else if (compose_state == 1 && (HIWORD(lParam)&KF_UP))
2087             compose_state = 2;
2088         else
2089             compose_state = 0;
2090     }
2091     else if (compose_state==1 && wParam != VK_CONTROL)
2092         compose_state = 0;
2093
2094     /* 
2095      * Record that we pressed key so the scroll window can be reset, but
2096      * be careful to avoid Shift-UP/Down
2097      */
2098     if( wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT ) {
2099         seen_key_event = 1; 
2100     }
2101
2102     /* Make sure we're not pasting */
2103     if (key_down) term_nopaste();
2104
2105     if (compose_state>1 && left_alt) compose_state = 0;
2106
2107     /* Sanitize the number pad if not using a PC NumPad */
2108     if( left_alt || (app_keypad_keys && !cfg.no_applic_k
2109                      && cfg.funky_type != 2)
2110         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state )
2111     {
2112         if ((HIWORD(lParam)&KF_EXTENDED) == 0)
2113         {
2114             int nParam = 0;
2115             switch(wParam)
2116             {
2117             case VK_INSERT:     nParam = VK_NUMPAD0; break;
2118             case VK_END:        nParam = VK_NUMPAD1; break;
2119             case VK_DOWN:       nParam = VK_NUMPAD2; break;
2120             case VK_NEXT:       nParam = VK_NUMPAD3; break;
2121             case VK_LEFT:       nParam = VK_NUMPAD4; break;
2122             case VK_CLEAR:      nParam = VK_NUMPAD5; break;
2123             case VK_RIGHT:      nParam = VK_NUMPAD6; break;
2124             case VK_HOME:       nParam = VK_NUMPAD7; break;
2125             case VK_UP:         nParam = VK_NUMPAD8; break;
2126             case VK_PRIOR:      nParam = VK_NUMPAD9; break;
2127             case VK_DELETE:     nParam = VK_DECIMAL; break;
2128             }
2129             if (nParam)
2130             {
2131                 if (keystate[VK_NUMLOCK]&1) shift_state |= 1;
2132                 wParam = nParam;
2133             }
2134         }
2135     }
2136
2137     /* If a key is pressed and AltGr is not active */
2138     if (key_down && (keystate[VK_RMENU]&0x80) == 0 && !compose_state)
2139     {
2140         /* Okay, prepare for most alts then ...*/
2141         if (left_alt) *p++ = '\033';
2142
2143         /* Lets see if it's a pattern we know all about ... */
2144         if (wParam == VK_PRIOR && shift_state == 1) {
2145             SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2146             return 0;
2147         }
2148         if (wParam == VK_NEXT && shift_state == 1) {
2149             SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2150             return 0;
2151         }
2152         if (wParam == VK_INSERT && shift_state == 1) {
2153             term_mouse (MB_PASTE, MA_CLICK, 0, 0);
2154             term_mouse (MB_PASTE, MA_RELEASE, 0, 0);
2155             return 0;
2156         }
2157         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2158             return -1;
2159         }
2160         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2161             alt_state = 0;
2162             PostMessage(hwnd, WM_CHAR, ' ', 0);
2163             SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2164             return -1;
2165         }
2166         /* Control-Numlock for app-keypad mode switch */
2167         if (wParam == VK_PAUSE && shift_state == 2) {
2168             app_keypad_keys ^= 1;
2169             return 0;
2170         }
2171
2172         /* Nethack keypad */
2173         if (cfg.nethack_keypad && !left_alt) {
2174            switch(wParam) {
2175                case VK_NUMPAD1: *p++ = shift_state ? 'B': 'b'; return p-output;
2176                case VK_NUMPAD2: *p++ = shift_state ? 'J': 'j'; return p-output;
2177                case VK_NUMPAD3: *p++ = shift_state ? 'N': 'n'; return p-output;
2178                case VK_NUMPAD4: *p++ = shift_state ? 'H': 'h'; return p-output;
2179                case VK_NUMPAD5: *p++ = shift_state ? '.': '.'; return p-output;
2180                case VK_NUMPAD6: *p++ = shift_state ? 'L': 'l'; return p-output;
2181                case VK_NUMPAD7: *p++ = shift_state ? 'Y': 'y'; return p-output;
2182                case VK_NUMPAD8: *p++ = shift_state ? 'K': 'k'; return p-output;
2183                case VK_NUMPAD9: *p++ = shift_state ? 'U': 'u'; return p-output;
2184            }
2185         }
2186
2187         /* Application Keypad */
2188         if (!left_alt) {
2189            int xkey = 0;
2190
2191            if ( cfg.funky_type == 3 ||
2192               ( cfg.funky_type <= 1 &&
2193                app_keypad_keys && !cfg.no_applic_k)) switch(wParam) {
2194                case VK_EXECUTE: xkey = 'P'; break;
2195                case VK_DIVIDE:  xkey = 'Q'; break;
2196                case VK_MULTIPLY:xkey = 'R'; break;
2197                case VK_SUBTRACT:xkey = 'S'; break;
2198            }
2199            if(app_keypad_keys && !cfg.no_applic_k) switch(wParam) {
2200                case VK_NUMPAD0: xkey = 'p'; break;
2201                case VK_NUMPAD1: xkey = 'q'; break;
2202                case VK_NUMPAD2: xkey = 'r'; break;
2203                case VK_NUMPAD3: xkey = 's'; break;
2204                case VK_NUMPAD4: xkey = 't'; break;
2205                case VK_NUMPAD5: xkey = 'u'; break;
2206                case VK_NUMPAD6: xkey = 'v'; break;
2207                case VK_NUMPAD7: xkey = 'w'; break;
2208                case VK_NUMPAD8: xkey = 'x'; break;
2209                case VK_NUMPAD9: xkey = 'y'; break;
2210
2211                case VK_DECIMAL: xkey = 'n'; break;
2212                case VK_ADD:     if(cfg.funky_type==2) { 
2213                                     if(shift_state) xkey = 'l';
2214                                     else            xkey = 'k';
2215                                 } else if(shift_state)  xkey = 'm'; 
2216                                   else                  xkey = 'l';
2217                                 break;
2218
2219                case VK_DIVIDE:  if(cfg.funky_type==2) xkey = 'o'; break;
2220                case VK_MULTIPLY:if(cfg.funky_type==2) xkey = 'j'; break;
2221                case VK_SUBTRACT:if(cfg.funky_type==2) xkey = 'm'; break;
2222
2223                case VK_RETURN:
2224                                 if (HIWORD(lParam)&KF_EXTENDED)
2225                                     xkey = 'M';
2226                                 break;
2227             }
2228             if(xkey)
2229             {
2230                 if (vt52_mode)
2231                 {
2232                     if (xkey>='P' && xkey<='S')
2233                         p += sprintf((char *)p, "\x1B%c", xkey); 
2234                     else
2235                         p += sprintf((char *)p, "\x1B?%c", xkey); 
2236                 }
2237                 else 
2238                     p += sprintf((char *)p, "\x1BO%c", xkey); 
2239                 return p - output;
2240             }
2241         }
2242
2243         if (wParam == VK_BACK && shift_state == 0 )     /* Backspace */
2244         {
2245             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2246             return p-output;
2247         }
2248         if (wParam == VK_TAB && shift_state == 1 )      /* Shift tab */
2249         {
2250             *p++ = 0x1B; *p++ = '['; *p++ = 'Z'; return p - output;
2251         }
2252         if (wParam == VK_SPACE && shift_state == 2 )    /* Ctrl-Space */
2253         {
2254             *p++ = 0; return p - output;
2255         }
2256         if (wParam == VK_SPACE && shift_state == 3 )    /* Ctrl-Shift-Space */
2257         {
2258             *p++ = 160; return p - output;
2259         }
2260         if (wParam == VK_CANCEL && shift_state == 2 )   /* Ctrl-Break */
2261         {
2262             *p++ = 3; return p - output;
2263         }
2264         if (wParam == VK_PAUSE)                         /* Break/Pause */
2265         {
2266             *p++ = 26; *p++ = 0; return -2;
2267         }
2268         /* Control-2 to Control-8 are special */
2269         if (shift_state == 2 && wParam >= '2' && wParam <= '8')
2270         {
2271             *p++ = "\000\033\034\035\036\037\177"[wParam-'2'];
2272             return p - output;
2273         }
2274         if (shift_state == 2 && wParam == 0xBD) {
2275             *p++ = 0x1F;
2276             return p - output;
2277         }
2278         if (shift_state == 2 && wParam == 0xDF) {
2279             *p++ = 0x1C;
2280             return p - output;
2281         }
2282         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2283             *p++ = '\r'; *p++ = '\n';
2284             return p - output;
2285         }
2286
2287         /*
2288          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2289          * for integer decimal nn.)
2290          *
2291          * We also deal with the weird ones here. Linux VCs replace F1
2292          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2293          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2294          * respectively.
2295          */
2296         code = 0;
2297         switch (wParam) {
2298           case VK_F1: code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11); break;
2299           case VK_F2: code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12); break;
2300           case VK_F3: code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13); break;
2301           case VK_F4: code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14); break;
2302           case VK_F5: code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15); break;
2303           case VK_F6: code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17); break;
2304           case VK_F7: code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18); break;
2305           case VK_F8: code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19); break;
2306           case VK_F9: code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20); break;
2307           case VK_F10: code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21); break;
2308           case VK_F11: code = 23; break;
2309           case VK_F12: code = 24; break;
2310           case VK_F13: code = 25; break;
2311           case VK_F14: code = 26; break;
2312           case VK_F15: code = 28; break;
2313           case VK_F16: code = 29; break;
2314           case VK_F17: code = 31; break;
2315           case VK_F18: code = 32; break;
2316           case VK_F19: code = 33; break;
2317           case VK_F20: code = 34; break;
2318           case VK_HOME: code = 1; break;
2319           case VK_INSERT: code = 2; break;
2320           case VK_DELETE: code = 3; break;
2321           case VK_END: code = 4; break;
2322           case VK_PRIOR: code = 5; break;
2323           case VK_NEXT: code = 6; break;
2324         }
2325         /* Reorder edit keys to physical order */
2326         if (cfg.funky_type == 3 && code <= 6 ) code = "\0\2\1\4\5\3\6"[code];
2327
2328         if (vt52_mode && code > 0 && code <= 6) {
2329             p += sprintf((char *)p, "\x1B%c", " HLMEIG"[code]);
2330             return p - output;
2331         }
2332
2333         if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2334             p += sprintf((char *)p, "\x1B[%c", code + 'M' - 11);
2335             return p - output;
2336         }
2337         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2338             int offt = 0;
2339             if (code>15) offt++; if (code>21) offt++;
2340             if (vt52_mode)
2341                 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11 - offt);
2342             else
2343                 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11 - offt);
2344             return p - output;
2345         }
2346         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2347             p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11);
2348             return p - output;
2349         }
2350         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2351             if (vt52_mode)
2352                 p += sprintf((char *)p, "\x1B%c", code + 'P' - 11);
2353             else
2354                 p += sprintf((char *)p, "\x1BO%c", code + 'P' - 11);
2355             return p - output;
2356         }
2357         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2358             p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw");
2359             return p - output;
2360         }
2361         if (code) {
2362             p += sprintf((char *)p, "\x1B[%d~", code);
2363             return p - output;
2364         }
2365
2366         /*
2367          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2368          * some reason seems to send VK_CLEAR to Windows...).
2369          */
2370         {
2371             char xkey = 0;
2372             switch (wParam) {
2373                 case VK_UP:     xkey = 'A'; break;
2374                 case VK_DOWN:   xkey = 'B'; break;
2375                 case VK_RIGHT:  xkey = 'C'; break;
2376                 case VK_LEFT:   xkey = 'D'; break;
2377                 case VK_CLEAR:  xkey = 'G'; break;
2378             }
2379             if (xkey)
2380             {
2381                 if (vt52_mode)
2382                     p += sprintf((char *)p, "\x1B%c", xkey);
2383                 else if (app_cursor_keys && !cfg.no_applic_c)
2384                     p += sprintf((char *)p, "\x1BO%c", xkey);
2385                 else
2386                     p += sprintf((char *)p, "\x1B[%c", xkey);
2387                 return p - output;
2388             }
2389         }
2390
2391         /*
2392          * Finally, deal with Return ourselves. (Win95 seems to
2393          * foul it up when Alt is pressed, for some reason.)
2394          */
2395         if (wParam == VK_RETURN)       /* Return */
2396         {
2397             *p++ = 0x0D;
2398             return p-output;
2399         }
2400     }
2401
2402     /* Okay we've done everything interesting; let windows deal with 
2403      * the boring stuff */
2404     {
2405         BOOL capsOn=keystate[VK_CAPITAL] !=0;
2406
2407         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2408         if(cfg.xlat_capslockcyr)
2409             keystate[VK_CAPITAL] = 0;
2410
2411         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2412         if(r>0)
2413         {
2414             p = output;
2415             for(i=0; i<r; i++)
2416             {
2417                 unsigned char ch = (unsigned char)keys[i];
2418
2419                 if (compose_state==2 && (ch&0x80) == 0 && ch>' ') {
2420                     compose_char = ch;
2421                     compose_state ++;
2422                     continue;
2423                 }
2424                 if (compose_state==3 && (ch&0x80) == 0 && ch>' ') {
2425                     int nc;
2426                     compose_state = 0;
2427
2428                     if ((nc=check_compose(compose_char,ch)) == -1)
2429                     {
2430                         MessageBeep(MB_ICONHAND);
2431                         return 0;
2432                     }
2433                     *p++ = xlat_kbd2tty((unsigned char)nc);
2434                     return p-output;
2435                 }
2436
2437                 compose_state = 0;
2438
2439                 if( left_alt && key_down ) *p++ = '\033';
2440                 if (!key_down)
2441                     *p++ = ch;
2442                 else
2443                 {
2444                     if(capsOn)
2445                         ch = xlat_latkbd2win(ch);
2446                     *p++ = xlat_kbd2tty(ch);
2447                 }
2448             }
2449
2450             /* This is so the ALT-Numpad and dead keys work correctly. */
2451             keys[0] = 0;
2452
2453             return p-output;
2454         }
2455         /* If we're definitly not building up an ALT-54321 then clear it */
2456         if (!left_alt) keys[0] = 0;
2457     }
2458
2459     /* ALT alone may or may not want to bring up the System menu */
2460     if (wParam == VK_MENU) {
2461         if (cfg.alt_only) {
2462             if (message == WM_SYSKEYDOWN)
2463                 alt_state = 1;
2464             else if (message == WM_SYSKEYUP && alt_state)
2465                 PostMessage(hwnd, WM_CHAR, ' ', 0);
2466             if (message == WM_SYSKEYUP)
2467                 alt_state = 0;
2468         } else
2469             return 0;
2470     }
2471     else alt_state = 0;
2472
2473     return -1;
2474 }
2475
2476 void set_title (char *title) {
2477     sfree (window_name);
2478     window_name = smalloc(1+strlen(title));
2479     strcpy (window_name, title);
2480     if (cfg.win_name_always || !IsIconic(hwnd))
2481         SetWindowText (hwnd, title);
2482 }
2483
2484 void set_icon (char *title) {
2485     sfree (icon_name);
2486     icon_name = smalloc(1+strlen(title));
2487     strcpy (icon_name, title);
2488     if (!cfg.win_name_always && IsIconic(hwnd))
2489         SetWindowText (hwnd, title);
2490 }
2491
2492 void set_sbar (int total, int start, int page) {
2493     SCROLLINFO si;
2494
2495     if (!cfg.scrollbar) return;
2496
2497     si.cbSize = sizeof(si);
2498     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2499     si.nMin = 0;
2500     si.nMax = total - 1;
2501     si.nPage = page;
2502     si.nPos = start;
2503     if (hwnd)
2504         SetScrollInfo (hwnd, SB_VERT, &si, TRUE);
2505 }
2506
2507 Context get_ctx(void) {
2508     HDC hdc;
2509     if (hwnd) {
2510         hdc = GetDC (hwnd);
2511         if (hdc && pal)
2512             SelectPalette (hdc, pal, FALSE);
2513         return hdc;
2514     } else
2515         return NULL;
2516 }
2517
2518 void free_ctx (Context ctx) {
2519     SelectPalette (ctx, GetStockObject (DEFAULT_PALETTE), FALSE);
2520     ReleaseDC (hwnd, ctx);
2521 }
2522
2523 static void real_palette_set (int n, int r, int g, int b) {
2524     if (pal) {
2525         logpal->palPalEntry[n].peRed = r;
2526         logpal->palPalEntry[n].peGreen = g;
2527         logpal->palPalEntry[n].peBlue = b;
2528         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2529         colours[n] = PALETTERGB(r, g, b);
2530         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2531     } else
2532         colours[n] = RGB(r, g, b);
2533 }
2534
2535 void palette_set (int n, int r, int g, int b) {
2536     static const int first[21] = {
2537         0, 2, 4, 6, 8, 10, 12, 14,
2538         1, 3, 5, 7, 9, 11, 13, 15,
2539         16, 17, 18, 20, 22
2540     };
2541     real_palette_set (first[n], r, g, b);
2542     if (first[n] >= 18)
2543         real_palette_set (first[n]+1, r, g, b);
2544     if (pal) {
2545         HDC hdc = get_ctx();
2546         UnrealizeObject (pal);
2547         RealizePalette (hdc);
2548         free_ctx (hdc);
2549     }
2550 }
2551
2552 void palette_reset (void) {
2553     int i;
2554
2555     for (i = 0; i < NCOLOURS; i++) {
2556         if (pal) {
2557             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2558             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2559             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2560             logpal->palPalEntry[i].peFlags = 0;
2561             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2562                                     defpal[i].rgbtGreen,
2563                                     defpal[i].rgbtBlue);
2564         } else
2565             colours[i] = RGB(defpal[i].rgbtRed,
2566                              defpal[i].rgbtGreen,
2567                              defpal[i].rgbtBlue);
2568     }
2569
2570     if (pal) {
2571         HDC hdc;
2572         SetPaletteEntries (pal, 0, NCOLOURS, logpal->palPalEntry);
2573         hdc = get_ctx();
2574         RealizePalette (hdc);
2575         free_ctx (hdc);
2576     }
2577 }
2578
2579 void write_clip (void *data, int len, int must_deselect) {
2580     HGLOBAL clipdata;
2581     void *lock;
2582
2583     clipdata = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2584     if (!clipdata)
2585         return;
2586     lock = GlobalLock (clipdata);
2587     if (!lock)
2588         return;
2589     memcpy (lock, data, len);
2590     ((unsigned char *) lock) [len] = 0;
2591     GlobalUnlock (clipdata);
2592
2593     if (!must_deselect)
2594         SendMessage (hwnd, WM_IGNORE_CLIP, TRUE, 0);
2595
2596     if (OpenClipboard (hwnd)) {
2597         EmptyClipboard();
2598         SetClipboardData (CF_TEXT, clipdata);
2599         CloseClipboard();
2600     } else
2601         GlobalFree (clipdata);
2602
2603     if (!must_deselect)
2604         SendMessage (hwnd, WM_IGNORE_CLIP, FALSE, 0);
2605 }
2606
2607 void get_clip (void **p, int *len) {
2608     static HGLOBAL clipdata = NULL;
2609
2610     if (!p) {
2611         if (clipdata)
2612             GlobalUnlock (clipdata);
2613         clipdata = NULL;
2614         return;
2615     } else {
2616         if (OpenClipboard (NULL)) {
2617             clipdata = GetClipboardData (CF_TEXT);
2618             CloseClipboard();
2619             if (clipdata) {
2620                 *p = GlobalLock (clipdata);
2621                 if (*p) {
2622                     *len = strlen(*p);
2623                     return;
2624                 }
2625             }
2626         }
2627     }
2628
2629     *p = NULL;
2630     *len = 0;
2631 }
2632
2633 /*
2634  * Move `lines' lines from position `from' to position `to' in the
2635  * window.
2636  */
2637 void optimised_move (int to, int from, int lines) {
2638     RECT r;
2639     int min, max;
2640
2641     min = (to < from ? to : from);
2642     max = to + from - min;
2643
2644     r.left = 0; r.right = cols * font_width;
2645     r.top = min * font_height; r.bottom = (max+lines) * font_height;
2646     ScrollWindow (hwnd, 0, (to - from) * font_height, &r, &r);
2647 }
2648
2649 /*
2650  * Print a message box and perform a fatal exit.
2651  */
2652 void fatalbox(char *fmt, ...) {
2653     va_list ap;
2654     char stuff[200];
2655
2656     va_start(ap, fmt);
2657     vsprintf(stuff, fmt, ap);
2658     va_end(ap);
2659     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
2660     exit(1);
2661 }
2662
2663 /*
2664  * Beep.
2665  */
2666 void beep(int mode) {
2667     if (mode == BELL_DEFAULT) {
2668         /*
2669          * For MessageBeep style bells, we want to be careful of
2670          * timing, because they don't have the nice property of
2671          * PlaySound bells that each one cancels the previous
2672          * active one. So we limit the rate to one per 50ms or so.
2673          */
2674         static long lastbeep = 0;
2675         long now, beepdiff;
2676
2677         now = GetTickCount();
2678         beepdiff = now - lastbeep;
2679         if (beepdiff >= 0 && beepdiff < 50)
2680             return;
2681         MessageBeep(MB_OK);
2682         lastbeep = now;
2683     } else if (mode == BELL_WAVEFILE) {
2684         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
2685             char buf[sizeof(cfg.bell_wavefile)+80];
2686             sprintf(buf, "Unable to play sound file\n%s\n"
2687                     "Using default sound instead", cfg.bell_wavefile);
2688             MessageBox(hwnd, buf, "PuTTY Sound Error", MB_OK | MB_ICONEXCLAMATION);
2689             cfg.beep = BELL_DEFAULT;
2690         }
2691     }
2692 }