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