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