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