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