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