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