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