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