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