]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Retired the #ifdef DUMP_PACKETS stuff in ssh.c because I'm utterly
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <richedit.h>
5 #include <mmsystem.h>
6 #ifndef AUTO_WINSOCK
7 #ifdef WINSOCK_TWO
8 #include <winsock2.h>
9 #else
10 #include <winsock.h>
11 #endif
12 #endif
13
14 #if WINVER < 0x0500
15 #define COMPILE_MULTIMON_STUBS
16 #include <multimon.h>
17 #endif
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <ctype.h>
22 #include <time.h>
23 #include <assert.h>
24
25 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
26 #include "putty.h"
27 #include "winstuff.h"
28 #include "storage.h"
29 #include "win_res.h"
30
31 #define IDM_SHOWLOG   0x0010
32 #define IDM_NEWSESS   0x0020
33 #define IDM_DUPSESS   0x0030
34 #define IDM_RECONF    0x0040
35 #define IDM_CLRSB     0x0050
36 #define IDM_RESET     0x0060
37 #define IDM_TEL_AYT   0x0070
38 #define IDM_TEL_BRK   0x0080
39 #define IDM_TEL_SYNCH 0x0090
40 #define IDM_TEL_EC    0x00a0
41 #define IDM_TEL_EL    0x00b0
42 #define IDM_TEL_GA    0x00c0
43 #define IDM_TEL_NOP   0x00d0
44 #define IDM_TEL_ABORT 0x00e0
45 #define IDM_TEL_AO    0x00f0
46 #define IDM_TEL_IP    0x0100
47 #define IDM_TEL_SUSP  0x0110
48 #define IDM_TEL_EOR   0x0120
49 #define IDM_TEL_EOF   0x0130
50 #define IDM_HELP      0x0140
51 #define IDM_ABOUT     0x0150
52 #define IDM_SAVEDSESS 0x0160
53 #define IDM_COPYALL   0x0170
54 #define IDM_FULLSCREEN  0x0180
55
56 #define IDM_SESSLGP   0x0250           /* log type printable */
57 #define IDM_SESSLGA   0x0260           /* log type all chars */
58 #define IDM_SESSLGE   0x0270           /* log end */
59 #define IDM_SAVED_MIN 0x1000
60 #define IDM_SAVED_MAX 0x2000
61
62 #define WM_IGNORE_CLIP (WM_XUSER + 2)
63 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
64
65 /* Needed for Chinese support and apparently not always defined. */
66 #ifndef VK_PROCESSKEY
67 #define VK_PROCESSKEY 0xE5
68 #endif
69
70 /* Needed for mouse wheel support and not defined in earlier SDKs. */
71 #ifndef WM_MOUSEWHEEL
72 #define WM_MOUSEWHEEL 0x020A
73 #endif
74
75 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
76 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
77                         unsigned char *output);
78 static void cfgtopalette(void);
79 static void init_palette(void);
80 static void init_fonts(int, int);
81 static void another_font(int);
82 static void deinit_fonts(void);
83 static void set_input_locale(HKL);
84
85 static int is_full_screen(void);
86 static void make_full_screen(void);
87 static void clear_full_screen(void);
88 static void flip_full_screen(void);
89
90 /* Window layout information */
91 static void reset_window(int);
92 static int extra_width, extra_height;
93 static int font_width, font_height, font_dualwidth;
94 static int offset_width, offset_height;
95 static int was_zoomed = 0;
96 static int prev_rows, prev_cols;
97   
98 static int pending_netevent = 0;
99 static WPARAM pend_netevent_wParam = 0;
100 static LPARAM pend_netevent_lParam = 0;
101 static void enact_pending_netevent(void);
102 static void flash_window(int mode);
103
104 static time_t last_movement = 0;
105
106 #define FONT_NORMAL 0
107 #define FONT_BOLD 1
108 #define FONT_UNDERLINE 2
109 #define FONT_BOLDUND 3
110 #define FONT_WIDE       0x04
111 #define FONT_HIGH       0x08
112 #define FONT_NARROW     0x10
113
114 #define FONT_OEM        0x20
115 #define FONT_OEMBOLD    0x21
116 #define FONT_OEMUND     0x22
117 #define FONT_OEMBOLDUND 0x23
118
119 #define FONT_MAXNO      0x2F
120 #define FONT_SHIFT      5
121 static HFONT fonts[FONT_MAXNO];
122 static LOGFONT lfont;
123 static int fontflag[FONT_MAXNO];
124 static enum {
125     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
126 } bold_mode;
127 static enum {
128     UND_LINE, UND_FONT
129 } und_mode;
130 static int descent;
131
132 #define NCOLOURS 24
133 static COLORREF colours[NCOLOURS];
134 static HPALETTE pal;
135 static LPLOGPALETTE logpal;
136 static RGBTRIPLE defpal[NCOLOURS];
137
138 static HWND hwnd;
139
140 static HBITMAP caretbm;
141
142 static int dbltime, lasttime, lastact;
143 static Mouse_Button lastbtn;
144
145 /* this allows xterm-style mouse handling. */
146 static int send_raw_mouse = 0;
147 static int wheel_accumulator = 0;
148
149 static char *window_name, *icon_name;
150
151 static int compose_state = 0;
152
153 static OSVERSIONINFO osVersion;
154
155 /* Dummy routine, only required in plink. */
156 void ldisc_update(int echo, int edit)
157 {
158 }
159
160 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
161 {
162     static char appname[] = "PuTTY";
163     WORD winsock_ver;
164     WSADATA wsadata;
165     WNDCLASS wndclass;
166     MSG msg;
167     int guess_width, guess_height;
168
169     hinst = inst;
170     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
171
172     winsock_ver = MAKEWORD(1, 1);
173     if (WSAStartup(winsock_ver, &wsadata)) {
174         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
175                    MB_OK | MB_ICONEXCLAMATION);
176         return 1;
177     }
178     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
179         MessageBox(NULL, "WinSock version is incompatible with 1.1",
180                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
181         WSACleanup();
182         return 1;
183     }
184     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
185     sk_init();
186
187     InitCommonControls();
188
189     /* Ensure a Maximize setting in Explorer doesn't maximise the
190      * config box. */
191     defuse_showwindow();
192
193     {
194         ZeroMemory(&osVersion, sizeof(osVersion));
195         osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
196         if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
197             MessageBox(NULL, "Windows refuses to report a version",
198                        "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
199             return 1;
200         }
201     }
202
203     /*
204      * See if we can find our Help file.
205      */
206     {
207         char b[2048], *p, *q, *r;
208         FILE *fp;
209         GetModuleFileName(NULL, b, sizeof(b) - 1);
210         r = b;
211         p = strrchr(b, '\\');
212         if (p && p >= r) r = p+1;
213         q = strrchr(b, ':');
214         if (q && q >= r) r = q+1;
215         strcpy(r, "putty.hlp");
216         if ( (fp = fopen(b, "r")) != NULL) {
217             help_path = dupstr(b);
218             fclose(fp);
219         } else
220             help_path = NULL;
221         strcpy(r, "putty.cnt");
222         if ( (fp = fopen(b, "r")) != NULL) {
223             help_has_contents = TRUE;
224             fclose(fp);
225         } else
226             help_has_contents = FALSE;
227     }
228
229     /*
230      * Process the command line.
231      */
232     {
233         char *p;
234
235         default_protocol = DEFAULT_PROTOCOL;
236         default_port = DEFAULT_PORT;
237         cfg.logtype = LGTYP_NONE;
238
239         do_defaults(NULL, &cfg);
240
241         p = cmdline;
242         while (*p && isspace(*p))
243             p++;
244
245         /*
246          * Process command line options first. Yes, this can be
247          * done better, and it will be as soon as I have the
248          * energy...
249          */
250         while (*p == '-') {
251             char *q = p + strcspn(p, " \t");
252             p++;
253             if (q == p + 3 &&
254                 tolower(p[0]) == 's' &&
255                 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
256                 default_protocol = cfg.protocol = PROT_SSH;
257                 default_port = cfg.port = 22;
258             } else if (q == p + 7 &&
259                        tolower(p[0]) == 'c' &&
260                        tolower(p[1]) == 'l' &&
261                        tolower(p[2]) == 'e' &&
262                        tolower(p[3]) == 'a' &&
263                        tolower(p[4]) == 'n' &&
264                        tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
265                 /*
266                  * `putty -cleanup'. Remove all registry entries
267                  * associated with PuTTY, and also find and delete
268                  * the random seed file.
269                  */
270                 if (MessageBox(NULL,
271                                "This procedure will remove ALL Registry\n"
272                                "entries associated with PuTTY, and will\n"
273                                "also remove the PuTTY random seed file.\n"
274                                "\n"
275                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
276                                "SESSIONS. Are you really sure you want\n"
277                                "to continue?",
278                                "PuTTY Warning",
279                                MB_YESNO | MB_ICONWARNING) == IDYES) {
280                     cleanup_all();
281                 }
282                 exit(0);
283             }
284             p = q + strspn(q, " \t");
285         }
286
287         /*
288          * An initial @ means to activate a saved session.
289          */
290         if (*p == '@') {
291             int i = strlen(p);
292             while (i > 1 && isspace(p[i - 1]))
293                 i--;
294             p[i] = '\0';
295             do_defaults(p + 1, &cfg);
296             if (!*cfg.host && !do_config()) {
297                 WSACleanup();
298                 return 0;
299             }
300         } else if (*p == '&') {
301             /*
302              * An initial & means we've been given a command line
303              * containing the hex value of a HANDLE for a file
304              * mapping object, which we must then extract as a
305              * config.
306              */
307             HANDLE filemap;
308             Config *cp;
309             if (sscanf(p + 1, "%p", &filemap) == 1 &&
310                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
311                                     0, 0, sizeof(Config))) != NULL) {
312                 cfg = *cp;
313                 UnmapViewOfFile(cp);
314                 CloseHandle(filemap);
315             } else if (!do_config()) {
316                 WSACleanup();
317                 return 0;
318             }
319         } else if (*p) {
320             char *q = p;
321             /*
322              * If the hostname starts with "telnet:", set the
323              * protocol to Telnet and process the string as a
324              * Telnet URL.
325              */
326             if (!strncmp(q, "telnet:", 7)) {
327                 char c;
328
329                 q += 7;
330                 if (q[0] == '/' && q[1] == '/')
331                     q += 2;
332                 cfg.protocol = PROT_TELNET;
333                 p = q;
334                 while (*p && *p != ':' && *p != '/')
335                     p++;
336                 c = *p;
337                 if (*p)
338                     *p++ = '\0';
339                 if (c == ':')
340                     cfg.port = atoi(p);
341                 else
342                     cfg.port = -1;
343                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
344                 cfg.host[sizeof(cfg.host) - 1] = '\0';
345             } else {
346                 while (*p && !isspace(*p))
347                     p++;
348                 if (*p)
349                     *p++ = '\0';
350                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
351                 cfg.host[sizeof(cfg.host) - 1] = '\0';
352                 while (*p && isspace(*p))
353                     p++;
354                 if (*p)
355                     cfg.port = atoi(p);
356                 else
357                     cfg.port = -1;
358             }
359         } else {
360             if (!do_config()) {
361                 WSACleanup();
362                 return 0;
363             }
364         }
365
366         /*
367          * Trim leading whitespace off the hostname if it's there.
368          */
369         {
370             int space = strspn(cfg.host, " \t");
371             memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
372         }
373
374         /* See if host is of the form user@host */
375         if (cfg.host[0] != '\0') {
376             char *atsign = strchr(cfg.host, '@');
377             /* Make sure we're not overflowing the user field */
378             if (atsign) {
379                 if (atsign - cfg.host < sizeof cfg.username) {
380                     strncpy(cfg.username, cfg.host, atsign - cfg.host);
381                     cfg.username[atsign - cfg.host] = '\0';
382                 }
383                 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
384             }
385         }
386
387         /*
388          * Trim a colon suffix off the hostname if it's there.
389          */
390         cfg.host[strcspn(cfg.host, ":")] = '\0';
391     }
392
393     /*
394      * Select protocol. This is farmed out into a table in a
395      * separate file to enable an ssh-free variant.
396      */
397     {
398         int i;
399         back = NULL;
400         for (i = 0; backends[i].backend != NULL; i++)
401             if (backends[i].protocol == cfg.protocol) {
402                 back = backends[i].backend;
403                 break;
404             }
405         if (back == NULL) {
406             MessageBox(NULL, "Unsupported protocol number found",
407                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
408             WSACleanup();
409             return 1;
410         }
411     }
412
413     /* Check for invalid Port number (i.e. zero) */
414     if (cfg.port == 0) {
415         MessageBox(NULL, "Invalid Port Number",
416                    "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
417         WSACleanup();
418         return 1;
419     }
420
421     if (!prev) {
422         wndclass.style = 0;
423         wndclass.lpfnWndProc = WndProc;
424         wndclass.cbClsExtra = 0;
425         wndclass.cbWndExtra = 0;
426         wndclass.hInstance = inst;
427         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
428         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
429         wndclass.hbrBackground = NULL;
430         wndclass.lpszMenuName = NULL;
431         wndclass.lpszClassName = appname;
432
433         RegisterClass(&wndclass);
434     }
435
436     hwnd = NULL;
437
438     savelines = cfg.savelines;
439     term_init();
440
441     cfgtopalette();
442
443     /*
444      * Guess some defaults for the window size. This all gets
445      * updated later, so we don't really care too much. However, we
446      * do want the font width/height guesses to correspond to a
447      * large font rather than a small one...
448      */
449
450     font_width = 10;
451     font_height = 20;
452     extra_width = 25;
453     extra_height = 28;
454     term_size(cfg.height, cfg.width, cfg.savelines);
455     guess_width = extra_width + font_width * cols;
456     guess_height = extra_height + font_height * rows;
457     {
458         RECT r;
459         HWND w = GetDesktopWindow();
460         GetWindowRect(w, &r);
461         if (guess_width > r.right - r.left)
462             guess_width = r.right - r.left;
463         if (guess_height > r.bottom - r.top)
464             guess_height = r.bottom - r.top;
465     }
466
467     {
468         int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
469         int exwinmode = 0;
470         if (!cfg.scrollbar)
471             winmode &= ~(WS_VSCROLL);
472         if (cfg.resize_action == RESIZE_DISABLED)
473             winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
474         if (cfg.alwaysontop)
475             exwinmode |= WS_EX_TOPMOST;
476         if (cfg.sunken_edge)
477             exwinmode |= WS_EX_CLIENTEDGE;
478         hwnd = CreateWindowEx(exwinmode, appname, appname,
479                               winmode, CW_USEDEFAULT, CW_USEDEFAULT,
480                               guess_width, guess_height,
481                               NULL, NULL, inst, NULL);
482     }
483
484     /*
485      * Initialise the fonts, simultaneously correcting the guesses
486      * for font_{width,height}.
487      */
488     init_fonts(0,0);
489
490     /*
491      * Correct the guesses for extra_{width,height}.
492      */
493     {
494         RECT cr, wr;
495         GetWindowRect(hwnd, &wr);
496         GetClientRect(hwnd, &cr);
497         offset_width = offset_height = cfg.window_border;
498         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
499         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
500     }
501
502     /*
503      * Resize the window, now we know what size we _really_ want it
504      * to be.
505      */
506     guess_width = extra_width + font_width * cols;
507     guess_height = extra_height + font_height * rows;
508     SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
509                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
510
511     /*
512      * Set up a caret bitmap, with no content.
513      */
514     {
515         char *bits;
516         int size = (font_width + 15) / 16 * 2 * font_height;
517         bits = smalloc(size);
518         memset(bits, 0, size);
519         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
520         sfree(bits);
521     }
522     CreateCaret(hwnd, caretbm, font_width, font_height);
523
524     /*
525      * Initialise the scroll bar.
526      */
527     {
528         SCROLLINFO si;
529
530         si.cbSize = sizeof(si);
531         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
532         si.nMin = 0;
533         si.nMax = rows - 1;
534         si.nPage = rows;
535         si.nPos = 0;
536         SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
537     }
538
539     /*
540      * Start up the telnet connection.
541      */
542     {
543         char *error;
544         char msg[1024], *title;
545         char *realhost;
546
547         error = back->init(cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
548         if (error) {
549             sprintf(msg, "Unable to open connection to\n"
550                     "%.800s\n" "%s", cfg.host, error);
551             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
552             return 0;
553         }
554         window_name = icon_name = NULL;
555         if (*cfg.wintitle) {
556             title = cfg.wintitle;
557         } else {
558             sprintf(msg, "%s - PuTTY", realhost);
559             title = msg;
560         }
561         sfree(realhost);
562         set_title(title);
563         set_icon(title);
564     }
565
566     session_closed = FALSE;
567
568     /*
569      * Prepare the mouse handler.
570      */
571     lastact = MA_NOTHING;
572     lastbtn = MBT_NOTHING;
573     dbltime = GetDoubleClickTime();
574
575     /*
576      * Set up the session-control options on the system menu.
577      */
578     {
579         HMENU m = GetSystemMenu(hwnd, FALSE);
580         HMENU p, s;
581         int i;
582
583         AppendMenu(m, MF_SEPARATOR, 0, 0);
584         if (cfg.protocol == PROT_TELNET) {
585             p = CreateMenu();
586             AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
587             AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
588             AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
589             AppendMenu(p, MF_SEPARATOR, 0, 0);
590             AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
591             AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
592             AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
593             AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
594             AppendMenu(p, MF_SEPARATOR, 0, 0);
595             AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
596             AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
597             AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
598             AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
599             AppendMenu(p, MF_SEPARATOR, 0, 0);
600             AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
601             AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
602             AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
603                        "Telnet Command");
604             AppendMenu(m, MF_SEPARATOR, 0, 0);
605         }
606         AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
607         AppendMenu(m, MF_SEPARATOR, 0, 0);
608         AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
609         AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
610         s = CreateMenu();
611         get_sesslist(TRUE);
612         for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
613             AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
614                        sessions[i]);
615         AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
616         AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
617         AppendMenu(m, MF_SEPARATOR, 0, 0);
618         AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
619         AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
620         AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
621         AppendMenu(m, MF_SEPARATOR, 0, 0);
622         AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
623                    MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
624         AppendMenu(m, MF_SEPARATOR, 0, 0);
625         if (help_path)
626             AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
627         AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
628     }
629
630     /*
631      * Set up the initial input locale.
632      */
633     set_input_locale(GetKeyboardLayout(0));
634
635     /*
636      * Open the initial log file if there is one.
637      */
638     logfopen();
639
640     /*
641      * Finally show the window!
642      */
643     ShowWindow(hwnd, show);
644     SetForegroundWindow(hwnd);
645
646     /*
647      * Set the palette up.
648      */
649     pal = NULL;
650     logpal = NULL;
651     init_palette();
652
653     has_focus = (GetForegroundWindow() == hwnd);
654     UpdateWindow(hwnd);
655
656     if (GetMessage(&msg, NULL, 0, 0) == 1) {
657         int timer_id = 0, long_timer = 0;
658
659         while (msg.message != WM_QUIT) {
660             /* Sometimes DispatchMessage calls routines that use their own
661              * GetMessage loop, setup this timer so we get some control back.
662              *
663              * Also call term_update() from the timer so that if the host
664              * is sending data flat out we still do redraws.
665              */
666             if (timer_id && long_timer) {
667                 KillTimer(hwnd, timer_id);
668                 long_timer = timer_id = 0;
669             }
670             if (!timer_id)
671                 timer_id = SetTimer(hwnd, 1, 20, NULL);
672             if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
673                 DispatchMessage(&msg);
674
675             /* Make sure we blink everything that needs it. */
676             term_blink(0);
677
678             /* Send the paste buffer if there's anything to send */
679             term_paste();
680
681             /* If there's nothing new in the queue then we can do everything
682              * we've delayed, reading the socket, writing, and repainting
683              * the window.
684              */
685             if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
686                 continue;
687
688             if (pending_netevent) {
689                 enact_pending_netevent();
690
691                 /* Force the cursor blink on */
692                 term_blink(1);
693
694                 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
695                     continue;
696             }
697
698             /* Okay there is now nothing to do so we make sure the screen is
699              * completely up to date then tell windows to call us in a little 
700              * while.
701              */
702             if (timer_id) {
703                 KillTimer(hwnd, timer_id);
704                 timer_id = 0;
705             }
706             HideCaret(hwnd);
707             term_out();
708             term_update();
709             ShowCaret(hwnd);
710
711             flash_window(1);           /* maintain */
712
713             /* The messages seem unreliable; especially if we're being tricky */
714             has_focus = (GetForegroundWindow() == hwnd);
715
716             if (in_vbell)
717                 /* Hmm, term_update didn't want to do an update too soon ... */
718                 timer_id = SetTimer(hwnd, 1, 50, NULL);
719             else if (!has_focus)
720                 timer_id = SetTimer(hwnd, 1, 500, NULL);
721             else
722                 timer_id = SetTimer(hwnd, 1, 100, NULL);
723             long_timer = 1;
724
725             /* There's no point rescanning everything in the message queue
726              * so we do an apparently unnecessary wait here
727              */
728             WaitMessage();
729             if (GetMessage(&msg, NULL, 0, 0) != 1)
730                 break;
731         }
732     }
733
734     /*
735      * Clean up.
736      */
737     deinit_fonts();
738     sfree(logpal);
739     if (pal)
740         DeleteObject(pal);
741     WSACleanup();
742
743     if (cfg.protocol == PROT_SSH) {
744         random_save_seed();
745 #ifdef MSCRYPTOAPI
746         crypto_wrapup();
747 #endif
748     }
749
750     return msg.wParam;
751 }
752
753 /*
754  * Set up, or shut down, an AsyncSelect. Called from winnet.c.
755  */
756 char *do_select(SOCKET skt, int startup)
757 {
758     int msg, events;
759     if (startup) {
760         msg = WM_NETEVENT;
761         events = (FD_CONNECT | FD_READ | FD_WRITE |
762                   FD_OOB | FD_CLOSE | FD_ACCEPT);
763     } else {
764         msg = events = 0;
765     }
766     if (!hwnd)
767         return "do_select(): internal error (hwnd==NULL)";
768     if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
769         switch (WSAGetLastError()) {
770           case WSAENETDOWN:
771             return "Network is down";
772           default:
773             return "WSAAsyncSelect(): unknown error";
774         }
775     }
776     return NULL;
777 }
778
779 /*
780  * set or clear the "raw mouse message" mode
781  */
782 void set_raw_mouse_mode(int activate)
783 {
784     send_raw_mouse = activate;
785     SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
786 }
787
788 /*
789  * Print a message box and close the connection.
790  */
791 void connection_fatal(char *fmt, ...)
792 {
793     va_list ap;
794     char stuff[200];
795
796     va_start(ap, fmt);
797     vsprintf(stuff, fmt, ap);
798     va_end(ap);
799     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
800     if (cfg.close_on_exit == COE_ALWAYS)
801         PostQuitMessage(1);
802     else {
803         session_closed = TRUE;
804         SetWindowText(hwnd, "PuTTY (inactive)");
805     }
806 }
807
808 /*
809  * Actually do the job requested by a WM_NETEVENT
810  */
811 static void enact_pending_netevent(void)
812 {
813     static int reentering = 0;
814     extern int select_result(WPARAM, LPARAM);
815     int ret;
816
817     if (reentering)
818         return;                        /* don't unpend the pending */
819
820     pending_netevent = FALSE;
821
822     reentering = 1;
823     ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
824     reentering = 0;
825
826     if (ret == 0 && !session_closed) {
827         /* Abnormal exits will already have set session_closed and taken
828          * appropriate action. */
829         if (cfg.close_on_exit == COE_ALWAYS ||
830             cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
831         else {
832             session_closed = TRUE;
833             SetWindowText(hwnd, "PuTTY (inactive)");
834             MessageBox(hwnd, "Connection closed by remote host",
835                        "PuTTY", MB_OK | MB_ICONINFORMATION);
836         }
837     }
838 }
839
840 /*
841  * Copy the colour palette from the configuration data into defpal.
842  * This is non-trivial because the colour indices are different.
843  */
844 static void cfgtopalette(void)
845 {
846     int i;
847     static const int ww[] = {
848         6, 7, 8, 9, 10, 11, 12, 13,
849         14, 15, 16, 17, 18, 19, 20, 21,
850         0, 1, 2, 3, 4, 4, 5, 5
851     };
852
853     for (i = 0; i < 24; i++) {
854         int w = ww[i];
855         defpal[i].rgbtRed = cfg.colours[w][0];
856         defpal[i].rgbtGreen = cfg.colours[w][1];
857         defpal[i].rgbtBlue = cfg.colours[w][2];
858     }
859 }
860
861 /*
862  * Set up the colour palette.
863  */
864 static void init_palette(void)
865 {
866     int i;
867     HDC hdc = GetDC(hwnd);
868     if (hdc) {
869         if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
870             logpal = smalloc(sizeof(*logpal)
871                              - sizeof(logpal->palPalEntry)
872                              + NCOLOURS * sizeof(PALETTEENTRY));
873             logpal->palVersion = 0x300;
874             logpal->palNumEntries = NCOLOURS;
875             for (i = 0; i < NCOLOURS; i++) {
876                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
877                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
878                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
879                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
880             }
881             pal = CreatePalette(logpal);
882             if (pal) {
883                 SelectPalette(hdc, pal, FALSE);
884                 RealizePalette(hdc);
885                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
886             }
887         }
888         ReleaseDC(hwnd, hdc);
889     }
890     if (pal)
891         for (i = 0; i < NCOLOURS; i++)
892             colours[i] = PALETTERGB(defpal[i].rgbtRed,
893                                     defpal[i].rgbtGreen,
894                                     defpal[i].rgbtBlue);
895     else
896         for (i = 0; i < NCOLOURS; i++)
897             colours[i] = RGB(defpal[i].rgbtRed,
898                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
899 }
900
901 /*
902  * Initialise all the fonts we will need initially. There may be as many as
903  * three or as few as one.  The other (poentially) twentyone fonts are done
904  * if/when they are needed.
905  *
906  * We also:
907  *
908  * - check the font width and height, correcting our guesses if
909  *   necessary.
910  *
911  * - verify that the bold font is the same width as the ordinary
912  *   one, and engage shadow bolding if not.
913  * 
914  * - verify that the underlined font is the same width as the
915  *   ordinary one (manual underlining by means of line drawing can
916  *   be done in a pinch).
917  */
918 static void init_fonts(int pick_width, int pick_height)
919 {
920     TEXTMETRIC tm;
921     CPINFO cpinfo;
922     int fontsize[3];
923     int i;
924     HDC hdc;
925     int fw_dontcare, fw_bold;
926
927     for (i = 0; i < FONT_MAXNO; i++)
928         fonts[i] = NULL;
929
930     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
931     und_mode = UND_FONT;
932
933     if (cfg.fontisbold) {
934         fw_dontcare = FW_BOLD;
935         fw_bold = FW_HEAVY;
936     } else {
937         fw_dontcare = FW_DONTCARE;
938         fw_bold = FW_BOLD;
939     }
940
941     hdc = GetDC(hwnd);
942
943     if (pick_height)
944         font_height = pick_height;
945     else {
946         font_height = cfg.fontheight;
947         if (font_height > 0) {
948             font_height =
949                 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
950         }
951     }
952     font_width = pick_width;
953
954 #define f(i,c,w,u) \
955     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
956                            c, OUT_DEFAULT_PRECIS, \
957                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
958                            FIXED_PITCH | FF_DONTCARE, cfg.font)
959
960     f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
961
962     lfont.lfHeight = font_height;
963     lfont.lfWidth = font_width;
964     lfont.lfEscapement = 0;
965     lfont.lfOrientation  = 0;
966     lfont.lfWeight  = fw_dontcare;
967     lfont.lfItalic = FALSE;
968     lfont.lfUnderline = FALSE;
969     lfont.lfStrikeOut = FALSE;
970     lfont.lfCharSet = cfg.fontcharset;
971     lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
972     lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
973     lfont.lfQuality = DEFAULT_QUALITY;
974     lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
975     strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
976
977     SelectObject(hdc, fonts[FONT_NORMAL]);
978     GetTextMetrics(hdc, &tm);
979
980     if (pick_width == 0 || pick_height == 0) {
981         font_height = tm.tmHeight;
982         font_width = tm.tmAveCharWidth;
983     }
984     font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
985
986 #ifdef RDB_DEBUG_PATCH
987     debug(23, "Primary font H=%d, AW=%d, MW=%d",
988             tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
989 #endif
990
991     {
992         CHARSETINFO info;
993         DWORD cset = tm.tmCharSet;
994         memset(&info, 0xFF, sizeof(info));
995
996         /* !!! Yes the next line is right */
997         if (cset == OEM_CHARSET)
998             font_codepage = GetOEMCP();
999         else
1000             if (TranslateCharsetInfo
1001                 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
1002                 info.ciACP;
1003         else
1004             font_codepage = -1;
1005
1006         GetCPInfo(font_codepage, &cpinfo);
1007         dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1008     }
1009
1010     f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
1011
1012     /*
1013      * Some fonts, e.g. 9-pt Courier, draw their underlines
1014      * outside their character cell. We successfully prevent
1015      * screen corruption by clipping the text output, but then
1016      * we lose the underline completely. Here we try to work
1017      * out whether this is such a font, and if it is, we set a
1018      * flag that causes underlines to be drawn by hand.
1019      *
1020      * Having tried other more sophisticated approaches (such
1021      * as examining the TEXTMETRIC structure or requesting the
1022      * height of a string), I think we'll do this the brute
1023      * force way: we create a small bitmap, draw an underlined
1024      * space on it, and test to see whether any pixels are
1025      * foreground-coloured. (Since we expect the underline to
1026      * go all the way across the character cell, we only search
1027      * down a single column of the bitmap, half way across.)
1028      */
1029     {
1030         HDC und_dc;
1031         HBITMAP und_bm, und_oldbm;
1032         int i, gotit;
1033         COLORREF c;
1034
1035         und_dc = CreateCompatibleDC(hdc);
1036         und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1037         und_oldbm = SelectObject(und_dc, und_bm);
1038         SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1039         SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1040         SetTextColor(und_dc, RGB(255, 255, 255));
1041         SetBkColor(und_dc, RGB(0, 0, 0));
1042         SetBkMode(und_dc, OPAQUE);
1043         ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1044         gotit = FALSE;
1045         for (i = 0; i < font_height; i++) {
1046             c = GetPixel(und_dc, font_width / 2, i);
1047             if (c != RGB(0, 0, 0))
1048                 gotit = TRUE;
1049         }
1050         SelectObject(und_dc, und_oldbm);
1051         DeleteObject(und_bm);
1052         DeleteDC(und_dc);
1053         if (!gotit) {
1054             und_mode = UND_LINE;
1055             DeleteObject(fonts[FONT_UNDERLINE]);
1056             fonts[FONT_UNDERLINE] = 0;
1057         }
1058     }
1059
1060     if (bold_mode == BOLD_FONT) {
1061         f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1062     }
1063 #undef f
1064
1065     descent = tm.tmAscent + 1;
1066     if (descent >= font_height)
1067         descent = font_height - 1;
1068
1069     for (i = 0; i < 3; i++) {
1070         if (fonts[i]) {
1071             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1072                 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1073             else
1074                 fontsize[i] = -i;
1075         } else
1076             fontsize[i] = -i;
1077     }
1078
1079     ReleaseDC(hwnd, hdc);
1080
1081     if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1082         und_mode = UND_LINE;
1083         DeleteObject(fonts[FONT_UNDERLINE]);
1084         fonts[FONT_UNDERLINE] = 0;
1085     }
1086
1087     if (bold_mode == BOLD_FONT &&
1088         fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1089         bold_mode = BOLD_SHADOW;
1090         DeleteObject(fonts[FONT_BOLD]);
1091         fonts[FONT_BOLD] = 0;
1092     }
1093     fontflag[0] = fontflag[1] = fontflag[2] = 1;
1094
1095     init_ucs_tables();
1096 }
1097
1098 static void another_font(int fontno)
1099 {
1100     int basefont;
1101     int fw_dontcare, fw_bold;
1102     int c, u, w, x;
1103     char *s;
1104
1105     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1106         return;
1107
1108     basefont = (fontno & ~(FONT_BOLDUND));
1109     if (basefont != fontno && !fontflag[basefont])
1110         another_font(basefont);
1111
1112     if (cfg.fontisbold) {
1113         fw_dontcare = FW_BOLD;
1114         fw_bold = FW_HEAVY;
1115     } else {
1116         fw_dontcare = FW_DONTCARE;
1117         fw_bold = FW_BOLD;
1118     }
1119
1120     c = cfg.fontcharset;
1121     w = fw_dontcare;
1122     u = FALSE;
1123     s = cfg.font;
1124     x = font_width;
1125
1126     if (fontno & FONT_WIDE)
1127         x *= 2;
1128     if (fontno & FONT_NARROW)
1129         x = (x+1)/2;
1130     if (fontno & FONT_OEM)
1131         c = OEM_CHARSET;
1132     if (fontno & FONT_BOLD)
1133         w = fw_bold;
1134     if (fontno & FONT_UNDERLINE)
1135         u = TRUE;
1136
1137     fonts[fontno] =
1138         CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1139                    FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1140                    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1141                    FIXED_PITCH | FF_DONTCARE, s);
1142
1143     fontflag[fontno] = 1;
1144 }
1145
1146 static void deinit_fonts(void)
1147 {
1148     int i;
1149     for (i = 0; i < FONT_MAXNO; i++) {
1150         if (fonts[i])
1151             DeleteObject(fonts[i]);
1152         fonts[i] = 0;
1153         fontflag[i] = 0;
1154     }
1155 }
1156
1157 void request_resize(int w, int h)
1158 {
1159     int width, height;
1160
1161     /* If the window is maximized supress resizing attempts */
1162     if (IsZoomed(hwnd)) {
1163         if (cfg.resize_action == RESIZE_TERM)
1164             return;
1165     }
1166
1167     if (cfg.resize_action == RESIZE_DISABLED) return;
1168     if (h == rows && w == cols) return;
1169
1170     /* Sanity checks ... */
1171     {
1172         static int first_time = 1;
1173         static RECT ss;
1174
1175         switch (first_time) {
1176           case 1:
1177             /* Get the size of the screen */
1178             if (GetClientRect(GetDesktopWindow(), &ss))
1179                 /* first_time = 0 */ ;
1180             else {
1181                 first_time = 2;
1182                 break;
1183             }
1184           case 0:
1185             /* Make sure the values are sane */
1186             width = (ss.right - ss.left - extra_width) / 4;
1187             height = (ss.bottom - ss.top - extra_height) / 6;
1188
1189             if (w > width || h > height)
1190                 return;
1191             if (w < 15)
1192                 w = 15;
1193             if (h < 1)
1194                 h = 1;
1195         }
1196     }
1197
1198     term_size(h, w, cfg.savelines);
1199
1200     if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1201         width = extra_width + font_width * w;
1202         height = extra_height + font_height * h;
1203
1204         SetWindowPos(hwnd, NULL, 0, 0, width, height,
1205             SWP_NOACTIVATE | SWP_NOCOPYBITS |
1206             SWP_NOMOVE | SWP_NOZORDER);
1207     } else
1208         reset_window(0);
1209
1210     InvalidateRect(hwnd, NULL, TRUE);
1211 }
1212
1213 static void reset_window(int reinit) {
1214     /*
1215      * This function decides how to resize or redraw when the 
1216      * user changes something. 
1217      *
1218      * This function doesn't like to change the terminal size but if the
1219      * font size is locked that may be it's only soluion.
1220      */
1221     int win_width, win_height;
1222     RECT cr, wr;
1223
1224 #ifdef RDB_DEBUG_PATCH
1225     debug((27, "reset_window()"));
1226 #endif
1227
1228     /* Current window sizes ... */
1229     GetWindowRect(hwnd, &wr);
1230     GetClientRect(hwnd, &cr);
1231
1232     win_width  = cr.right - cr.left;
1233     win_height = cr.bottom - cr.top;
1234
1235     if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1236
1237     /* Are we being forced to reload the fonts ? */
1238     if (reinit>1) {
1239 #ifdef RDB_DEBUG_PATCH
1240         debug((27, "reset_window() -- Forced deinit"));
1241 #endif
1242         deinit_fonts();
1243         init_fonts(0,0);
1244     }
1245
1246     /* Oh, looks like we're minimised */
1247     if (win_width == 0 || win_height == 0)
1248         return;
1249
1250     /* Is the window out of position ? */
1251     if ( !reinit && 
1252             (offset_width != (win_width-font_width*cols)/2 ||
1253              offset_height != (win_height-font_height*rows)/2) ){
1254         offset_width = (win_width-font_width*cols)/2;
1255         offset_height = (win_height-font_height*rows)/2;
1256         InvalidateRect(hwnd, NULL, TRUE);
1257 #ifdef RDB_DEBUG_PATCH
1258         debug((27, "reset_window() -> Reposition terminal"));
1259 #endif
1260     }
1261
1262     if (IsZoomed(hwnd)) {
1263         /* We're fullscreen, this means we must not change the size of
1264          * the window so it's the font size or the terminal itself.
1265          */
1266
1267         extra_width = wr.right - wr.left - cr.right + cr.left;
1268         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1269
1270         if (cfg.resize_action != RESIZE_TERM) {
1271             if (  font_width != win_width/cols || 
1272                   font_height != win_height/rows) {
1273                 deinit_fonts();
1274                 init_fonts(win_width/cols, win_height/rows);
1275                 offset_width = (win_width-font_width*cols)/2;
1276                 offset_height = (win_height-font_height*rows)/2;
1277                 InvalidateRect(hwnd, NULL, TRUE);
1278 #ifdef RDB_DEBUG_PATCH
1279                 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1280                         font_width, font_height));
1281 #endif
1282             }
1283         } else {
1284             if (  font_width != win_width/cols || 
1285                   font_height != win_height/rows) {
1286                 /* Our only choice at this point is to change the 
1287                  * size of the terminal; Oh well.
1288                  */
1289                 term_size( win_height/font_height, win_width/font_width,
1290                            cfg.savelines);
1291                 offset_width = (win_width-font_width*cols)/2;
1292                 offset_height = (win_height-font_height*rows)/2;
1293                 InvalidateRect(hwnd, NULL, TRUE);
1294 #ifdef RDB_DEBUG_PATCH
1295                 debug((27, "reset_window() -> Zoomed term_size"));
1296 #endif
1297             }
1298         }
1299         return;
1300     }
1301
1302     /* Hmm, a force re-init means we should ignore the current window
1303      * so we resize to the default font size.
1304      */
1305     if (reinit>0) {
1306 #ifdef RDB_DEBUG_PATCH
1307         debug((27, "reset_window() -> Forced re-init"));
1308 #endif
1309
1310         offset_width = offset_height = cfg.window_border;
1311         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1312         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1313
1314         if (win_width != font_width*cols + offset_width*2 ||
1315             win_height != font_height*rows + offset_height*2) {
1316
1317             /* If this is too large windows will resize it to the maximum
1318              * allowed window size, we will then be back in here and resize
1319              * the font or terminal to fit.
1320              */
1321             SetWindowPos(hwnd, NULL, 0, 0, 
1322                          font_width*cols + extra_width, 
1323                          font_height*rows + extra_height,
1324                          SWP_NOMOVE | SWP_NOZORDER);
1325         }
1326
1327         InvalidateRect(hwnd, NULL, TRUE);
1328         return;
1329     }
1330
1331     /* Okay the user doesn't want us to change the font so we try the 
1332      * window. But that may be too big for the screen which forces us
1333      * to change the terminal.
1334      */
1335     if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1336         (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1337             reinit>0) {
1338         offset_width = offset_height = cfg.window_border;
1339         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1340         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1341
1342         if (win_width != font_width*cols + offset_width*2 ||
1343             win_height != font_height*rows + offset_height*2) {
1344
1345             static RECT ss;
1346             int width, height;
1347
1348             GetClientRect(GetDesktopWindow(), &ss);
1349             width = (ss.right - ss.left - extra_width) / font_width;
1350             height = (ss.bottom - ss.top - extra_height) / font_height;
1351
1352             /* Grrr too big */
1353             if ( rows > height || cols > width ) {
1354                 if (cfg.resize_action == RESIZE_EITHER) {
1355                     /* Make the font the biggest we can */
1356                     if (cols > width)
1357                         font_width = (ss.right - ss.left - extra_width)/cols;
1358                     if (rows > height)
1359                         font_height = (ss.bottom - ss.top - extra_height)/rows;
1360
1361                     deinit_fonts();
1362                     init_fonts(font_width, font_height);
1363
1364                     width = (ss.right - ss.left - extra_width) / font_width;
1365                     height = (ss.bottom - ss.top - extra_height) / font_height;
1366                 } else {
1367                     if ( height > rows ) height = rows;
1368                     if ( width > cols )  width = cols;
1369                     term_size(height, width, cfg.savelines);
1370 #ifdef RDB_DEBUG_PATCH
1371                     debug((27, "reset_window() -> term resize to (%d,%d)",
1372                                height, width));
1373 #endif
1374                 }
1375             }
1376             
1377             SetWindowPos(hwnd, NULL, 0, 0, 
1378                          font_width*cols + extra_width, 
1379                          font_height*rows + extra_height,
1380                          SWP_NOMOVE | SWP_NOZORDER);
1381
1382             InvalidateRect(hwnd, NULL, TRUE);
1383 #ifdef RDB_DEBUG_PATCH
1384             debug((27, "reset_window() -> window resize to (%d,%d)",
1385                         font_width*cols + extra_width,
1386                         font_height*rows + extra_height));
1387 #endif
1388         }
1389         return;
1390     }
1391
1392     /* We're allowed to or must change the font but do we want to ?  */
1393
1394     if (font_width != (win_width-cfg.window_border*2)/cols || 
1395         font_height != (win_height-cfg.window_border*2)/rows) {
1396
1397         deinit_fonts();
1398         init_fonts((win_width-cfg.window_border*2)/cols, 
1399                    (win_height-cfg.window_border*2)/rows);
1400         offset_width = (win_width-font_width*cols)/2;
1401         offset_height = (win_height-font_height*rows)/2;
1402
1403         extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1404         extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1405
1406         InvalidateRect(hwnd, NULL, TRUE);
1407 #ifdef RDB_DEBUG_PATCH
1408         debug((25, "reset_window() -> font resize to (%d,%d)", 
1409                    font_width, font_height));
1410 #endif
1411     }
1412 }
1413
1414 static void set_input_locale(HKL kl)
1415 {
1416     char lbuf[20];
1417
1418     GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1419                   lbuf, sizeof(lbuf));
1420
1421     kbd_codepage = atoi(lbuf);
1422 }
1423
1424 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1425 {
1426     int thistime = GetMessageTime();
1427
1428     if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1429         lastbtn = MBT_NOTHING;
1430         term_mouse(b, MA_CLICK, x, y, shift, ctrl, alt);
1431         return;
1432     }
1433
1434     if (lastbtn == b && thistime - lasttime < dbltime) {
1435         lastact = (lastact == MA_CLICK ? MA_2CLK :
1436                    lastact == MA_2CLK ? MA_3CLK :
1437                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1438     } else {
1439         lastbtn = b;
1440         lastact = MA_CLICK;
1441     }
1442     if (lastact != MA_NOTHING)
1443         term_mouse(b, lastact, x, y, shift, ctrl, alt);
1444     lasttime = thistime;
1445 }
1446
1447 /*
1448  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1449  * into a cooked one (SELECT, EXTEND, PASTE).
1450  */
1451 Mouse_Button translate_button(Mouse_Button button)
1452 {
1453     if (button == MBT_LEFT)
1454         return MBT_SELECT;
1455     if (button == MBT_MIDDLE)
1456         return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1457     if (button == MBT_RIGHT)
1458         return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1459     return 0;                          /* shouldn't happen */
1460 }
1461
1462 static void show_mouseptr(int show)
1463 {
1464     static int cursor_visible = 1;
1465     if (!cfg.hide_mouseptr)            /* override if this feature disabled */
1466         show = 1;
1467     if (cursor_visible && !show)
1468         ShowCursor(FALSE);
1469     else if (!cursor_visible && show)
1470         ShowCursor(TRUE);
1471     cursor_visible = show;
1472 }
1473
1474 static int is_alt_pressed(void)
1475 {
1476     BYTE keystate[256];
1477     int r = GetKeyboardState(keystate);
1478     if (!r)
1479         return FALSE;
1480     if (keystate[VK_MENU] & 0x80)
1481         return TRUE;
1482     if (keystate[VK_RMENU] & 0x80)
1483         return TRUE;
1484     return FALSE;
1485 }
1486
1487 static int resizing;
1488
1489 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1490                                 WPARAM wParam, LPARAM lParam)
1491 {
1492     HDC hdc;
1493     static int ignore_clip = FALSE;
1494     static int need_backend_resize = FALSE;
1495     static int fullscr_on_max = FALSE;
1496
1497     switch (message) {
1498       case WM_TIMER:
1499         if (pending_netevent)
1500             enact_pending_netevent();
1501         term_out();
1502         noise_regular();
1503         HideCaret(hwnd);
1504         term_update();
1505         ShowCaret(hwnd);
1506         if (cfg.ping_interval > 0) {
1507             time_t now;
1508             time(&now);
1509             if (now - last_movement > cfg.ping_interval) {
1510                 back->special(TS_PING);
1511                 last_movement = now;
1512             }
1513         }
1514         net_pending_errors();
1515         return 0;
1516       case WM_CREATE:
1517         break;
1518       case WM_CLOSE:
1519         show_mouseptr(1);
1520         if (!cfg.warn_on_close || session_closed ||
1521             MessageBox(hwnd,
1522                        "Are you sure you want to close this session?",
1523                        "PuTTY Exit Confirmation",
1524                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1525             DestroyWindow(hwnd);
1526         return 0;
1527       case WM_DESTROY:
1528         show_mouseptr(1);
1529         PostQuitMessage(0);
1530         return 0;
1531       case WM_SYSCOMMAND:
1532         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1533           case IDM_SHOWLOG:
1534             showeventlog(hwnd);
1535             break;
1536           case IDM_NEWSESS:
1537           case IDM_DUPSESS:
1538           case IDM_SAVEDSESS:
1539             {
1540                 char b[2048];
1541                 char c[30], *cl;
1542                 int freecl = FALSE;
1543                 STARTUPINFO si;
1544                 PROCESS_INFORMATION pi;
1545                 HANDLE filemap = NULL;
1546
1547                 if (wParam == IDM_DUPSESS) {
1548                     /*
1549                      * Allocate a file-mapping memory chunk for the
1550                      * config structure.
1551                      */
1552                     SECURITY_ATTRIBUTES sa;
1553                     Config *p;
1554
1555                     sa.nLength = sizeof(sa);
1556                     sa.lpSecurityDescriptor = NULL;
1557                     sa.bInheritHandle = TRUE;
1558                     filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1559                                                 &sa,
1560                                                 PAGE_READWRITE,
1561                                                 0, sizeof(Config), NULL);
1562                     if (filemap) {
1563                         p = (Config *) MapViewOfFile(filemap,
1564                                                      FILE_MAP_WRITE,
1565                                                      0, 0, sizeof(Config));
1566                         if (p) {
1567                             *p = cfg;  /* structure copy */
1568                             UnmapViewOfFile(p);
1569                         }
1570                     }
1571                     sprintf(c, "putty &%p", filemap);
1572                     cl = c;
1573                 } else if (wParam == IDM_SAVEDSESS) {
1574                     if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) {
1575                         char *session =
1576                             sessions[(lParam - IDM_SAVED_MIN) / 16];
1577                         cl = smalloc(16 + strlen(session));
1578                                        /* 8, but play safe */
1579                         if (!cl)
1580                             cl = NULL;    
1581                                        /* not a very important failure mode */
1582                         else {
1583                             sprintf(cl, "putty @%s", session);
1584                             freecl = TRUE;
1585                         }
1586                     } else
1587                         break;
1588                 } else
1589                     cl = NULL;
1590
1591                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1592                 si.cb = sizeof(si);
1593                 si.lpReserved = NULL;
1594                 si.lpDesktop = NULL;
1595                 si.lpTitle = NULL;
1596                 si.dwFlags = 0;
1597                 si.cbReserved2 = 0;
1598                 si.lpReserved2 = NULL;
1599                 CreateProcess(b, cl, NULL, NULL, TRUE,
1600                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1601
1602                 if (filemap)
1603                     CloseHandle(filemap);
1604                 if (freecl)
1605                     sfree(cl);
1606             }
1607             break;
1608           case IDM_RECONF:
1609             {
1610                 Config prev_cfg;
1611                 int init_lvl = 1;
1612
1613                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1614                 prev_cfg = cfg;
1615
1616                 if (!do_reconfig(hwnd))
1617                     break;
1618
1619                 {
1620                     /* Disable full-screen if resizing forbidden */
1621                     HMENU m = GetSystemMenu (hwnd, FALSE);
1622                     EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND | 
1623                                    (cfg.resize_action == RESIZE_DISABLED)
1624                                    ? MF_GRAYED : MF_ENABLED);
1625                     /* Gracefully unzoom if necessary */
1626                     if (IsZoomed(hwnd) &&
1627                         (cfg.resize_action == RESIZE_DISABLED)) {
1628                         ShowWindow(hwnd, SW_RESTORE);
1629                     }
1630                 }
1631
1632                 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1633                     prev_cfg.logtype != cfg.logtype) {
1634                     logfclose();       /* reset logging */
1635                     logfopen();
1636                 }
1637
1638                 sfree(logpal);
1639                 /*
1640                  * Flush the line discipline's edit buffer in the
1641                  * case where local editing has just been disabled.
1642                  */
1643                 ldisc_send(NULL, 0, 0);
1644                 if (pal)
1645                     DeleteObject(pal);
1646                 logpal = NULL;
1647                 pal = NULL;
1648                 cfgtopalette();
1649                 init_palette();
1650
1651                 /* Screen size changed ? */
1652                 if (cfg.height != prev_cfg.height ||
1653                     cfg.width != prev_cfg.width ||
1654                     cfg.savelines != prev_cfg.savelines ||
1655                     cfg.resize_action == RESIZE_FONT ||
1656                     (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1657                     cfg.resize_action == RESIZE_DISABLED)
1658                     term_size(cfg.height, cfg.width, cfg.savelines);
1659
1660                 /* Enable or disable the scroll bar, etc */
1661                 {
1662                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1663                     LONG nexflag, exflag =
1664                         GetWindowLong(hwnd, GWL_EXSTYLE);
1665
1666                     nexflag = exflag;
1667                     if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1668                         if (cfg.alwaysontop) {
1669                             nexflag |= WS_EX_TOPMOST;
1670                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1671                                          SWP_NOMOVE | SWP_NOSIZE);
1672                         } else {
1673                             nexflag &= ~(WS_EX_TOPMOST);
1674                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1675                                          SWP_NOMOVE | SWP_NOSIZE);
1676                         }
1677                     }
1678                     if (cfg.sunken_edge)
1679                         nexflag |= WS_EX_CLIENTEDGE;
1680                     else
1681                         nexflag &= ~(WS_EX_CLIENTEDGE);
1682
1683                     nflg = flag;
1684                     if (is_full_screen() ?
1685                         cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1686                         nflg |= WS_VSCROLL;
1687                     else
1688                         nflg &= ~WS_VSCROLL;
1689
1690                     if (cfg.resize_action == RESIZE_DISABLED ||
1691                         is_full_screen())
1692                         nflg &= ~WS_THICKFRAME;
1693                     else
1694                         nflg |= WS_THICKFRAME;
1695
1696                     if (cfg.resize_action == RESIZE_DISABLED)
1697                         nflg &= ~WS_MAXIMIZEBOX;
1698                     else
1699                         nflg |= WS_MAXIMIZEBOX;
1700
1701                     if (nflg != flag || nexflag != exflag) {
1702                         if (nflg != flag)
1703                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1704                         if (nexflag != exflag)
1705                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1706
1707                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1708                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1709                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1710                                      SWP_FRAMECHANGED);
1711
1712                         init_lvl = 2;
1713                     }
1714                 }
1715
1716                 /* Oops */
1717                 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1718                     force_normal(hwnd);
1719                     init_lvl = 2;
1720                 }
1721
1722                 set_title(cfg.wintitle);
1723                 if (IsIconic(hwnd)) {
1724                     SetWindowText(hwnd,
1725                                   cfg.win_name_always ? window_name :
1726                                   icon_name);
1727                 }
1728
1729                 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1730                     strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1731                     cfg.fontisbold != prev_cfg.fontisbold ||
1732                     cfg.fontheight != prev_cfg.fontheight ||
1733                     cfg.fontcharset != prev_cfg.fontcharset ||
1734                     cfg.vtmode != prev_cfg.vtmode ||
1735                     cfg.bold_colour != prev_cfg.bold_colour ||
1736                     cfg.resize_action == RESIZE_DISABLED ||
1737                     cfg.resize_action == RESIZE_EITHER ||
1738                     (cfg.resize_action != prev_cfg.resize_action))
1739                     init_lvl = 2;
1740
1741                 InvalidateRect(hwnd, NULL, TRUE);
1742                 reset_window(init_lvl);
1743                 net_pending_errors();
1744             }
1745             break;
1746           case IDM_COPYALL:
1747             term_copyall();
1748             break;
1749           case IDM_CLRSB:
1750             term_clrsb();
1751             break;
1752           case IDM_RESET:
1753             term_pwron();
1754             break;
1755           case IDM_TEL_AYT:
1756             back->special(TS_AYT);
1757             net_pending_errors();
1758             break;
1759           case IDM_TEL_BRK:
1760             back->special(TS_BRK);
1761             net_pending_errors();
1762             break;
1763           case IDM_TEL_SYNCH:
1764             back->special(TS_SYNCH);
1765             net_pending_errors();
1766             break;
1767           case IDM_TEL_EC:
1768             back->special(TS_EC);
1769             net_pending_errors();
1770             break;
1771           case IDM_TEL_EL:
1772             back->special(TS_EL);
1773             net_pending_errors();
1774             break;
1775           case IDM_TEL_GA:
1776             back->special(TS_GA);
1777             net_pending_errors();
1778             break;
1779           case IDM_TEL_NOP:
1780             back->special(TS_NOP);
1781             net_pending_errors();
1782             break;
1783           case IDM_TEL_ABORT:
1784             back->special(TS_ABORT);
1785             net_pending_errors();
1786             break;
1787           case IDM_TEL_AO:
1788             back->special(TS_AO);
1789             net_pending_errors();
1790             break;
1791           case IDM_TEL_IP:
1792             back->special(TS_IP);
1793             net_pending_errors();
1794             break;
1795           case IDM_TEL_SUSP:
1796             back->special(TS_SUSP);
1797             net_pending_errors();
1798             break;
1799           case IDM_TEL_EOR:
1800             back->special(TS_EOR);
1801             net_pending_errors();
1802             break;
1803           case IDM_TEL_EOF:
1804             back->special(TS_EOF);
1805             net_pending_errors();
1806             break;
1807           case IDM_ABOUT:
1808             showabout(hwnd);
1809             break;
1810           case IDM_HELP:
1811             WinHelp(hwnd, help_path,
1812                     help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1813             break;
1814           case SC_MOUSEMENU:
1815             /*
1816              * We get this if the System menu has been activated
1817              * using the mouse.
1818              */
1819             show_mouseptr(1);
1820             break;
1821           case SC_KEYMENU:
1822             /*
1823              * We get this if the System menu has been activated
1824              * using the keyboard. This might happen from within
1825              * TranslateKey, in which case it really wants to be
1826              * followed by a `space' character to actually _bring
1827              * the menu up_ rather than just sitting there in
1828              * `ready to appear' state.
1829              */
1830             show_mouseptr(1);          /* make sure pointer is visible */
1831             if( lParam == 0 )
1832                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1833             break;
1834           case IDM_FULLSCREEN:
1835             flip_full_screen();
1836             break;
1837           default:
1838             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1839                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1840             }
1841         }
1842         break;
1843
1844 #define X_POS(l) ((int)(short)LOWORD(l))
1845 #define Y_POS(l) ((int)(short)HIWORD(l))
1846
1847 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1848 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1849 #define WHEEL_DELTA 120
1850       case WM_MOUSEWHEEL:
1851         {
1852             wheel_accumulator += (short) HIWORD(wParam);
1853             wParam = LOWORD(wParam);
1854
1855             /* process events when the threshold is reached */
1856             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1857                 int b;
1858
1859                 /* reduce amount for next time */
1860                 if (wheel_accumulator > 0) {
1861                     b = MBT_WHEEL_UP;
1862                     wheel_accumulator -= WHEEL_DELTA;
1863                 } else if (wheel_accumulator < 0) {
1864                     b = MBT_WHEEL_DOWN;
1865                     wheel_accumulator += WHEEL_DELTA;
1866                 } else
1867                     break;
1868
1869                 if (send_raw_mouse) {
1870                     /* send a mouse-down followed by a mouse up */
1871                     
1872                     term_mouse(b,
1873                                MA_CLICK,
1874                                TO_CHR_X(X_POS(lParam)),
1875                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1876                                wParam & MK_CONTROL, is_alt_pressed());
1877                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1878                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1879                                wParam & MK_CONTROL, is_alt_pressed());
1880                 } else {
1881                     /* trigger a scroll */
1882                     term_scroll(0,
1883                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1884                 }
1885             }
1886             return 0;
1887         }
1888       case WM_LBUTTONDOWN:
1889       case WM_MBUTTONDOWN:
1890       case WM_RBUTTONDOWN:
1891       case WM_LBUTTONUP:
1892       case WM_MBUTTONUP:
1893       case WM_RBUTTONUP:
1894         {
1895             int button, press;
1896
1897             switch (message) {
1898               case WM_LBUTTONDOWN:
1899                 button = MBT_LEFT;
1900                 press = 1;
1901                 break;
1902               case WM_MBUTTONDOWN:
1903                 button = MBT_MIDDLE;
1904                 press = 1;
1905                 break;
1906               case WM_RBUTTONDOWN:
1907                 button = MBT_RIGHT;
1908                 press = 1;
1909                 break;
1910               case WM_LBUTTONUP:
1911                 button = MBT_LEFT;
1912                 press = 0;
1913                 break;
1914               case WM_MBUTTONUP:
1915                 button = MBT_MIDDLE;
1916                 press = 0;
1917                 break;
1918               case WM_RBUTTONUP:
1919                 button = MBT_RIGHT;
1920                 press = 0;
1921                 break;
1922               default:
1923                 button = press = 0;    /* shouldn't happen */
1924             }
1925             show_mouseptr(1);
1926             /*
1927              * Special case: in full-screen mode, if the left
1928              * button is clicked in the very top left corner of the
1929              * window, we put up the System menu instead of doing
1930              * selection.
1931              */
1932             if (is_full_screen() && press && button == MBT_LEFT &&
1933                 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1934                 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1935                 return 0;
1936             }
1937             if (press) {
1938                 click(button,
1939                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1940                       wParam & MK_SHIFT, wParam & MK_CONTROL,
1941                       is_alt_pressed());
1942                 SetCapture(hwnd);
1943             } else {
1944                 term_mouse(button, MA_RELEASE,
1945                            TO_CHR_X(X_POS(lParam)),
1946                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1947                            wParam & MK_CONTROL, is_alt_pressed());
1948                 ReleaseCapture();
1949             }
1950         }
1951         return 0;
1952       case WM_MOUSEMOVE:
1953         show_mouseptr(1);
1954         /*
1955          * Add the mouse position and message time to the random
1956          * number noise.
1957          */
1958         noise_ultralight(lParam);
1959
1960         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1961             Mouse_Button b;
1962             if (wParam & MK_LBUTTON)
1963                 b = MBT_LEFT;
1964             else if (wParam & MK_MBUTTON)
1965                 b = MBT_MIDDLE;
1966             else
1967                 b = MBT_RIGHT;
1968             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1969                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1970                        wParam & MK_CONTROL, is_alt_pressed());
1971         }
1972         return 0;
1973       case WM_NCMOUSEMOVE:
1974         show_mouseptr(1);
1975         noise_ultralight(lParam);
1976         return 0;
1977       case WM_IGNORE_CLIP:
1978         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1979         break;
1980       case WM_DESTROYCLIPBOARD:
1981         if (!ignore_clip)
1982             term_deselect();
1983         ignore_clip = FALSE;
1984         return 0;
1985       case WM_PAINT:
1986         {
1987             PAINTSTRUCT p;
1988             HideCaret(hwnd);
1989             hdc = BeginPaint(hwnd, &p);
1990             if (pal) {
1991                 SelectPalette(hdc, pal, TRUE);
1992                 RealizePalette(hdc);
1993             }
1994             term_paint(hdc, 
1995                        (p.rcPaint.left-offset_width)/font_width,
1996                        (p.rcPaint.top-offset_height)/font_height,
1997                        (p.rcPaint.right-offset_width-1)/font_width,
1998                        (p.rcPaint.bottom-offset_height-1)/font_height);
1999
2000             if (p.fErase ||
2001                 p.rcPaint.left  < offset_width  ||
2002                 p.rcPaint.top   < offset_height ||
2003                 p.rcPaint.right >= offset_width + font_width*cols ||
2004                 p.rcPaint.bottom>= offset_height + font_height*rows)
2005             {
2006                 HBRUSH fillcolour, oldbrush;
2007                 HPEN   edge, oldpen;
2008                 fillcolour = CreateSolidBrush (
2009                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2010                 oldbrush = SelectObject(hdc, fillcolour);
2011                 edge = CreatePen(PS_SOLID, 0, 
2012                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2013                 oldpen = SelectObject(hdc, edge);
2014
2015                 ExcludeClipRect(hdc, 
2016                         offset_width, offset_height,
2017                         offset_width+font_width*cols,
2018                         offset_height+font_height*rows);
2019
2020                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
2021                           p.rcPaint.right, p.rcPaint.bottom);
2022
2023                 // SelectClipRgn(hdc, NULL);
2024
2025                 SelectObject(hdc, oldbrush);
2026                 DeleteObject(fillcolour);
2027                 SelectObject(hdc, oldpen);
2028                 DeleteObject(edge);
2029             }
2030             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2031             SelectObject(hdc, GetStockObject(WHITE_PEN));
2032             EndPaint(hwnd, &p);
2033             ShowCaret(hwnd);
2034         }
2035         return 0;
2036       case WM_NETEVENT:
2037         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2038          * but the only one that's likely to try to overload us is FD_READ.
2039          * This means buffering just one is fine.
2040          */
2041         if (pending_netevent)
2042             enact_pending_netevent();
2043
2044         pending_netevent = TRUE;
2045         pend_netevent_wParam = wParam;
2046         pend_netevent_lParam = lParam;
2047         if (WSAGETSELECTEVENT(lParam) != FD_READ)
2048             enact_pending_netevent();
2049
2050         time(&last_movement);
2051         return 0;
2052       case WM_SETFOCUS:
2053         has_focus = TRUE;
2054         CreateCaret(hwnd, caretbm, font_width, font_height);
2055         ShowCaret(hwnd);
2056         flash_window(0);               /* stop */
2057         compose_state = 0;
2058         term_out();
2059         term_update();
2060         break;
2061       case WM_KILLFOCUS:
2062         show_mouseptr(1);
2063         has_focus = FALSE;
2064         DestroyCaret();
2065         term_out();
2066         term_update();
2067         break;
2068       case WM_ENTERSIZEMOVE:
2069 #ifdef RDB_DEBUG_PATCH
2070         debug((27, "WM_ENTERSIZEMOVE"));
2071 #endif
2072         EnableSizeTip(1);
2073         resizing = TRUE;
2074         need_backend_resize = FALSE;
2075         break;
2076       case WM_EXITSIZEMOVE:
2077         EnableSizeTip(0);
2078         resizing = FALSE;
2079 #ifdef RDB_DEBUG_PATCH
2080         debug((27, "WM_EXITSIZEMOVE"));
2081 #endif
2082         if (need_backend_resize) {
2083             term_size(cfg.height, cfg.width, cfg.savelines);
2084             InvalidateRect(hwnd, NULL, TRUE);
2085         }
2086         break;
2087       case WM_SIZING:
2088         /*
2089          * This does two jobs:
2090          * 1) Keep the sizetip uptodate
2091          * 2) Make sure the window size is _stepped_ in units of the font size.
2092          */
2093         if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2094             int width, height, w, h, ew, eh;
2095             LPRECT r = (LPRECT) lParam;
2096
2097             if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2098                     (cfg.height != rows || cfg.width != cols )) {
2099                 /* 
2100                  * Great! It seems that both the terminal size and the
2101                  * font size have been changed and the user is now dragging.
2102                  * 
2103                  * It will now be difficult to get back to the configured
2104                  * font size!
2105                  *
2106                  * This would be easier but it seems to be too confusing.
2107
2108                 term_size(cfg.height, cfg.width, cfg.savelines);
2109                 reset_window(2);
2110                  */
2111                 cfg.height=rows; cfg.width=cols;
2112
2113                 InvalidateRect(hwnd, NULL, TRUE);
2114                 need_backend_resize = TRUE;
2115             }
2116
2117             width = r->right - r->left - extra_width;
2118             height = r->bottom - r->top - extra_height;
2119             w = (width + font_width / 2) / font_width;
2120             if (w < 1)
2121                 w = 1;
2122             h = (height + font_height / 2) / font_height;
2123             if (h < 1)
2124                 h = 1;
2125             UpdateSizeTip(hwnd, w, h);
2126             ew = width - w * font_width;
2127             eh = height - h * font_height;
2128             if (ew != 0) {
2129                 if (wParam == WMSZ_LEFT ||
2130                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2131                     r->left += ew;
2132                 else
2133                     r->right -= ew;
2134             }
2135             if (eh != 0) {
2136                 if (wParam == WMSZ_TOP ||
2137                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2138                     r->top += eh;
2139                 else
2140                     r->bottom -= eh;
2141             }
2142             if (ew || eh)
2143                 return 1;
2144             else
2145                 return 0;
2146         } else {
2147             int width, height, w, h, rv = 0;
2148             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2149             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2150             LPRECT r = (LPRECT) lParam;
2151
2152             width = r->right - r->left - ex_width;
2153             height = r->bottom - r->top - ex_height;
2154
2155             w = (width + cols/2)/cols;
2156             h = (height + rows/2)/rows;
2157             if ( r->right != r->left + w*cols + ex_width) 
2158                 rv = 1;
2159
2160             if (wParam == WMSZ_LEFT ||
2161                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2162                 r->left = r->right - w*cols - ex_width;
2163             else
2164                 r->right = r->left + w*cols + ex_width;
2165
2166             if (r->bottom != r->top + h*rows + ex_height)
2167                 rv = 1;
2168
2169             if (wParam == WMSZ_TOP ||
2170                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2171                 r->top = r->bottom - h*rows - ex_height;
2172             else
2173                 r->bottom = r->top + h*rows + ex_height;
2174
2175             return rv;
2176         }
2177         /* break;  (never reached) */
2178       case WM_FULLSCR_ON_MAX:
2179         fullscr_on_max = TRUE;
2180         break;
2181       case WM_SIZE:
2182 #ifdef RDB_DEBUG_PATCH
2183         debug((27, "WM_SIZE %s (%d,%d)",
2184                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2185                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2186                 (wParam == SIZE_RESTORED && resizing) ? "to":
2187                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2188                 "...",
2189             LOWORD(lParam), HIWORD(lParam)));
2190 #endif
2191         if (wParam == SIZE_MINIMIZED)
2192             SetWindowText(hwnd,
2193                           cfg.win_name_always ? window_name : icon_name);
2194         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2195             SetWindowText(hwnd, window_name);
2196         if (wParam == SIZE_RESTORED)
2197             clear_full_screen();
2198         if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2199             make_full_screen();
2200             fullscr_on_max = FALSE;
2201         }
2202
2203         if (cfg.resize_action == RESIZE_DISABLED) {
2204             /* A resize, well it better be a minimize. */
2205             reset_window(-1);
2206         } else {
2207
2208             int width, height, w, h;
2209
2210             width = LOWORD(lParam);
2211             height = HIWORD(lParam);
2212
2213             if (!resizing) {
2214                 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2215                     was_zoomed = 1;
2216                     prev_rows = rows;
2217                     prev_cols = cols;
2218                     if (cfg.resize_action == RESIZE_TERM) {
2219                         w = width / font_width;
2220                         if (w < 1) w = 1;
2221                         h = height / font_height;
2222                         if (h < 1) h = 1;
2223
2224                         term_size(h, w, cfg.savelines);
2225                     }
2226                     reset_window(0);
2227                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2228                     was_zoomed = 0;
2229                     if (cfg.resize_action == RESIZE_TERM)
2230                         term_size(prev_rows, prev_cols, cfg.savelines);
2231                     if (cfg.resize_action != RESIZE_FONT)
2232                         reset_window(2);
2233                     else
2234                         reset_window(0);
2235                 }
2236                 /* This is an unexpected resize, these will normally happen
2237                  * if the window is too large. Probably either the user
2238                  * selected a huge font or the screen size has changed.
2239                  *
2240                  * This is also called with minimize.
2241                  */
2242                 else reset_window(-1);
2243             }
2244
2245             /*
2246              * Don't call back->size in mid-resize. (To prevent
2247              * massive numbers of resize events getting sent
2248              * down the connection during an NT opaque drag.)
2249              */
2250             if (resizing) {
2251                 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2252                     need_backend_resize = TRUE;
2253                     w = (width-cfg.window_border*2) / font_width;
2254                     if (w < 1) w = 1;
2255                     h = (height-cfg.window_border*2) / font_height;
2256                     if (h < 1) h = 1;
2257
2258                     cfg.height = h;
2259                     cfg.width = w;
2260                 } else 
2261                     reset_window(0);
2262             }
2263         }
2264         return 0;
2265       case WM_VSCROLL:
2266         switch (LOWORD(wParam)) {
2267           case SB_BOTTOM:
2268             term_scroll(-1, 0);
2269             break;
2270           case SB_TOP:
2271             term_scroll(+1, 0);
2272             break;
2273           case SB_LINEDOWN:
2274             term_scroll(0, +1);
2275             break;
2276           case SB_LINEUP:
2277             term_scroll(0, -1);
2278             break;
2279           case SB_PAGEDOWN:
2280             term_scroll(0, +rows / 2);
2281             break;
2282           case SB_PAGEUP:
2283             term_scroll(0, -rows / 2);
2284             break;
2285           case SB_THUMBPOSITION:
2286           case SB_THUMBTRACK:
2287             term_scroll(1, HIWORD(wParam));
2288             break;
2289         }
2290         break;
2291       case WM_PALETTECHANGED:
2292         if ((HWND) wParam != hwnd && pal != NULL) {
2293             HDC hdc = get_ctx();
2294             if (hdc) {
2295                 if (RealizePalette(hdc) > 0)
2296                     UpdateColors(hdc);
2297                 free_ctx(hdc);
2298             }
2299         }
2300         break;
2301       case WM_QUERYNEWPALETTE:
2302         if (pal != NULL) {
2303             HDC hdc = get_ctx();
2304             if (hdc) {
2305                 if (RealizePalette(hdc) > 0)
2306                     UpdateColors(hdc);
2307                 free_ctx(hdc);
2308                 return TRUE;
2309             }
2310         }
2311         return FALSE;
2312       case WM_KEYDOWN:
2313       case WM_SYSKEYDOWN:
2314       case WM_KEYUP:
2315       case WM_SYSKEYUP:
2316         /*
2317          * Add the scan code and keypress timing to the random
2318          * number noise.
2319          */
2320         noise_ultralight(lParam);
2321
2322         /*
2323          * We don't do TranslateMessage since it disassociates the
2324          * resulting CHAR message from the KEYDOWN that sparked it,
2325          * which we occasionally don't want. Instead, we process
2326          * KEYDOWN, and call the Win32 translator functions so that
2327          * we get the translations under _our_ control.
2328          */
2329         {
2330             unsigned char buf[20];
2331             int len;
2332
2333             if (wParam == VK_PROCESSKEY) {
2334                 MSG m;
2335                 m.hwnd = hwnd;
2336                 m.message = WM_KEYDOWN;
2337                 m.wParam = wParam;
2338                 m.lParam = lParam & 0xdfff;
2339                 TranslateMessage(&m);
2340             } else {
2341                 len = TranslateKey(message, wParam, lParam, buf);
2342                 if (len == -1)
2343                     return DefWindowProc(hwnd, message, wParam, lParam);
2344
2345                 if (len != 0) {
2346                     /*
2347                      * Interrupt an ongoing paste. I'm not sure
2348                      * this is sensible, but for the moment it's
2349                      * preferable to having to faff about buffering
2350                      * things.
2351                      */
2352                     term_nopaste();
2353
2354                     /*
2355                      * We need not bother about stdin backlogs
2356                      * here, because in GUI PuTTY we can't do
2357                      * anything about it anyway; there's no means
2358                      * of asking Windows to hold off on KEYDOWN
2359                      * messages. We _have_ to buffer everything
2360                      * we're sent.
2361                      */
2362                     ldisc_send(buf, len, 1);
2363                     show_mouseptr(0);
2364                 }
2365             }
2366         }
2367         net_pending_errors();
2368         return 0;
2369       case WM_INPUTLANGCHANGE:
2370         /* wParam == Font number */
2371         /* lParam == Locale */
2372         set_input_locale((HKL)lParam);
2373         break;
2374       case WM_IME_NOTIFY:
2375         if(wParam == IMN_SETOPENSTATUS) {
2376             HIMC hImc = ImmGetContext(hwnd);
2377             ImmSetCompositionFont(hImc, &lfont);
2378             ImmReleaseContext(hwnd, hImc);
2379             return 0;
2380         }
2381         break;
2382       case WM_IME_COMPOSITION:
2383         {
2384             HIMC hIMC;
2385             int n;
2386             char *buff;
2387    
2388             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2389                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2390
2391             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2392                 break; /* fall back to DefWindowProc */
2393
2394             hIMC = ImmGetContext(hwnd);
2395             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2396
2397             if (n > 0) {
2398                 buff = (char*) smalloc(n);
2399                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2400                 luni_send((unsigned short *)buff, n / 2, 1);
2401                 free(buff);
2402             }
2403             ImmReleaseContext(hwnd, hIMC);
2404             return 1;
2405         }
2406
2407       case WM_IME_CHAR:
2408         if (wParam & 0xFF00) {
2409             unsigned char buf[2];
2410
2411             buf[1] = wParam;
2412             buf[0] = wParam >> 8;
2413             lpage_send(kbd_codepage, buf, 2, 1);
2414         } else {
2415             char c = (unsigned char) wParam;
2416             lpage_send(kbd_codepage, &c, 1, 1);
2417         }
2418         return (0);
2419       case WM_CHAR:
2420       case WM_SYSCHAR:
2421         /*
2422          * Nevertheless, we are prepared to deal with WM_CHAR
2423          * messages, should they crop up. So if someone wants to
2424          * post the things to us as part of a macro manoeuvre,
2425          * we're ready to cope.
2426          */
2427         {
2428             char c = (unsigned char)wParam;
2429             lpage_send(CP_ACP, &c, 1, 1);
2430         }
2431         return 0;
2432       case WM_SETCURSOR:
2433         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2434             SetCursor(LoadCursor(NULL, IDC_ARROW));
2435             return TRUE;
2436         }
2437     }
2438
2439     return DefWindowProc(hwnd, message, wParam, lParam);
2440 }
2441
2442 /*
2443  * Move the system caret. (We maintain one, even though it's
2444  * invisible, for the benefit of blind people: apparently some
2445  * helper software tracks the system caret, so we should arrange to
2446  * have one.)
2447  */
2448 void sys_cursor(int x, int y)
2449 {
2450     COMPOSITIONFORM cf;
2451     HIMC hIMC;
2452
2453     if (!has_focus) return;
2454     
2455     SetCaretPos(x * font_width + offset_width,
2456                 y * font_height + offset_height);
2457
2458     /* IMM calls on Win98 and beyond only */
2459     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2460     
2461     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2462             osVersion.dwMinorVersion == 0) return; /* 95 */
2463
2464     /* we should have the IMM functions */
2465     hIMC = ImmGetContext(hwnd);
2466     cf.dwStyle = CFS_POINT;
2467     cf.ptCurrentPos.x = x * font_width + offset_width;
2468     cf.ptCurrentPos.y = y * font_height + offset_height;
2469     ImmSetCompositionWindow(hIMC, &cf);
2470
2471     ImmReleaseContext(hwnd, hIMC);
2472 }
2473
2474 /*
2475  * Draw a line of text in the window, at given character
2476  * coordinates, in given attributes.
2477  *
2478  * We are allowed to fiddle with the contents of `text'.
2479  */
2480 void do_text(Context ctx, int x, int y, char *text, int len,
2481              unsigned long attr, int lattr)
2482 {
2483     COLORREF fg, bg, t;
2484     int nfg, nbg, nfont;
2485     HDC hdc = ctx;
2486     RECT line_box;
2487     int force_manual_underline = 0;
2488     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2489     int char_width = fnt_width;
2490     int text_adjust = 0;
2491     static int *IpDx = 0, IpDxLEN = 0;
2492
2493     if (attr & ATTR_WIDE)
2494         char_width *= 2;
2495
2496     if (len > IpDxLEN || IpDx[0] != char_width) {
2497         int i;
2498         if (len > IpDxLEN) {
2499             sfree(IpDx);
2500             IpDx = smalloc((len + 16) * sizeof(int));
2501             IpDxLEN = (len + 16);
2502         }
2503         for (i = 0; i < IpDxLEN; i++)
2504             IpDx[i] = char_width;
2505     }
2506
2507     /* Only want the left half of double width lines */
2508     if (lattr != LATTR_NORM && x*2 >= cols)
2509         return;
2510
2511     x *= fnt_width;
2512     y *= font_height;
2513     x += offset_width;
2514     y += offset_height;
2515
2516     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2517         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2518         attr ^= ATTR_CUR_XOR;
2519     }
2520
2521     nfont = 0;
2522     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2523         /* Assume a poorman font is borken in other ways too. */
2524         lattr = LATTR_WIDE;
2525     } else
2526         switch (lattr) {
2527           case LATTR_NORM:
2528             break;
2529           case LATTR_WIDE:
2530             nfont |= FONT_WIDE;
2531             break;
2532           default:
2533             nfont |= FONT_WIDE + FONT_HIGH;
2534             break;
2535         }
2536     if (attr & ATTR_NARROW)
2537         nfont |= FONT_NARROW;
2538
2539     /* Special hack for the VT100 linedraw glyphs. */
2540     if ((attr & CSET_MASK) == 0x2300) {
2541         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2542             switch ((unsigned char) (text[0])) {
2543               case 0xBA:
2544                 text_adjust = -2 * font_height / 5;
2545                 break;
2546               case 0xBB:
2547                 text_adjust = -1 * font_height / 5;
2548                 break;
2549               case 0xBC:
2550                 text_adjust = font_height / 5;
2551                 break;
2552               case 0xBD:
2553                 text_adjust = 2 * font_height / 5;
2554                 break;
2555             }
2556             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2557                 text_adjust *= 2;
2558             attr &= ~CSET_MASK;
2559             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2560             attr |= (unitab_xterm['q'] & CSET_MASK);
2561             if (attr & ATTR_UNDER) {
2562                 attr &= ~ATTR_UNDER;
2563                 force_manual_underline = 1;
2564             }
2565         }
2566     }
2567
2568     /* Anything left as an original character set is unprintable. */
2569     if (DIRECT_CHAR(attr)) {
2570         attr &= ~CSET_MASK;
2571         attr |= 0xFF00;
2572         memset(text, 0xFD, len);
2573     }
2574
2575     /* OEM CP */
2576     if ((attr & CSET_MASK) == ATTR_OEMCP)
2577         nfont |= FONT_OEM;
2578
2579     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2580     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2581     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2582         nfont |= FONT_BOLD;
2583     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2584         nfont |= FONT_UNDERLINE;
2585     another_font(nfont);
2586     if (!fonts[nfont]) {
2587         if (nfont & FONT_UNDERLINE)
2588             force_manual_underline = 1;
2589         /* Don't do the same for manual bold, it could be bad news. */
2590
2591         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2592     }
2593     another_font(nfont);
2594     if (!fonts[nfont])
2595         nfont = FONT_NORMAL;
2596     if (attr & ATTR_REVERSE) {
2597         t = nfg;
2598         nfg = nbg;
2599         nbg = t;
2600     }
2601     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2602         nfg++;
2603     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2604         nbg++;
2605     fg = colours[nfg];
2606     bg = colours[nbg];
2607     SelectObject(hdc, fonts[nfont]);
2608     SetTextColor(hdc, fg);
2609     SetBkColor(hdc, bg);
2610     SetBkMode(hdc, OPAQUE);
2611     line_box.left = x;
2612     line_box.top = y;
2613     line_box.right = x + char_width * len;
2614     line_box.bottom = y + font_height;
2615
2616     /* Only want the left half of double width lines */
2617     if (line_box.right > font_width*cols+offset_width)
2618         line_box.right = font_width*cols+offset_width;
2619
2620     /* We're using a private area for direct to font. (512 chars.) */
2621     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2622         /* Ho Hum, dbcs fonts are a PITA! */
2623         /* To display on W9x I have to convert to UCS */
2624         static wchar_t *uni_buf = 0;
2625         static int uni_len = 0;
2626         int nlen, mptr;
2627         if (len > uni_len) {
2628             sfree(uni_buf);
2629             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2630         }
2631
2632         for(nlen = mptr = 0; mptr<len; mptr++) {
2633             uni_buf[nlen] = 0xFFFD;
2634             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2635                 IpDx[nlen] += char_width;
2636                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2637                                    text+mptr, 2, uni_buf+nlen, 1);
2638                 mptr++;
2639             }
2640             else
2641             {
2642                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2643                                    text+mptr, 1, uni_buf+nlen, 1);
2644             }
2645             nlen++;
2646         }
2647         if (nlen <= 0)
2648             return;                    /* Eeek! */
2649
2650         ExtTextOutW(hdc, x,
2651                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2652                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2653         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2654             SetBkMode(hdc, TRANSPARENT);
2655             ExtTextOutW(hdc, x - 1,
2656                         y - font_height * (lattr ==
2657                                            LATTR_BOT) + text_adjust,
2658                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2659         }
2660
2661         IpDx[0] = -1;
2662     } else if (DIRECT_FONT(attr)) {
2663         ExtTextOut(hdc, x,
2664                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2665                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2666         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2667             SetBkMode(hdc, TRANSPARENT);
2668
2669             /* GRR: This draws the character outside it's box and can leave
2670              * 'droppings' even with the clip box! I suppose I could loop it
2671              * one character at a time ... yuk. 
2672              * 
2673              * Or ... I could do a test print with "W", and use +1 or -1 for this
2674              * shift depending on if the leftmost column is blank...
2675              */
2676             ExtTextOut(hdc, x - 1,
2677                        y - font_height * (lattr ==
2678                                           LATTR_BOT) + text_adjust,
2679                        ETO_CLIPPED, &line_box, text, len, IpDx);
2680         }
2681     } else {
2682         /* And 'normal' unicode characters */
2683         static WCHAR *wbuf = NULL;
2684         static int wlen = 0;
2685         int i;
2686         if (wlen < len) {
2687             sfree(wbuf);
2688             wlen = len;
2689             wbuf = smalloc(wlen * sizeof(WCHAR));
2690         }
2691         for (i = 0; i < len; i++)
2692             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2693
2694         ExtTextOutW(hdc, x,
2695                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2696                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2697
2698         /* And the shadow bold hack. */
2699         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2700             SetBkMode(hdc, TRANSPARENT);
2701             ExtTextOutW(hdc, x - 1,
2702                         y - font_height * (lattr ==
2703                                            LATTR_BOT) + text_adjust,
2704                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2705         }
2706     }
2707     if (lattr != LATTR_TOP && (force_manual_underline ||
2708                                (und_mode == UND_LINE
2709                                 && (attr & ATTR_UNDER)))) {
2710         HPEN oldpen;
2711         int dec = descent;
2712         if (lattr == LATTR_BOT)
2713             dec = dec * 2 - font_height;
2714
2715         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2716         MoveToEx(hdc, x, y + dec, NULL);
2717         LineTo(hdc, x + len * char_width, y + dec);
2718         oldpen = SelectObject(hdc, oldpen);
2719         DeleteObject(oldpen);
2720     }
2721 }
2722
2723 void do_cursor(Context ctx, int x, int y, char *text, int len,
2724                unsigned long attr, int lattr)
2725 {
2726
2727     int fnt_width;
2728     int char_width;
2729     HDC hdc = ctx;
2730     int ctype = cfg.cursor_type;
2731
2732     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2733         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2734             do_text(ctx, x, y, text, len, attr, lattr);
2735             return;
2736         }
2737         ctype = 2;
2738         attr |= TATTR_RIGHTCURS;
2739     }
2740
2741     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2742     if (attr & ATTR_WIDE)
2743         char_width *= 2;
2744     x *= fnt_width;
2745     y *= font_height;
2746     x += offset_width;
2747     y += offset_height;
2748
2749     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2750         POINT pts[5];
2751         HPEN oldpen;
2752         pts[0].x = pts[1].x = pts[4].x = x;
2753         pts[2].x = pts[3].x = x + char_width - 1;
2754         pts[0].y = pts[3].y = pts[4].y = y;
2755         pts[1].y = pts[2].y = y + font_height - 1;
2756         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2757         Polyline(hdc, pts, 5);
2758         oldpen = SelectObject(hdc, oldpen);
2759         DeleteObject(oldpen);
2760     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2761         int startx, starty, dx, dy, length, i;
2762         if (ctype == 1) {
2763             startx = x;
2764             starty = y + descent;
2765             dx = 1;
2766             dy = 0;
2767             length = char_width;
2768         } else {
2769             int xadjust = 0;
2770             if (attr & TATTR_RIGHTCURS)
2771                 xadjust = char_width - 1;
2772             startx = x + xadjust;
2773             starty = y;
2774             dx = 0;
2775             dy = 1;
2776             length = font_height;
2777         }
2778         if (attr & TATTR_ACTCURS) {
2779             HPEN oldpen;
2780             oldpen =
2781                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2782             MoveToEx(hdc, startx, starty, NULL);
2783             LineTo(hdc, startx + dx * length, starty + dy * length);
2784             oldpen = SelectObject(hdc, oldpen);
2785             DeleteObject(oldpen);
2786         } else {
2787             for (i = 0; i < length; i++) {
2788                 if (i % 2 == 0) {
2789                     SetPixel(hdc, startx, starty, colours[23]);
2790                 }
2791                 startx += dx;
2792                 starty += dy;
2793             }
2794         }
2795     }
2796 }
2797
2798 /* This function gets the actual width of a character in the normal font.
2799  */
2800 int CharWidth(Context ctx, int uc) {
2801     HDC hdc = ctx;
2802     int ibuf = 0;
2803
2804     /* If the font max is the same as the font ave width then this
2805      * function is a no-op.
2806      */
2807     if (!font_dualwidth) return 1;
2808
2809     switch (uc & CSET_MASK) {
2810       case ATTR_ASCII:
2811         uc = unitab_line[uc & 0xFF];
2812         break;
2813       case ATTR_LINEDRW:
2814         uc = unitab_xterm[uc & 0xFF];
2815         break;
2816       case ATTR_SCOACS:
2817         uc = unitab_scoacs[uc & 0xFF];
2818         break;
2819     }
2820     if (DIRECT_FONT(uc)) {
2821         if (dbcs_screenfont) return 1;
2822
2823         /* Speedup, I know of no font where ascii is the wrong width */
2824         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2825             return 1;
2826
2827         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2828             SelectObject(hdc, fonts[FONT_NORMAL]);
2829         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2830             another_font(FONT_OEM);
2831             if (!fonts[FONT_OEM]) return 0;
2832
2833             SelectObject(hdc, fonts[FONT_OEM]);
2834         } else
2835             return 0;
2836
2837         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2838              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2839             return 0;
2840     } else {
2841         /* Speedup, I know of no font where ascii is the wrong width */
2842         if (uc >= ' ' && uc <= '~') return 1;
2843
2844         SelectObject(hdc, fonts[FONT_NORMAL]);
2845         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2846             /* Okay that one worked */ ;
2847         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2848             /* This should work on 9x too, but it's "less accurate" */ ;
2849         else
2850             return 0;
2851     }
2852
2853     ibuf += font_width / 2 -1;
2854     ibuf /= font_width;
2855
2856     return ibuf;
2857 }
2858
2859 /*
2860  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2861  * codes. Returns number of bytes used or zero to drop the message
2862  * or -1 to forward the message to windows.
2863  */
2864 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2865                         unsigned char *output)
2866 {
2867     BYTE keystate[256];
2868     int scan, left_alt = 0, key_down, shift_state;
2869     int r, i, code;
2870     unsigned char *p = output;
2871     static int alt_sum = 0;
2872
2873     HKL kbd_layout = GetKeyboardLayout(0);
2874
2875     static WORD keys[3];
2876     static int compose_char = 0;
2877     static WPARAM compose_key = 0;
2878
2879     r = GetKeyboardState(keystate);
2880     if (!r)
2881         memset(keystate, 0, sizeof(keystate));
2882     else {
2883 #if 0
2884 #define SHOW_TOASCII_RESULT
2885         {                              /* Tell us all about key events */
2886             static BYTE oldstate[256];
2887             static int first = 1;
2888             static int scan;
2889             int ch;
2890             if (first)
2891                 memcpy(oldstate, keystate, sizeof(oldstate));
2892             first = 0;
2893
2894             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2895                 debug(("+"));
2896             } else if ((HIWORD(lParam) & KF_UP)
2897                        && scan == (HIWORD(lParam) & 0xFF)) {
2898                 debug((". U"));
2899             } else {
2900                 debug((".\n"));
2901                 if (wParam >= VK_F1 && wParam <= VK_F20)
2902                     debug(("K_F%d", wParam + 1 - VK_F1));
2903                 else
2904                     switch (wParam) {
2905                       case VK_SHIFT:
2906                         debug(("SHIFT"));
2907                         break;
2908                       case VK_CONTROL:
2909                         debug(("CTRL"));
2910                         break;
2911                       case VK_MENU:
2912                         debug(("ALT"));
2913                         break;
2914                       default:
2915                         debug(("VK_%02x", wParam));
2916                     }
2917                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2918                     debug(("*"));
2919                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2920
2921                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2922                 if (ch >= ' ' && ch <= '~')
2923                     debug((", '%c'", ch));
2924                 else if (ch)
2925                     debug((", $%02x", ch));
2926
2927                 if (keys[0])
2928                     debug((", KB0=%02x", keys[0]));
2929                 if (keys[1])
2930                     debug((", KB1=%02x", keys[1]));
2931                 if (keys[2])
2932                     debug((", KB2=%02x", keys[2]));
2933
2934                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2935                     debug((", S"));
2936                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2937                     debug((", C"));
2938                 if ((HIWORD(lParam) & KF_EXTENDED))
2939                     debug((", E"));
2940                 if ((HIWORD(lParam) & KF_UP))
2941                     debug((", U"));
2942             }
2943
2944             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2945             else if ((HIWORD(lParam) & KF_UP))
2946                 oldstate[wParam & 0xFF] ^= 0x80;
2947             else
2948                 oldstate[wParam & 0xFF] ^= 0x81;
2949
2950             for (ch = 0; ch < 256; ch++)
2951                 if (oldstate[ch] != keystate[ch])
2952                     debug((", M%02x=%02x", ch, keystate[ch]));
2953
2954             memcpy(oldstate, keystate, sizeof(oldstate));
2955         }
2956 #endif
2957
2958         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2959             keystate[VK_RMENU] = keystate[VK_MENU];
2960         }
2961
2962
2963         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2964         if ((cfg.funky_type == 3 ||
2965              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2966             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2967
2968             wParam = VK_EXECUTE;
2969
2970             /* UnToggle NUMLock */
2971             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2972                 keystate[VK_NUMLOCK] ^= 1;
2973         }
2974
2975         /* And write back the 'adjusted' state */
2976         SetKeyboardState(keystate);
2977     }
2978
2979     /* Disable Auto repeat if required */
2980     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2981         return 0;
2982
2983     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2984         left_alt = 1;
2985
2986     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2987
2988     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2989     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2990         if (cfg.ctrlaltkeys)
2991             keystate[VK_MENU] = 0;
2992         else {
2993             keystate[VK_RMENU] = 0x80;
2994             left_alt = 0;
2995         }
2996     }
2997
2998     alt_pressed = (left_alt && key_down);
2999
3000     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3001     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3002         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3003
3004     /* Note if AltGr was pressed and if it was used as a compose key */
3005     if (!compose_state) {
3006         compose_key = 0x100;
3007         if (cfg.compose_key) {
3008             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3009                 compose_key = wParam;
3010         }
3011         if (wParam == VK_APPS)
3012             compose_key = wParam;
3013     }
3014
3015     if (wParam == compose_key) {
3016         if (compose_state == 0
3017             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3018                 1;
3019         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3020             compose_state = 2;
3021         else
3022             compose_state = 0;
3023     } else if (compose_state == 1 && wParam != VK_CONTROL)
3024         compose_state = 0;
3025
3026     /* 
3027      * Record that we pressed key so the scroll window can be reset, but
3028      * be careful to avoid Shift-UP/Down
3029      */
3030     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3031         wParam != VK_MENU && wParam != VK_CONTROL) {
3032         seen_key_event = 1;
3033     }
3034
3035     if (compose_state > 1 && left_alt)
3036         compose_state = 0;
3037
3038     /* Sanitize the number pad if not using a PC NumPad */
3039     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3040                      && cfg.funky_type != 2)
3041         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3042         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3043             int nParam = 0;
3044             switch (wParam) {
3045               case VK_INSERT:
3046                 nParam = VK_NUMPAD0;
3047                 break;
3048               case VK_END:
3049                 nParam = VK_NUMPAD1;
3050                 break;
3051               case VK_DOWN:
3052                 nParam = VK_NUMPAD2;
3053                 break;
3054               case VK_NEXT:
3055                 nParam = VK_NUMPAD3;
3056                 break;
3057               case VK_LEFT:
3058                 nParam = VK_NUMPAD4;
3059                 break;
3060               case VK_CLEAR:
3061                 nParam = VK_NUMPAD5;
3062                 break;
3063               case VK_RIGHT:
3064                 nParam = VK_NUMPAD6;
3065                 break;
3066               case VK_HOME:
3067                 nParam = VK_NUMPAD7;
3068                 break;
3069               case VK_UP:
3070                 nParam = VK_NUMPAD8;
3071                 break;
3072               case VK_PRIOR:
3073                 nParam = VK_NUMPAD9;
3074                 break;
3075               case VK_DELETE:
3076                 nParam = VK_DECIMAL;
3077                 break;
3078             }
3079             if (nParam) {
3080                 if (keystate[VK_NUMLOCK] & 1)
3081                     shift_state |= 1;
3082                 wParam = nParam;
3083             }
3084         }
3085     }
3086
3087     /* If a key is pressed and AltGr is not active */
3088     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3089         /* Okay, prepare for most alts then ... */
3090         if (left_alt)
3091             *p++ = '\033';
3092
3093         /* Lets see if it's a pattern we know all about ... */
3094         if (wParam == VK_PRIOR && shift_state == 1) {
3095             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3096             return 0;
3097         }
3098         if (wParam == VK_NEXT && shift_state == 1) {
3099             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3100             return 0;
3101         }
3102         if (wParam == VK_INSERT && shift_state == 1) {
3103             term_do_paste();
3104             return 0;
3105         }
3106         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3107             return -1;
3108         }
3109         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3110             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3111             return -1;
3112         }
3113         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3114             (cfg.resize_action != RESIZE_DISABLED)) {
3115             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3116                 flip_full_screen();
3117             return -1;
3118         }
3119         /* Control-Numlock for app-keypad mode switch */
3120         if (wParam == VK_PAUSE && shift_state == 2) {
3121             app_keypad_keys ^= 1;
3122             return 0;
3123         }
3124
3125         /* Nethack keypad */
3126         if (cfg.nethack_keypad && !left_alt) {
3127             switch (wParam) {
3128               case VK_NUMPAD1:
3129                 *p++ = shift_state ? 'B' : 'b';
3130                 return p - output;
3131               case VK_NUMPAD2:
3132                 *p++ = shift_state ? 'J' : 'j';
3133                 return p - output;
3134               case VK_NUMPAD3:
3135                 *p++ = shift_state ? 'N' : 'n';
3136                 return p - output;
3137               case VK_NUMPAD4:
3138                 *p++ = shift_state ? 'H' : 'h';
3139                 return p - output;
3140               case VK_NUMPAD5:
3141                 *p++ = shift_state ? '.' : '.';
3142                 return p - output;
3143               case VK_NUMPAD6:
3144                 *p++ = shift_state ? 'L' : 'l';
3145                 return p - output;
3146               case VK_NUMPAD7:
3147                 *p++ = shift_state ? 'Y' : 'y';
3148                 return p - output;
3149               case VK_NUMPAD8:
3150                 *p++ = shift_state ? 'K' : 'k';
3151                 return p - output;
3152               case VK_NUMPAD9:
3153                 *p++ = shift_state ? 'U' : 'u';
3154                 return p - output;
3155             }
3156         }
3157
3158         /* Application Keypad */
3159         if (!left_alt) {
3160             int xkey = 0;
3161
3162             if (cfg.funky_type == 3 ||
3163                 (cfg.funky_type <= 1 &&
3164                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3165                   case VK_EXECUTE:
3166                     xkey = 'P';
3167                     break;
3168                   case VK_DIVIDE:
3169                     xkey = 'Q';
3170                     break;
3171                   case VK_MULTIPLY:
3172                     xkey = 'R';
3173                     break;
3174                   case VK_SUBTRACT:
3175                     xkey = 'S';
3176                     break;
3177                 }
3178             if (app_keypad_keys && !cfg.no_applic_k)
3179                 switch (wParam) {
3180                   case VK_NUMPAD0:
3181                     xkey = 'p';
3182                     break;
3183                   case VK_NUMPAD1:
3184                     xkey = 'q';
3185                     break;
3186                   case VK_NUMPAD2:
3187                     xkey = 'r';
3188                     break;
3189                   case VK_NUMPAD3:
3190                     xkey = 's';
3191                     break;
3192                   case VK_NUMPAD4:
3193                     xkey = 't';
3194                     break;
3195                   case VK_NUMPAD5:
3196                     xkey = 'u';
3197                     break;
3198                   case VK_NUMPAD6:
3199                     xkey = 'v';
3200                     break;
3201                   case VK_NUMPAD7:
3202                     xkey = 'w';
3203                     break;
3204                   case VK_NUMPAD8:
3205                     xkey = 'x';
3206                     break;
3207                   case VK_NUMPAD9:
3208                     xkey = 'y';
3209                     break;
3210
3211                   case VK_DECIMAL:
3212                     xkey = 'n';
3213                     break;
3214                   case VK_ADD:
3215                     if (cfg.funky_type == 2) {
3216                         if (shift_state)
3217                             xkey = 'l';
3218                         else
3219                             xkey = 'k';
3220                     } else if (shift_state)
3221                         xkey = 'm';
3222                     else
3223                         xkey = 'l';
3224                     break;
3225
3226                   case VK_DIVIDE:
3227                     if (cfg.funky_type == 2)
3228                         xkey = 'o';
3229                     break;
3230                   case VK_MULTIPLY:
3231                     if (cfg.funky_type == 2)
3232                         xkey = 'j';
3233                     break;
3234                   case VK_SUBTRACT:
3235                     if (cfg.funky_type == 2)
3236                         xkey = 'm';
3237                     break;
3238
3239                   case VK_RETURN:
3240                     if (HIWORD(lParam) & KF_EXTENDED)
3241                         xkey = 'M';
3242                     break;
3243                 }
3244             if (xkey) {
3245                 if (vt52_mode) {
3246                     if (xkey >= 'P' && xkey <= 'S')
3247                         p += sprintf((char *) p, "\x1B%c", xkey);
3248                     else
3249                         p += sprintf((char *) p, "\x1B?%c", xkey);
3250                 } else
3251                     p += sprintf((char *) p, "\x1BO%c", xkey);
3252                 return p - output;
3253             }
3254         }
3255
3256         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3257             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3258             *p++ = 0;
3259             return -2;
3260         }
3261         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3262             *p++ = 0x1B;
3263             *p++ = '[';
3264             *p++ = 'Z';
3265             return p - output;
3266         }
3267         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3268             *p++ = 0;
3269             return p - output;
3270         }
3271         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3272             *p++ = 160;
3273             return p - output;
3274         }
3275         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3276             *p++ = 3;
3277             *p++ = 0;
3278             return -2;
3279         }
3280         if (wParam == VK_PAUSE) {      /* Break/Pause */
3281             *p++ = 26;
3282             *p++ = 0;
3283             return -2;
3284         }
3285         /* Control-2 to Control-8 are special */
3286         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3287             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3288             return p - output;
3289         }
3290         if (shift_state == 2 && wParam == 0xBD) {
3291             *p++ = 0x1F;
3292             return p - output;
3293         }
3294         if (shift_state == 2 && wParam == 0xDF) {
3295             *p++ = 0x1C;
3296             return p - output;
3297         }
3298         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3299             *p++ = '\r';
3300             *p++ = '\n';
3301             return p - output;
3302         }
3303
3304         /*
3305          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3306          * for integer decimal nn.)
3307          *
3308          * We also deal with the weird ones here. Linux VCs replace F1
3309          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3310          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3311          * respectively.
3312          */
3313         code = 0;
3314         switch (wParam) {
3315           case VK_F1:
3316             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3317             break;
3318           case VK_F2:
3319             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3320             break;
3321           case VK_F3:
3322             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3323             break;
3324           case VK_F4:
3325             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3326             break;
3327           case VK_F5:
3328             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3329             break;
3330           case VK_F6:
3331             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3332             break;
3333           case VK_F7:
3334             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3335             break;
3336           case VK_F8:
3337             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3338             break;
3339           case VK_F9:
3340             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3341             break;
3342           case VK_F10:
3343             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3344             break;
3345           case VK_F11:
3346             code = 23;
3347             break;
3348           case VK_F12:
3349             code = 24;
3350             break;
3351           case VK_F13:
3352             code = 25;
3353             break;
3354           case VK_F14:
3355             code = 26;
3356             break;
3357           case VK_F15:
3358             code = 28;
3359             break;
3360           case VK_F16:
3361             code = 29;
3362             break;
3363           case VK_F17:
3364             code = 31;
3365             break;
3366           case VK_F18:
3367             code = 32;
3368             break;
3369           case VK_F19:
3370             code = 33;
3371             break;
3372           case VK_F20:
3373             code = 34;
3374             break;
3375         }
3376         if ((shift_state&2) == 0) switch (wParam) {
3377           case VK_HOME:
3378             code = 1;
3379             break;
3380           case VK_INSERT:
3381             code = 2;
3382             break;
3383           case VK_DELETE:
3384             code = 3;
3385             break;
3386           case VK_END:
3387             code = 4;
3388             break;
3389           case VK_PRIOR:
3390             code = 5;
3391             break;
3392           case VK_NEXT:
3393             code = 6;
3394             break;
3395         }
3396         /* Reorder edit keys to physical order */
3397         if (cfg.funky_type == 3 && code <= 6)
3398             code = "\0\2\1\4\5\3\6"[code];
3399
3400         if (vt52_mode && code > 0 && code <= 6) {
3401             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3402             return p - output;
3403         }
3404
3405         if (cfg.funky_type == 5 &&     /* SCO function keys */
3406             code >= 11 && code <= 34) {
3407             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3408             int index = 0;
3409             switch (wParam) {
3410               case VK_F1: index = 0; break;
3411               case VK_F2: index = 1; break;
3412               case VK_F3: index = 2; break;
3413               case VK_F4: index = 3; break;
3414               case VK_F5: index = 4; break;
3415               case VK_F6: index = 5; break;
3416               case VK_F7: index = 6; break;
3417               case VK_F8: index = 7; break;
3418               case VK_F9: index = 8; break;
3419               case VK_F10: index = 9; break;
3420               case VK_F11: index = 10; break;
3421               case VK_F12: index = 11; break;
3422             }
3423             if (keystate[VK_SHIFT] & 0x80) index += 12;
3424             if (keystate[VK_CONTROL] & 0x80) index += 24;
3425             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3426             return p - output;
3427         }
3428         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3429             code >= 1 && code <= 6) {
3430             char codes[] = "HL.FIG";
3431             if (code == 3) {
3432                 *p++ = '\x7F';
3433             } else {
3434                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3435             }
3436             return p - output;
3437         }
3438         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3439             int offt = 0;
3440             if (code > 15)
3441                 offt++;
3442             if (code > 21)
3443                 offt++;
3444             if (vt52_mode)
3445                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3446             else
3447                 p +=
3448                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3449             return p - output;
3450         }
3451         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3452             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3453             return p - output;
3454         }
3455         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3456             if (vt52_mode)
3457                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3458             else
3459                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3460             return p - output;
3461         }
3462         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3463             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3464             return p - output;
3465         }
3466         if (code) {
3467             p += sprintf((char *) p, "\x1B[%d~", code);
3468             return p - output;
3469         }
3470
3471         /*
3472          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3473          * some reason seems to send VK_CLEAR to Windows...).
3474          */
3475         {
3476             char xkey = 0;
3477             switch (wParam) {
3478               case VK_UP:
3479                 xkey = 'A';
3480                 break;
3481               case VK_DOWN:
3482                 xkey = 'B';
3483                 break;
3484               case VK_RIGHT:
3485                 xkey = 'C';
3486                 break;
3487               case VK_LEFT:
3488                 xkey = 'D';
3489                 break;
3490               case VK_CLEAR:
3491                 xkey = 'G';
3492                 break;
3493             }
3494             if (xkey) {
3495                 if (vt52_mode)
3496                     p += sprintf((char *) p, "\x1B%c", xkey);
3497                 else {
3498                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3499                     /* VT100 & VT102 manuals both state the app cursor keys
3500                      * only work if the app keypad is on.
3501                      */
3502                     if (!app_keypad_keys)
3503                         app_flg = 0;
3504                     /* Useful mapping of Ctrl-arrows */
3505                     if (shift_state == 2)
3506                         app_flg = !app_flg;
3507
3508                     if (app_flg)
3509                         p += sprintf((char *) p, "\x1BO%c", xkey);
3510                     else
3511                         p += sprintf((char *) p, "\x1B[%c", xkey);
3512                 }
3513                 return p - output;
3514             }
3515         }
3516
3517         /*
3518          * Finally, deal with Return ourselves. (Win95 seems to
3519          * foul it up when Alt is pressed, for some reason.)
3520          */
3521         if (wParam == VK_RETURN) {     /* Return */
3522             *p++ = 0x0D;
3523             *p++ = 0;
3524             return -2;
3525         }
3526
3527         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3528             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3529         else
3530             alt_sum = 0;
3531     }
3532
3533     /* Okay we've done everything interesting; let windows deal with 
3534      * the boring stuff */
3535     {
3536         BOOL capsOn=0;
3537
3538         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3539         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3540             capsOn= !left_alt;
3541             keystate[VK_CAPITAL] = 0;
3542         }
3543
3544         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3545 #ifdef SHOW_TOASCII_RESULT
3546         if (r == 1 && !key_down) {
3547             if (alt_sum) {
3548                 if (in_utf || dbcs_screenfont)
3549                     debug((", (U+%04x)", alt_sum));
3550                 else
3551                     debug((", LCH(%d)", alt_sum));
3552             } else {
3553                 debug((", ACH(%d)", keys[0]));
3554             }
3555         } else if (r > 0) {
3556             int r1;
3557             debug((", ASC("));
3558             for (r1 = 0; r1 < r; r1++) {
3559                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3560             }
3561             debug((")"));
3562         }
3563 #endif
3564         if (r > 0) {
3565             WCHAR keybuf;
3566
3567             /*
3568              * Interrupt an ongoing paste. I'm not sure this is
3569              * sensible, but for the moment it's preferable to
3570              * having to faff about buffering things.
3571              */
3572             term_nopaste();
3573
3574             p = output;
3575             for (i = 0; i < r; i++) {
3576                 unsigned char ch = (unsigned char) keys[i];
3577
3578                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3579                     compose_char = ch;
3580                     compose_state++;
3581                     continue;
3582                 }
3583                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3584                     int nc;
3585                     compose_state = 0;
3586
3587                     if ((nc = check_compose(compose_char, ch)) == -1) {
3588                         MessageBeep(MB_ICONHAND);
3589                         return 0;
3590                     }
3591                     keybuf = nc;
3592                     luni_send(&keybuf, 1, 1);
3593                     continue;
3594                 }
3595
3596                 compose_state = 0;
3597
3598                 if (!key_down) {
3599                     if (alt_sum) {
3600                         if (in_utf || dbcs_screenfont) {
3601                             keybuf = alt_sum;
3602                             luni_send(&keybuf, 1, 1);
3603                         } else {
3604                             ch = (char) alt_sum;
3605                             /*
3606                              * We need not bother about stdin
3607                              * backlogs here, because in GUI PuTTY
3608                              * we can't do anything about it
3609                              * anyway; there's no means of asking
3610                              * Windows to hold off on KEYDOWN
3611                              * messages. We _have_ to buffer
3612                              * everything we're sent.
3613                              */
3614                             ldisc_send(&ch, 1, 1);
3615                         }
3616                         alt_sum = 0;
3617                     } else
3618                         lpage_send(kbd_codepage, &ch, 1, 1);
3619                 } else {
3620                     if(capsOn && ch < 0x80) {
3621                         WCHAR cbuf[2];
3622                         cbuf[0] = 27;
3623                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3624                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3625                     } else {
3626                         char cbuf[2];
3627                         cbuf[0] = '\033';
3628                         cbuf[1] = ch;
3629                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3630                     }
3631                 }
3632                 show_mouseptr(0);
3633             }
3634
3635             /* This is so the ALT-Numpad and dead keys work correctly. */
3636             keys[0] = 0;
3637
3638             return p - output;
3639         }
3640         /* If we're definitly not building up an ALT-54321 then clear it */
3641         if (!left_alt)
3642             keys[0] = 0;
3643         /* If we will be using alt_sum fix the 256s */
3644         else if (keys[0] && (in_utf || dbcs_screenfont))
3645             keys[0] = 10;
3646     }
3647
3648     /*
3649      * ALT alone may or may not want to bring up the System menu.
3650      * If it's not meant to, we return 0 on presses or releases of
3651      * ALT, to show that we've swallowed the keystroke. Otherwise
3652      * we return -1, which means Windows will give the keystroke
3653      * its default handling (i.e. bring up the System menu).
3654      */
3655     if (wParam == VK_MENU && !cfg.alt_only)
3656         return 0;
3657
3658     return -1;
3659 }
3660
3661 void set_title(char *title)
3662 {
3663     sfree(window_name);
3664     window_name = smalloc(1 + strlen(title));
3665     strcpy(window_name, title);
3666     if (cfg.win_name_always || !IsIconic(hwnd))
3667         SetWindowText(hwnd, title);
3668 }
3669
3670 void set_icon(char *title)
3671 {
3672     sfree(icon_name);
3673     icon_name = smalloc(1 + strlen(title));
3674     strcpy(icon_name, title);
3675     if (!cfg.win_name_always && IsIconic(hwnd))
3676         SetWindowText(hwnd, title);
3677 }
3678
3679 void set_sbar(int total, int start, int page)
3680 {
3681     SCROLLINFO si;
3682
3683     if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3684         return;
3685
3686     si.cbSize = sizeof(si);
3687     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3688     si.nMin = 0;
3689     si.nMax = total - 1;
3690     si.nPage = page;
3691     si.nPos = start;
3692     if (hwnd)
3693         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3694 }
3695
3696 Context get_ctx(void)
3697 {
3698     HDC hdc;
3699     if (hwnd) {
3700         hdc = GetDC(hwnd);
3701         if (hdc && pal)
3702             SelectPalette(hdc, pal, FALSE);
3703         return hdc;
3704     } else
3705         return NULL;
3706 }
3707
3708 void free_ctx(Context ctx)
3709 {
3710     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3711     ReleaseDC(hwnd, ctx);
3712 }
3713
3714 static void real_palette_set(int n, int r, int g, int b)
3715 {
3716     if (pal) {
3717         logpal->palPalEntry[n].peRed = r;
3718         logpal->palPalEntry[n].peGreen = g;
3719         logpal->palPalEntry[n].peBlue = b;
3720         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3721         colours[n] = PALETTERGB(r, g, b);
3722         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3723     } else
3724         colours[n] = RGB(r, g, b);
3725 }
3726
3727 void palette_set(int n, int r, int g, int b)
3728 {
3729     static const int first[21] = {
3730         0, 2, 4, 6, 8, 10, 12, 14,
3731         1, 3, 5, 7, 9, 11, 13, 15,
3732         16, 17, 18, 20, 22
3733     };
3734     real_palette_set(first[n], r, g, b);
3735     if (first[n] >= 18)
3736         real_palette_set(first[n] + 1, r, g, b);
3737     if (pal) {
3738         HDC hdc = get_ctx();
3739         UnrealizeObject(pal);
3740         RealizePalette(hdc);
3741         free_ctx(hdc);
3742     }
3743 }
3744
3745 void palette_reset(void)
3746 {
3747     int i;
3748
3749     for (i = 0; i < NCOLOURS; i++) {
3750         if (pal) {
3751             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3752             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3753             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3754             logpal->palPalEntry[i].peFlags = 0;
3755             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3756                                     defpal[i].rgbtGreen,
3757                                     defpal[i].rgbtBlue);
3758         } else
3759             colours[i] = RGB(defpal[i].rgbtRed,
3760                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3761     }
3762
3763     if (pal) {
3764         HDC hdc;
3765         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3766         hdc = get_ctx();
3767         RealizePalette(hdc);
3768         free_ctx(hdc);
3769     }
3770 }
3771
3772 void write_aclip(char *data, int len, int must_deselect)
3773 {
3774     HGLOBAL clipdata;
3775     void *lock;
3776
3777     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3778     if (!clipdata)
3779         return;
3780     lock = GlobalLock(clipdata);
3781     if (!lock)
3782         return;
3783     memcpy(lock, data, len);
3784     ((unsigned char *) lock)[len] = 0;
3785     GlobalUnlock(clipdata);
3786
3787     if (!must_deselect)
3788         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3789
3790     if (OpenClipboard(hwnd)) {
3791         EmptyClipboard();
3792         SetClipboardData(CF_TEXT, clipdata);
3793         CloseClipboard();
3794     } else
3795         GlobalFree(clipdata);
3796
3797     if (!must_deselect)
3798         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3799 }
3800
3801 /*
3802  * Note: unlike write_aclip() this will not append a nul.
3803  */
3804 void write_clip(wchar_t * data, int len, int must_deselect)
3805 {
3806     HGLOBAL clipdata, clipdata2, clipdata3;
3807     int len2;
3808     void *lock, *lock2, *lock3;
3809
3810     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3811
3812     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3813                            len * sizeof(wchar_t));
3814     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3815
3816     if (!clipdata || !clipdata2) {
3817         if (clipdata)
3818             GlobalFree(clipdata);
3819         if (clipdata2)
3820             GlobalFree(clipdata2);
3821         return;
3822     }
3823     if (!(lock = GlobalLock(clipdata)))
3824         return;
3825     if (!(lock2 = GlobalLock(clipdata2)))
3826         return;
3827
3828     memcpy(lock, data, len * sizeof(wchar_t));
3829     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3830
3831     if (cfg.rtf_paste) {
3832         wchar_t unitab[256];
3833         char *rtf = NULL;
3834         unsigned char *tdata = (unsigned char *)lock2;
3835         wchar_t *udata = (wchar_t *)lock;
3836         int rtflen = 0, uindex = 0, tindex = 0;
3837         int rtfsize = 0;
3838         int multilen, blen, alen, totallen, i;
3839         char before[16], after[4];
3840
3841         get_unitab(CP_ACP, unitab, 0);
3842
3843         rtfsize = 100 + strlen(cfg.font);
3844         rtf = smalloc(rtfsize);
3845         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3846                 GetACP(), cfg.font);
3847         rtflen = strlen(rtf);
3848
3849         /*
3850          * We want to construct a piece of RTF that specifies the
3851          * same Unicode text. To do this we will read back in
3852          * parallel from the Unicode data in `udata' and the
3853          * non-Unicode data in `tdata'. For each character in
3854          * `tdata' which becomes the right thing in `udata' when
3855          * looked up in `unitab', we just copy straight over from
3856          * tdata. For each one that doesn't, we must WCToMB it
3857          * individually and produce a \u escape sequence.
3858          * 
3859          * It would probably be more robust to just bite the bullet
3860          * and WCToMB each individual Unicode character one by one,
3861          * then MBToWC each one back to see if it was an accurate
3862          * translation; but that strikes me as a horrifying number
3863          * of Windows API calls so I want to see if this faster way
3864          * will work. If it screws up badly we can always revert to
3865          * the simple and slow way.
3866          */
3867         while (tindex < len2 && uindex < len &&
3868                tdata[tindex] && udata[uindex]) {
3869             if (tindex + 1 < len2 &&
3870                 tdata[tindex] == '\r' &&
3871                 tdata[tindex+1] == '\n') {
3872                 tindex++;
3873                 uindex++;
3874             }
3875             if (unitab[tdata[tindex]] == udata[uindex]) {
3876                 multilen = 1;
3877                 before[0] = '\0';
3878                 after[0] = '\0';
3879                 blen = alen = 0;
3880             } else {
3881                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3882                                                NULL, 0, NULL, NULL);
3883                 if (multilen != 1) {
3884                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3885                                    udata[uindex]);
3886                     alen = 1; strcpy(after, "}");
3887                 } else {
3888                     blen = sprintf(before, "\\u%d", udata[uindex]);
3889                     alen = 0; after[0] = '\0';
3890                 }
3891             }
3892             assert(tindex + multilen <= len2);
3893             totallen = blen + alen;
3894             for (i = 0; i < multilen; i++) {
3895                 if (tdata[tindex+i] == '\\' ||
3896                     tdata[tindex+i] == '{' ||
3897                     tdata[tindex+i] == '}')
3898                     totallen += 2;
3899                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3900                     totallen += 6;     /* \par\r\n */
3901                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3902                     totallen += 4;
3903                 else
3904                     totallen++;
3905             }
3906
3907             if (rtfsize < rtflen + totallen + 3) {
3908                 rtfsize = rtflen + totallen + 512;
3909                 rtf = srealloc(rtf, rtfsize);
3910             }
3911
3912             strcpy(rtf + rtflen, before); rtflen += blen;
3913             for (i = 0; i < multilen; i++) {
3914                 if (tdata[tindex+i] == '\\' ||
3915                     tdata[tindex+i] == '{' ||
3916                     tdata[tindex+i] == '}') {
3917                     rtf[rtflen++] = '\\';
3918                     rtf[rtflen++] = tdata[tindex+i];
3919                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3920                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3921                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3922                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3923                 } else {
3924                     rtf[rtflen++] = tdata[tindex+i];
3925                 }
3926             }
3927             strcpy(rtf + rtflen, after); rtflen += alen;
3928
3929             tindex += multilen;
3930             uindex++;
3931         }
3932
3933         strcpy(rtf + rtflen, "}");
3934         rtflen += 2;
3935
3936         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3937         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3938             strcpy(lock3, rtf);
3939             GlobalUnlock(clipdata3);
3940         }
3941         sfree(rtf);
3942     } else
3943         clipdata3 = NULL;
3944
3945     GlobalUnlock(clipdata);
3946     GlobalUnlock(clipdata2);
3947
3948     if (!must_deselect)
3949         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3950
3951     if (OpenClipboard(hwnd)) {
3952         EmptyClipboard();
3953         SetClipboardData(CF_UNICODETEXT, clipdata);
3954         SetClipboardData(CF_TEXT, clipdata2);
3955         if (clipdata3)
3956             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3957         CloseClipboard();
3958     } else {
3959         GlobalFree(clipdata);
3960         GlobalFree(clipdata2);
3961     }
3962
3963     if (!must_deselect)
3964         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3965 }
3966
3967 void get_clip(wchar_t ** p, int *len)
3968 {
3969     static HGLOBAL clipdata = NULL;
3970     static wchar_t *converted = 0;
3971     wchar_t *p2;
3972
3973     if (converted) {
3974         sfree(converted);
3975         converted = 0;
3976     }
3977     if (!p) {
3978         if (clipdata)
3979             GlobalUnlock(clipdata);
3980         clipdata = NULL;
3981         return;
3982     } else if (OpenClipboard(NULL)) {
3983         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3984             CloseClipboard();
3985             *p = GlobalLock(clipdata);
3986             if (*p) {
3987                 for (p2 = *p; *p2; p2++);
3988                 *len = p2 - *p;
3989                 return;
3990             }
3991         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3992             char *s;
3993             int i;
3994             CloseClipboard();
3995             s = GlobalLock(clipdata);
3996             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3997             *p = converted = smalloc(i * sizeof(wchar_t));
3998             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3999             *len = i - 1;
4000             return;
4001         } else
4002             CloseClipboard();
4003     }
4004
4005     *p = NULL;
4006     *len = 0;
4007 }
4008
4009 #if 0
4010 /*
4011  * Move `lines' lines from position `from' to position `to' in the
4012  * window.
4013  */
4014 void optimised_move(int to, int from, int lines)
4015 {
4016     RECT r;
4017     int min, max;
4018
4019     min = (to < from ? to : from);
4020     max = to + from - min;
4021
4022     r.left = offset_width;
4023     r.right = offset_width + cols * font_width;
4024     r.top = offset_height + min * font_height;
4025     r.bottom = offset_height + (max + lines) * font_height;
4026     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4027 }
4028 #endif
4029
4030 /*
4031  * Print a message box and perform a fatal exit.
4032  */
4033 void fatalbox(char *fmt, ...)
4034 {
4035     va_list ap;
4036     char stuff[200];
4037
4038     va_start(ap, fmt);
4039     vsprintf(stuff, fmt, ap);
4040     va_end(ap);
4041     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4042     exit(1);
4043 }
4044
4045 /*
4046  * Manage window caption / taskbar flashing, if enabled.
4047  * 0 = stop, 1 = maintain, 2 = start
4048  */
4049 static void flash_window(int mode)
4050 {
4051     static long last_flash = 0;
4052     static int flashing = 0;
4053     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4054         /* stop */
4055         if (flashing) {
4056             FlashWindow(hwnd, FALSE);
4057             flashing = 0;
4058         }
4059
4060     } else if (mode == 2) {
4061         /* start */
4062         if (!flashing) {
4063             last_flash = GetTickCount();
4064             flashing = 1;
4065             FlashWindow(hwnd, TRUE);
4066         }
4067
4068     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4069         /* maintain */
4070         if (flashing) {
4071             long now = GetTickCount();
4072             long fdiff = now - last_flash;
4073             if (fdiff < 0 || fdiff > 450) {
4074                 last_flash = now;
4075                 FlashWindow(hwnd, TRUE);        /* toggle */
4076             }
4077         }
4078     }
4079 }
4080
4081 /*
4082  * Beep.
4083  */
4084 void beep(int mode)
4085 {
4086     if (mode == BELL_DEFAULT) {
4087         /*
4088          * For MessageBeep style bells, we want to be careful of
4089          * timing, because they don't have the nice property of
4090          * PlaySound bells that each one cancels the previous
4091          * active one. So we limit the rate to one per 50ms or so.
4092          */
4093         static long lastbeep = 0;
4094         long beepdiff;
4095
4096         beepdiff = GetTickCount() - lastbeep;
4097         if (beepdiff >= 0 && beepdiff < 50)
4098             return;
4099         MessageBeep(MB_OK);
4100         /*
4101          * The above MessageBeep call takes time, so we record the
4102          * time _after_ it finishes rather than before it starts.
4103          */
4104         lastbeep = GetTickCount();
4105     } else if (mode == BELL_WAVEFILE) {
4106         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4107             char buf[sizeof(cfg.bell_wavefile) + 80];
4108             sprintf(buf, "Unable to play sound file\n%s\n"
4109                     "Using default sound instead", cfg.bell_wavefile);
4110             MessageBox(hwnd, buf, "PuTTY Sound Error",
4111                        MB_OK | MB_ICONEXCLAMATION);
4112             cfg.beep = BELL_DEFAULT;
4113         }
4114     }
4115     /* Otherwise, either visual bell or disabled; do nothing here */
4116     if (!has_focus) {
4117         flash_window(2);               /* start */
4118     }
4119 }
4120
4121 /*
4122  * Minimise or restore the window in response to a server-side
4123  * request.
4124  */
4125 void set_iconic(int iconic)
4126 {
4127     if (IsIconic(hwnd)) {
4128         if (!iconic)
4129             ShowWindow(hwnd, SW_RESTORE);
4130     } else {
4131         if (iconic)
4132             ShowWindow(hwnd, SW_MINIMIZE);
4133     }
4134 }
4135
4136 /*
4137  * Move the window in response to a server-side request.
4138  */
4139 void move_window(int x, int y)
4140 {
4141     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4142 }
4143
4144 /*
4145  * Move the window to the top or bottom of the z-order in response
4146  * to a server-side request.
4147  */
4148 void set_zorder(int top)
4149 {
4150     if (cfg.alwaysontop)
4151         return;                        /* ignore */
4152     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4153                  SWP_NOMOVE | SWP_NOSIZE);
4154 }
4155
4156 /*
4157  * Refresh the window in response to a server-side request.
4158  */
4159 void refresh_window(void)
4160 {
4161     InvalidateRect(hwnd, NULL, TRUE);
4162 }
4163
4164 /*
4165  * Maximise or restore the window in response to a server-side
4166  * request.
4167  */
4168 void set_zoomed(int zoomed)
4169 {
4170     if (IsZoomed(hwnd)) {
4171         if (!zoomed)
4172             ShowWindow(hwnd, SW_RESTORE);
4173     } else {
4174         if (zoomed)
4175             ShowWindow(hwnd, SW_MAXIMIZE);
4176     }
4177 }
4178
4179 /*
4180  * Report whether the window is iconic, for terminal reports.
4181  */
4182 int is_iconic(void)
4183 {
4184     return IsIconic(hwnd);
4185 }
4186
4187 /*
4188  * Report the window's position, for terminal reports.
4189  */
4190 void get_window_pos(int *x, int *y)
4191 {
4192     RECT r;
4193     GetWindowRect(hwnd, &r);
4194     *x = r.left;
4195     *y = r.top;
4196 }
4197
4198 /*
4199  * Report the window's pixel size, for terminal reports.
4200  */
4201 void get_window_pixels(int *x, int *y)
4202 {
4203     RECT r;
4204     GetWindowRect(hwnd, &r);
4205     *x = r.right - r.left;
4206     *y = r.bottom - r.top;
4207 }
4208
4209 /*
4210  * Return the window or icon title.
4211  */
4212 char *get_window_title(int icon)
4213 {
4214     return icon ? icon_name : window_name;
4215 }
4216
4217 /*
4218  * See if we're in full-screen mode.
4219  */
4220 int is_full_screen()
4221 {
4222     if (!IsZoomed(hwnd))
4223         return FALSE;
4224     if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4225         return FALSE;
4226     return TRUE;
4227 }
4228
4229 /*
4230  * Go full-screen. This should only be called when we are already
4231  * maximised.
4232  */
4233 void make_full_screen()
4234 {
4235     DWORD style;
4236     int x, y, w, h;
4237
4238     assert(IsZoomed(hwnd));
4239
4240     /* Remove the window furniture. */
4241     style = GetWindowLong(hwnd, GWL_STYLE);
4242     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4243     if (cfg.scrollbar_in_fullscreen)
4244         style |= WS_VSCROLL;
4245     else
4246         style &= ~WS_VSCROLL;
4247     SetWindowLong(hwnd, GWL_STYLE, style);
4248
4249     /* Resize ourselves to exactly cover the nearest monitor. */
4250 #ifdef MONITOR_DEFAULTTONEAREST
4251     {
4252         HMONITOR mon;
4253         MONITORINFO mi;
4254         mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4255         mi.cbSize = sizeof(mi);
4256         GetMonitorInfo(mon, &mi);
4257         x = mi.rcMonitor.left;
4258         y = mi.rcMonitor.top;
4259         w = mi.rcMonitor.right;
4260         h = mi.rcMonitor.bottom;
4261     }
4262 #else
4263     x = y = 0;
4264     w = GetSystemMetrics(SM_CXSCREEN);
4265     h = GetSystemMetrics(SM_CYSCREEN);
4266 #endif
4267     SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4268
4269     /* Tick the menu item in the System menu. */
4270     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4271                   MF_CHECKED);
4272 }
4273
4274 /*
4275  * Clear the full-screen attributes.
4276  */
4277 void clear_full_screen()
4278 {
4279     DWORD oldstyle, style;
4280
4281     /* Reinstate the window furniture. */
4282     style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4283     style |= WS_CAPTION | WS_BORDER;
4284     if (cfg.resize_action == RESIZE_DISABLED)
4285         style &= ~WS_THICKFRAME;
4286     else
4287         style |= WS_THICKFRAME;
4288     if (cfg.scrollbar)
4289         style |= WS_VSCROLL;
4290     else
4291         style &= ~WS_VSCROLL;
4292     if (style != oldstyle) {
4293         SetWindowLong(hwnd, GWL_STYLE, style);
4294         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4295                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4296                      SWP_FRAMECHANGED);
4297     }
4298
4299     /* Untick the menu item in the System menu. */
4300     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4301                   MF_UNCHECKED);
4302 }
4303
4304 /*
4305  * Toggle full-screen mode.
4306  */
4307 void flip_full_screen()
4308 {
4309     if (is_full_screen()) {
4310         ShowWindow(hwnd, SW_RESTORE);
4311     } else if (IsZoomed(hwnd)) {
4312         make_full_screen();
4313     } else {
4314         SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4315         ShowWindow(hwnd, SW_MAXIMIZE);
4316     }
4317 }