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