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