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