]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Reverse part of RDB's pedantic patch from rev 1.129 [r1105]: app
[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                 int i;
2399                 buff = (char*) smalloc(n);
2400                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2401                 /*
2402                  * Jaeyoun Chung reports that Korean character
2403                  * input doesn't work correctly if we do a single
2404                  * luni_send() covering the whole of buff. So
2405                  * instead we luni_send the characters one by one.
2406                  */
2407                 for (i = 0; i < n; i += 2)
2408                     luni_send((unsigned short *)(buff+i), 1, 1);
2409                 free(buff);
2410             }
2411             ImmReleaseContext(hwnd, hIMC);
2412             return 1;
2413         }
2414
2415       case WM_IME_CHAR:
2416         if (wParam & 0xFF00) {
2417             unsigned char buf[2];
2418
2419             buf[1] = wParam;
2420             buf[0] = wParam >> 8;
2421             lpage_send(kbd_codepage, buf, 2, 1);
2422         } else {
2423             char c = (unsigned char) wParam;
2424             lpage_send(kbd_codepage, &c, 1, 1);
2425         }
2426         return (0);
2427       case WM_CHAR:
2428       case WM_SYSCHAR:
2429         /*
2430          * Nevertheless, we are prepared to deal with WM_CHAR
2431          * messages, should they crop up. So if someone wants to
2432          * post the things to us as part of a macro manoeuvre,
2433          * we're ready to cope.
2434          */
2435         {
2436             char c = (unsigned char)wParam;
2437             lpage_send(CP_ACP, &c, 1, 1);
2438         }
2439         return 0;
2440       case WM_SETCURSOR:
2441         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2442             SetCursor(LoadCursor(NULL, IDC_ARROW));
2443             return TRUE;
2444         }
2445     }
2446
2447     return DefWindowProc(hwnd, message, wParam, lParam);
2448 }
2449
2450 /*
2451  * Move the system caret. (We maintain one, even though it's
2452  * invisible, for the benefit of blind people: apparently some
2453  * helper software tracks the system caret, so we should arrange to
2454  * have one.)
2455  */
2456 void sys_cursor(int x, int y)
2457 {
2458     COMPOSITIONFORM cf;
2459     HIMC hIMC;
2460
2461     if (!has_focus) return;
2462     
2463     SetCaretPos(x * font_width + offset_width,
2464                 y * font_height + offset_height);
2465
2466     /* IMM calls on Win98 and beyond only */
2467     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2468     
2469     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2470             osVersion.dwMinorVersion == 0) return; /* 95 */
2471
2472     /* we should have the IMM functions */
2473     hIMC = ImmGetContext(hwnd);
2474     cf.dwStyle = CFS_POINT;
2475     cf.ptCurrentPos.x = x * font_width + offset_width;
2476     cf.ptCurrentPos.y = y * font_height + offset_height;
2477     ImmSetCompositionWindow(hIMC, &cf);
2478
2479     ImmReleaseContext(hwnd, hIMC);
2480 }
2481
2482 /*
2483  * Draw a line of text in the window, at given character
2484  * coordinates, in given attributes.
2485  *
2486  * We are allowed to fiddle with the contents of `text'.
2487  */
2488 void do_text(Context ctx, int x, int y, char *text, int len,
2489              unsigned long attr, int lattr)
2490 {
2491     COLORREF fg, bg, t;
2492     int nfg, nbg, nfont;
2493     HDC hdc = ctx;
2494     RECT line_box;
2495     int force_manual_underline = 0;
2496     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2497     int char_width = fnt_width;
2498     int text_adjust = 0;
2499     static int *IpDx = 0, IpDxLEN = 0;
2500
2501     if (attr & ATTR_WIDE)
2502         char_width *= 2;
2503
2504     if (len > IpDxLEN || IpDx[0] != char_width) {
2505         int i;
2506         if (len > IpDxLEN) {
2507             sfree(IpDx);
2508             IpDx = smalloc((len + 16) * sizeof(int));
2509             IpDxLEN = (len + 16);
2510         }
2511         for (i = 0; i < IpDxLEN; i++)
2512             IpDx[i] = char_width;
2513     }
2514
2515     /* Only want the left half of double width lines */
2516     if (lattr != LATTR_NORM && x*2 >= cols)
2517         return;
2518
2519     x *= fnt_width;
2520     y *= font_height;
2521     x += offset_width;
2522     y += offset_height;
2523
2524     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2525         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2526         attr ^= ATTR_CUR_XOR;
2527     }
2528
2529     nfont = 0;
2530     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2531         /* Assume a poorman font is borken in other ways too. */
2532         lattr = LATTR_WIDE;
2533     } else
2534         switch (lattr) {
2535           case LATTR_NORM:
2536             break;
2537           case LATTR_WIDE:
2538             nfont |= FONT_WIDE;
2539             break;
2540           default:
2541             nfont |= FONT_WIDE + FONT_HIGH;
2542             break;
2543         }
2544     if (attr & ATTR_NARROW)
2545         nfont |= FONT_NARROW;
2546
2547     /* Special hack for the VT100 linedraw glyphs. */
2548     if ((attr & CSET_MASK) == 0x2300) {
2549         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2550             switch ((unsigned char) (text[0])) {
2551               case 0xBA:
2552                 text_adjust = -2 * font_height / 5;
2553                 break;
2554               case 0xBB:
2555                 text_adjust = -1 * font_height / 5;
2556                 break;
2557               case 0xBC:
2558                 text_adjust = font_height / 5;
2559                 break;
2560               case 0xBD:
2561                 text_adjust = 2 * font_height / 5;
2562                 break;
2563             }
2564             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2565                 text_adjust *= 2;
2566             attr &= ~CSET_MASK;
2567             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2568             attr |= (unitab_xterm['q'] & CSET_MASK);
2569             if (attr & ATTR_UNDER) {
2570                 attr &= ~ATTR_UNDER;
2571                 force_manual_underline = 1;
2572             }
2573         }
2574     }
2575
2576     /* Anything left as an original character set is unprintable. */
2577     if (DIRECT_CHAR(attr)) {
2578         attr &= ~CSET_MASK;
2579         attr |= 0xFF00;
2580         memset(text, 0xFD, len);
2581     }
2582
2583     /* OEM CP */
2584     if ((attr & CSET_MASK) == ATTR_OEMCP)
2585         nfont |= FONT_OEM;
2586
2587     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2588     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2589     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2590         nfont |= FONT_BOLD;
2591     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2592         nfont |= FONT_UNDERLINE;
2593     another_font(nfont);
2594     if (!fonts[nfont]) {
2595         if (nfont & FONT_UNDERLINE)
2596             force_manual_underline = 1;
2597         /* Don't do the same for manual bold, it could be bad news. */
2598
2599         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2600     }
2601     another_font(nfont);
2602     if (!fonts[nfont])
2603         nfont = FONT_NORMAL;
2604     if (attr & ATTR_REVERSE) {
2605         t = nfg;
2606         nfg = nbg;
2607         nbg = t;
2608     }
2609     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2610         nfg++;
2611     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2612         nbg++;
2613     fg = colours[nfg];
2614     bg = colours[nbg];
2615     SelectObject(hdc, fonts[nfont]);
2616     SetTextColor(hdc, fg);
2617     SetBkColor(hdc, bg);
2618     SetBkMode(hdc, OPAQUE);
2619     line_box.left = x;
2620     line_box.top = y;
2621     line_box.right = x + char_width * len;
2622     line_box.bottom = y + font_height;
2623
2624     /* Only want the left half of double width lines */
2625     if (line_box.right > font_width*cols+offset_width)
2626         line_box.right = font_width*cols+offset_width;
2627
2628     /* We're using a private area for direct to font. (512 chars.) */
2629     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2630         /* Ho Hum, dbcs fonts are a PITA! */
2631         /* To display on W9x I have to convert to UCS */
2632         static wchar_t *uni_buf = 0;
2633         static int uni_len = 0;
2634         int nlen, mptr;
2635         if (len > uni_len) {
2636             sfree(uni_buf);
2637             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2638         }
2639
2640         for(nlen = mptr = 0; mptr<len; mptr++) {
2641             uni_buf[nlen] = 0xFFFD;
2642             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2643                 IpDx[nlen] += char_width;
2644                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2645                                    text+mptr, 2, uni_buf+nlen, 1);
2646                 mptr++;
2647             }
2648             else
2649             {
2650                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2651                                    text+mptr, 1, uni_buf+nlen, 1);
2652             }
2653             nlen++;
2654         }
2655         if (nlen <= 0)
2656             return;                    /* Eeek! */
2657
2658         ExtTextOutW(hdc, x,
2659                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2660                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2661         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2662             SetBkMode(hdc, TRANSPARENT);
2663             ExtTextOutW(hdc, x - 1,
2664                         y - font_height * (lattr ==
2665                                            LATTR_BOT) + text_adjust,
2666                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2667         }
2668
2669         IpDx[0] = -1;
2670     } else if (DIRECT_FONT(attr)) {
2671         ExtTextOut(hdc, x,
2672                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2673                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2674         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2675             SetBkMode(hdc, TRANSPARENT);
2676
2677             /* GRR: This draws the character outside it's box and can leave
2678              * 'droppings' even with the clip box! I suppose I could loop it
2679              * one character at a time ... yuk. 
2680              * 
2681              * Or ... I could do a test print with "W", and use +1 or -1 for this
2682              * shift depending on if the leftmost column is blank...
2683              */
2684             ExtTextOut(hdc, x - 1,
2685                        y - font_height * (lattr ==
2686                                           LATTR_BOT) + text_adjust,
2687                        ETO_CLIPPED, &line_box, text, len, IpDx);
2688         }
2689     } else {
2690         /* And 'normal' unicode characters */
2691         static WCHAR *wbuf = NULL;
2692         static int wlen = 0;
2693         int i;
2694         if (wlen < len) {
2695             sfree(wbuf);
2696             wlen = len;
2697             wbuf = smalloc(wlen * sizeof(WCHAR));
2698         }
2699         for (i = 0; i < len; i++)
2700             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2701
2702         ExtTextOutW(hdc, x,
2703                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2704                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2705
2706         /* And the shadow bold hack. */
2707         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2708             SetBkMode(hdc, TRANSPARENT);
2709             ExtTextOutW(hdc, x - 1,
2710                         y - font_height * (lattr ==
2711                                            LATTR_BOT) + text_adjust,
2712                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2713         }
2714     }
2715     if (lattr != LATTR_TOP && (force_manual_underline ||
2716                                (und_mode == UND_LINE
2717                                 && (attr & ATTR_UNDER)))) {
2718         HPEN oldpen;
2719         int dec = descent;
2720         if (lattr == LATTR_BOT)
2721             dec = dec * 2 - font_height;
2722
2723         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2724         MoveToEx(hdc, x, y + dec, NULL);
2725         LineTo(hdc, x + len * char_width, y + dec);
2726         oldpen = SelectObject(hdc, oldpen);
2727         DeleteObject(oldpen);
2728     }
2729 }
2730
2731 void do_cursor(Context ctx, int x, int y, char *text, int len,
2732                unsigned long attr, int lattr)
2733 {
2734
2735     int fnt_width;
2736     int char_width;
2737     HDC hdc = ctx;
2738     int ctype = cfg.cursor_type;
2739
2740     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2741         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2742             do_text(ctx, x, y, text, len, attr, lattr);
2743             return;
2744         }
2745         ctype = 2;
2746         attr |= TATTR_RIGHTCURS;
2747     }
2748
2749     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2750     if (attr & ATTR_WIDE)
2751         char_width *= 2;
2752     x *= fnt_width;
2753     y *= font_height;
2754     x += offset_width;
2755     y += offset_height;
2756
2757     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2758         POINT pts[5];
2759         HPEN oldpen;
2760         pts[0].x = pts[1].x = pts[4].x = x;
2761         pts[2].x = pts[3].x = x + char_width - 1;
2762         pts[0].y = pts[3].y = pts[4].y = y;
2763         pts[1].y = pts[2].y = y + font_height - 1;
2764         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2765         Polyline(hdc, pts, 5);
2766         oldpen = SelectObject(hdc, oldpen);
2767         DeleteObject(oldpen);
2768     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2769         int startx, starty, dx, dy, length, i;
2770         if (ctype == 1) {
2771             startx = x;
2772             starty = y + descent;
2773             dx = 1;
2774             dy = 0;
2775             length = char_width;
2776         } else {
2777             int xadjust = 0;
2778             if (attr & TATTR_RIGHTCURS)
2779                 xadjust = char_width - 1;
2780             startx = x + xadjust;
2781             starty = y;
2782             dx = 0;
2783             dy = 1;
2784             length = font_height;
2785         }
2786         if (attr & TATTR_ACTCURS) {
2787             HPEN oldpen;
2788             oldpen =
2789                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2790             MoveToEx(hdc, startx, starty, NULL);
2791             LineTo(hdc, startx + dx * length, starty + dy * length);
2792             oldpen = SelectObject(hdc, oldpen);
2793             DeleteObject(oldpen);
2794         } else {
2795             for (i = 0; i < length; i++) {
2796                 if (i % 2 == 0) {
2797                     SetPixel(hdc, startx, starty, colours[23]);
2798                 }
2799                 startx += dx;
2800                 starty += dy;
2801             }
2802         }
2803     }
2804 }
2805
2806 /* This function gets the actual width of a character in the normal font.
2807  */
2808 int CharWidth(Context ctx, int uc) {
2809     HDC hdc = ctx;
2810     int ibuf = 0;
2811
2812     /* If the font max is the same as the font ave width then this
2813      * function is a no-op.
2814      */
2815     if (!font_dualwidth) return 1;
2816
2817     switch (uc & CSET_MASK) {
2818       case ATTR_ASCII:
2819         uc = unitab_line[uc & 0xFF];
2820         break;
2821       case ATTR_LINEDRW:
2822         uc = unitab_xterm[uc & 0xFF];
2823         break;
2824       case ATTR_SCOACS:
2825         uc = unitab_scoacs[uc & 0xFF];
2826         break;
2827     }
2828     if (DIRECT_FONT(uc)) {
2829         if (dbcs_screenfont) return 1;
2830
2831         /* Speedup, I know of no font where ascii is the wrong width */
2832         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2833             return 1;
2834
2835         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2836             SelectObject(hdc, fonts[FONT_NORMAL]);
2837         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2838             another_font(FONT_OEM);
2839             if (!fonts[FONT_OEM]) return 0;
2840
2841             SelectObject(hdc, fonts[FONT_OEM]);
2842         } else
2843             return 0;
2844
2845         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2846              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2847             return 0;
2848     } else {
2849         /* Speedup, I know of no font where ascii is the wrong width */
2850         if (uc >= ' ' && uc <= '~') return 1;
2851
2852         SelectObject(hdc, fonts[FONT_NORMAL]);
2853         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2854             /* Okay that one worked */ ;
2855         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2856             /* This should work on 9x too, but it's "less accurate" */ ;
2857         else
2858             return 0;
2859     }
2860
2861     ibuf += font_width / 2 -1;
2862     ibuf /= font_width;
2863
2864     return ibuf;
2865 }
2866
2867 /*
2868  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2869  * codes. Returns number of bytes used or zero to drop the message
2870  * or -1 to forward the message to windows.
2871  */
2872 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2873                         unsigned char *output)
2874 {
2875     BYTE keystate[256];
2876     int scan, left_alt = 0, key_down, shift_state;
2877     int r, i, code;
2878     unsigned char *p = output;
2879     static int alt_sum = 0;
2880
2881     HKL kbd_layout = GetKeyboardLayout(0);
2882
2883     static WORD keys[3];
2884     static int compose_char = 0;
2885     static WPARAM compose_key = 0;
2886
2887     r = GetKeyboardState(keystate);
2888     if (!r)
2889         memset(keystate, 0, sizeof(keystate));
2890     else {
2891 #if 0
2892 #define SHOW_TOASCII_RESULT
2893         {                              /* Tell us all about key events */
2894             static BYTE oldstate[256];
2895             static int first = 1;
2896             static int scan;
2897             int ch;
2898             if (first)
2899                 memcpy(oldstate, keystate, sizeof(oldstate));
2900             first = 0;
2901
2902             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2903                 debug(("+"));
2904             } else if ((HIWORD(lParam) & KF_UP)
2905                        && scan == (HIWORD(lParam) & 0xFF)) {
2906                 debug((". U"));
2907             } else {
2908                 debug((".\n"));
2909                 if (wParam >= VK_F1 && wParam <= VK_F20)
2910                     debug(("K_F%d", wParam + 1 - VK_F1));
2911                 else
2912                     switch (wParam) {
2913                       case VK_SHIFT:
2914                         debug(("SHIFT"));
2915                         break;
2916                       case VK_CONTROL:
2917                         debug(("CTRL"));
2918                         break;
2919                       case VK_MENU:
2920                         debug(("ALT"));
2921                         break;
2922                       default:
2923                         debug(("VK_%02x", wParam));
2924                     }
2925                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2926                     debug(("*"));
2927                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2928
2929                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2930                 if (ch >= ' ' && ch <= '~')
2931                     debug((", '%c'", ch));
2932                 else if (ch)
2933                     debug((", $%02x", ch));
2934
2935                 if (keys[0])
2936                     debug((", KB0=%02x", keys[0]));
2937                 if (keys[1])
2938                     debug((", KB1=%02x", keys[1]));
2939                 if (keys[2])
2940                     debug((", KB2=%02x", keys[2]));
2941
2942                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2943                     debug((", S"));
2944                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2945                     debug((", C"));
2946                 if ((HIWORD(lParam) & KF_EXTENDED))
2947                     debug((", E"));
2948                 if ((HIWORD(lParam) & KF_UP))
2949                     debug((", U"));
2950             }
2951
2952             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2953             else if ((HIWORD(lParam) & KF_UP))
2954                 oldstate[wParam & 0xFF] ^= 0x80;
2955             else
2956                 oldstate[wParam & 0xFF] ^= 0x81;
2957
2958             for (ch = 0; ch < 256; ch++)
2959                 if (oldstate[ch] != keystate[ch])
2960                     debug((", M%02x=%02x", ch, keystate[ch]));
2961
2962             memcpy(oldstate, keystate, sizeof(oldstate));
2963         }
2964 #endif
2965
2966         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2967             keystate[VK_RMENU] = keystate[VK_MENU];
2968         }
2969
2970
2971         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2972         if ((cfg.funky_type == 3 ||
2973              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2974             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2975
2976             wParam = VK_EXECUTE;
2977
2978             /* UnToggle NUMLock */
2979             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2980                 keystate[VK_NUMLOCK] ^= 1;
2981         }
2982
2983         /* And write back the 'adjusted' state */
2984         SetKeyboardState(keystate);
2985     }
2986
2987     /* Disable Auto repeat if required */
2988     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2989         return 0;
2990
2991     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2992         left_alt = 1;
2993
2994     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2995
2996     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2997     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2998         if (cfg.ctrlaltkeys)
2999             keystate[VK_MENU] = 0;
3000         else {
3001             keystate[VK_RMENU] = 0x80;
3002             left_alt = 0;
3003         }
3004     }
3005
3006     alt_pressed = (left_alt && key_down);
3007
3008     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3009     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3010         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3011
3012     /* Note if AltGr was pressed and if it was used as a compose key */
3013     if (!compose_state) {
3014         compose_key = 0x100;
3015         if (cfg.compose_key) {
3016             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3017                 compose_key = wParam;
3018         }
3019         if (wParam == VK_APPS)
3020             compose_key = wParam;
3021     }
3022
3023     if (wParam == compose_key) {
3024         if (compose_state == 0
3025             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3026                 1;
3027         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3028             compose_state = 2;
3029         else
3030             compose_state = 0;
3031     } else if (compose_state == 1 && wParam != VK_CONTROL)
3032         compose_state = 0;
3033
3034     /* 
3035      * Record that we pressed key so the scroll window can be reset, but
3036      * be careful to avoid Shift-UP/Down
3037      */
3038     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3039         wParam != VK_MENU && wParam != VK_CONTROL) {
3040         seen_key_event = 1;
3041     }
3042
3043     if (compose_state > 1 && left_alt)
3044         compose_state = 0;
3045
3046     /* Sanitize the number pad if not using a PC NumPad */
3047     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3048                      && cfg.funky_type != 2)
3049         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3050         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3051             int nParam = 0;
3052             switch (wParam) {
3053               case VK_INSERT:
3054                 nParam = VK_NUMPAD0;
3055                 break;
3056               case VK_END:
3057                 nParam = VK_NUMPAD1;
3058                 break;
3059               case VK_DOWN:
3060                 nParam = VK_NUMPAD2;
3061                 break;
3062               case VK_NEXT:
3063                 nParam = VK_NUMPAD3;
3064                 break;
3065               case VK_LEFT:
3066                 nParam = VK_NUMPAD4;
3067                 break;
3068               case VK_CLEAR:
3069                 nParam = VK_NUMPAD5;
3070                 break;
3071               case VK_RIGHT:
3072                 nParam = VK_NUMPAD6;
3073                 break;
3074               case VK_HOME:
3075                 nParam = VK_NUMPAD7;
3076                 break;
3077               case VK_UP:
3078                 nParam = VK_NUMPAD8;
3079                 break;
3080               case VK_PRIOR:
3081                 nParam = VK_NUMPAD9;
3082                 break;
3083               case VK_DELETE:
3084                 nParam = VK_DECIMAL;
3085                 break;
3086             }
3087             if (nParam) {
3088                 if (keystate[VK_NUMLOCK] & 1)
3089                     shift_state |= 1;
3090                 wParam = nParam;
3091             }
3092         }
3093     }
3094
3095     /* If a key is pressed and AltGr is not active */
3096     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3097         /* Okay, prepare for most alts then ... */
3098         if (left_alt)
3099             *p++ = '\033';
3100
3101         /* Lets see if it's a pattern we know all about ... */
3102         if (wParam == VK_PRIOR && shift_state == 1) {
3103             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3104             return 0;
3105         }
3106         if (wParam == VK_NEXT && shift_state == 1) {
3107             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3108             return 0;
3109         }
3110         if (wParam == VK_INSERT && shift_state == 1) {
3111             term_do_paste();
3112             return 0;
3113         }
3114         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3115             return -1;
3116         }
3117         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3118             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3119             return -1;
3120         }
3121         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3122             (cfg.resize_action != RESIZE_DISABLED)) {
3123             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3124                 flip_full_screen();
3125             return -1;
3126         }
3127         /* Control-Numlock for app-keypad mode switch */
3128         if (wParam == VK_PAUSE && shift_state == 2) {
3129             app_keypad_keys ^= 1;
3130             return 0;
3131         }
3132
3133         /* Nethack keypad */
3134         if (cfg.nethack_keypad && !left_alt) {
3135             switch (wParam) {
3136               case VK_NUMPAD1:
3137                 *p++ = shift_state ? 'B' : 'b';
3138                 return p - output;
3139               case VK_NUMPAD2:
3140                 *p++ = shift_state ? 'J' : 'j';
3141                 return p - output;
3142               case VK_NUMPAD3:
3143                 *p++ = shift_state ? 'N' : 'n';
3144                 return p - output;
3145               case VK_NUMPAD4:
3146                 *p++ = shift_state ? 'H' : 'h';
3147                 return p - output;
3148               case VK_NUMPAD5:
3149                 *p++ = shift_state ? '.' : '.';
3150                 return p - output;
3151               case VK_NUMPAD6:
3152                 *p++ = shift_state ? 'L' : 'l';
3153                 return p - output;
3154               case VK_NUMPAD7:
3155                 *p++ = shift_state ? 'Y' : 'y';
3156                 return p - output;
3157               case VK_NUMPAD8:
3158                 *p++ = shift_state ? 'K' : 'k';
3159                 return p - output;
3160               case VK_NUMPAD9:
3161                 *p++ = shift_state ? 'U' : 'u';
3162                 return p - output;
3163             }
3164         }
3165
3166         /* Application Keypad */
3167         if (!left_alt) {
3168             int xkey = 0;
3169
3170             if (cfg.funky_type == 3 ||
3171                 (cfg.funky_type <= 1 &&
3172                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3173                   case VK_EXECUTE:
3174                     xkey = 'P';
3175                     break;
3176                   case VK_DIVIDE:
3177                     xkey = 'Q';
3178                     break;
3179                   case VK_MULTIPLY:
3180                     xkey = 'R';
3181                     break;
3182                   case VK_SUBTRACT:
3183                     xkey = 'S';
3184                     break;
3185                 }
3186             if (app_keypad_keys && !cfg.no_applic_k)
3187                 switch (wParam) {
3188                   case VK_NUMPAD0:
3189                     xkey = 'p';
3190                     break;
3191                   case VK_NUMPAD1:
3192                     xkey = 'q';
3193                     break;
3194                   case VK_NUMPAD2:
3195                     xkey = 'r';
3196                     break;
3197                   case VK_NUMPAD3:
3198                     xkey = 's';
3199                     break;
3200                   case VK_NUMPAD4:
3201                     xkey = 't';
3202                     break;
3203                   case VK_NUMPAD5:
3204                     xkey = 'u';
3205                     break;
3206                   case VK_NUMPAD6:
3207                     xkey = 'v';
3208                     break;
3209                   case VK_NUMPAD7:
3210                     xkey = 'w';
3211                     break;
3212                   case VK_NUMPAD8:
3213                     xkey = 'x';
3214                     break;
3215                   case VK_NUMPAD9:
3216                     xkey = 'y';
3217                     break;
3218
3219                   case VK_DECIMAL:
3220                     xkey = 'n';
3221                     break;
3222                   case VK_ADD:
3223                     if (cfg.funky_type == 2) {
3224                         if (shift_state)
3225                             xkey = 'l';
3226                         else
3227                             xkey = 'k';
3228                     } else if (shift_state)
3229                         xkey = 'm';
3230                     else
3231                         xkey = 'l';
3232                     break;
3233
3234                   case VK_DIVIDE:
3235                     if (cfg.funky_type == 2)
3236                         xkey = 'o';
3237                     break;
3238                   case VK_MULTIPLY:
3239                     if (cfg.funky_type == 2)
3240                         xkey = 'j';
3241                     break;
3242                   case VK_SUBTRACT:
3243                     if (cfg.funky_type == 2)
3244                         xkey = 'm';
3245                     break;
3246
3247                   case VK_RETURN:
3248                     if (HIWORD(lParam) & KF_EXTENDED)
3249                         xkey = 'M';
3250                     break;
3251                 }
3252             if (xkey) {
3253                 if (vt52_mode) {
3254                     if (xkey >= 'P' && xkey <= 'S')
3255                         p += sprintf((char *) p, "\x1B%c", xkey);
3256                     else
3257                         p += sprintf((char *) p, "\x1B?%c", xkey);
3258                 } else
3259                     p += sprintf((char *) p, "\x1BO%c", xkey);
3260                 return p - output;
3261             }
3262         }
3263
3264         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3265             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3266             *p++ = 0;
3267             return -2;
3268         }
3269         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3270             *p++ = 0x1B;
3271             *p++ = '[';
3272             *p++ = 'Z';
3273             return p - output;
3274         }
3275         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3276             *p++ = 0;
3277             return p - output;
3278         }
3279         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3280             *p++ = 160;
3281             return p - output;
3282         }
3283         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3284             *p++ = 3;
3285             *p++ = 0;
3286             return -2;
3287         }
3288         if (wParam == VK_PAUSE) {      /* Break/Pause */
3289             *p++ = 26;
3290             *p++ = 0;
3291             return -2;
3292         }
3293         /* Control-2 to Control-8 are special */
3294         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3295             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3296             return p - output;
3297         }
3298         if (shift_state == 2 && wParam == 0xBD) {
3299             *p++ = 0x1F;
3300             return p - output;
3301         }
3302         if (shift_state == 2 && wParam == 0xDF) {
3303             *p++ = 0x1C;
3304             return p - output;
3305         }
3306         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3307             *p++ = '\r';
3308             *p++ = '\n';
3309             return p - output;
3310         }
3311
3312         /*
3313          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3314          * for integer decimal nn.)
3315          *
3316          * We also deal with the weird ones here. Linux VCs replace F1
3317          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3318          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3319          * respectively.
3320          */
3321         code = 0;
3322         switch (wParam) {
3323           case VK_F1:
3324             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3325             break;
3326           case VK_F2:
3327             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3328             break;
3329           case VK_F3:
3330             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3331             break;
3332           case VK_F4:
3333             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3334             break;
3335           case VK_F5:
3336             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3337             break;
3338           case VK_F6:
3339             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3340             break;
3341           case VK_F7:
3342             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3343             break;
3344           case VK_F8:
3345             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3346             break;
3347           case VK_F9:
3348             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3349             break;
3350           case VK_F10:
3351             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3352             break;
3353           case VK_F11:
3354             code = 23;
3355             break;
3356           case VK_F12:
3357             code = 24;
3358             break;
3359           case VK_F13:
3360             code = 25;
3361             break;
3362           case VK_F14:
3363             code = 26;
3364             break;
3365           case VK_F15:
3366             code = 28;
3367             break;
3368           case VK_F16:
3369             code = 29;
3370             break;
3371           case VK_F17:
3372             code = 31;
3373             break;
3374           case VK_F18:
3375             code = 32;
3376             break;
3377           case VK_F19:
3378             code = 33;
3379             break;
3380           case VK_F20:
3381             code = 34;
3382             break;
3383         }
3384         if ((shift_state&2) == 0) switch (wParam) {
3385           case VK_HOME:
3386             code = 1;
3387             break;
3388           case VK_INSERT:
3389             code = 2;
3390             break;
3391           case VK_DELETE:
3392             code = 3;
3393             break;
3394           case VK_END:
3395             code = 4;
3396             break;
3397           case VK_PRIOR:
3398             code = 5;
3399             break;
3400           case VK_NEXT:
3401             code = 6;
3402             break;
3403         }
3404         /* Reorder edit keys to physical order */
3405         if (cfg.funky_type == 3 && code <= 6)
3406             code = "\0\2\1\4\5\3\6"[code];
3407
3408         if (vt52_mode && code > 0 && code <= 6) {
3409             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3410             return p - output;
3411         }
3412
3413         if (cfg.funky_type == 5 &&     /* SCO function keys */
3414             code >= 11 && code <= 34) {
3415             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3416             int index = 0;
3417             switch (wParam) {
3418               case VK_F1: index = 0; break;
3419               case VK_F2: index = 1; break;
3420               case VK_F3: index = 2; break;
3421               case VK_F4: index = 3; break;
3422               case VK_F5: index = 4; break;
3423               case VK_F6: index = 5; break;
3424               case VK_F7: index = 6; break;
3425               case VK_F8: index = 7; break;
3426               case VK_F9: index = 8; break;
3427               case VK_F10: index = 9; break;
3428               case VK_F11: index = 10; break;
3429               case VK_F12: index = 11; break;
3430             }
3431             if (keystate[VK_SHIFT] & 0x80) index += 12;
3432             if (keystate[VK_CONTROL] & 0x80) index += 24;
3433             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3434             return p - output;
3435         }
3436         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3437             code >= 1 && code <= 6) {
3438             char codes[] = "HL.FIG";
3439             if (code == 3) {
3440                 *p++ = '\x7F';
3441             } else {
3442                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3443             }
3444             return p - output;
3445         }
3446         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3447             int offt = 0;
3448             if (code > 15)
3449                 offt++;
3450             if (code > 21)
3451                 offt++;
3452             if (vt52_mode)
3453                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3454             else
3455                 p +=
3456                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3457             return p - output;
3458         }
3459         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3460             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3461             return p - output;
3462         }
3463         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3464             if (vt52_mode)
3465                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3466             else
3467                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3468             return p - output;
3469         }
3470         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3471             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3472             return p - output;
3473         }
3474         if (code) {
3475             p += sprintf((char *) p, "\x1B[%d~", code);
3476             return p - output;
3477         }
3478
3479         /*
3480          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3481          * some reason seems to send VK_CLEAR to Windows...).
3482          */
3483         {
3484             char xkey = 0;
3485             switch (wParam) {
3486               case VK_UP:
3487                 xkey = 'A';
3488                 break;
3489               case VK_DOWN:
3490                 xkey = 'B';
3491                 break;
3492               case VK_RIGHT:
3493                 xkey = 'C';
3494                 break;
3495               case VK_LEFT:
3496                 xkey = 'D';
3497                 break;
3498               case VK_CLEAR:
3499                 xkey = 'G';
3500                 break;
3501             }
3502             if (xkey) {
3503                 if (vt52_mode)
3504                     p += sprintf((char *) p, "\x1B%c", xkey);
3505                 else {
3506                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3507 #if 0
3508                     /*
3509                      * RDB: VT100 & VT102 manuals both state the
3510                      * app cursor keys only work if the app keypad
3511                      * is on.
3512                      * 
3513                      * SGT: That may well be true, but xterm
3514                      * disagrees and so does at least one
3515                      * application, so I've #if'ed this out and the
3516                      * behaviour is back to PuTTY's original: app
3517                      * cursor and app keypad are independently
3518                      * switchable modes. If anyone complains about
3519                      * _this_ I'll have to put in a configurable
3520                      * option.
3521                      */
3522                     if (!app_keypad_keys)
3523                         app_flg = 0;
3524 #endif
3525                     /* Useful mapping of Ctrl-arrows */
3526                     if (shift_state == 2)
3527                         app_flg = !app_flg;
3528
3529                     if (app_flg)
3530                         p += sprintf((char *) p, "\x1BO%c", xkey);
3531                     else
3532                         p += sprintf((char *) p, "\x1B[%c", xkey);
3533                 }
3534                 return p - output;
3535             }
3536         }
3537
3538         /*
3539          * Finally, deal with Return ourselves. (Win95 seems to
3540          * foul it up when Alt is pressed, for some reason.)
3541          */
3542         if (wParam == VK_RETURN) {     /* Return */
3543             *p++ = 0x0D;
3544             *p++ = 0;
3545             return -2;
3546         }
3547
3548         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3549             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3550         else
3551             alt_sum = 0;
3552     }
3553
3554     /* Okay we've done everything interesting; let windows deal with 
3555      * the boring stuff */
3556     {
3557         BOOL capsOn=0;
3558
3559         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3560         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3561             capsOn= !left_alt;
3562             keystate[VK_CAPITAL] = 0;
3563         }
3564
3565         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3566 #ifdef SHOW_TOASCII_RESULT
3567         if (r == 1 && !key_down) {
3568             if (alt_sum) {
3569                 if (in_utf || dbcs_screenfont)
3570                     debug((", (U+%04x)", alt_sum));
3571                 else
3572                     debug((", LCH(%d)", alt_sum));
3573             } else {
3574                 debug((", ACH(%d)", keys[0]));
3575             }
3576         } else if (r > 0) {
3577             int r1;
3578             debug((", ASC("));
3579             for (r1 = 0; r1 < r; r1++) {
3580                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3581             }
3582             debug((")"));
3583         }
3584 #endif
3585         if (r > 0) {
3586             WCHAR keybuf;
3587
3588             /*
3589              * Interrupt an ongoing paste. I'm not sure this is
3590              * sensible, but for the moment it's preferable to
3591              * having to faff about buffering things.
3592              */
3593             term_nopaste();
3594
3595             p = output;
3596             for (i = 0; i < r; i++) {
3597                 unsigned char ch = (unsigned char) keys[i];
3598
3599                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3600                     compose_char = ch;
3601                     compose_state++;
3602                     continue;
3603                 }
3604                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3605                     int nc;
3606                     compose_state = 0;
3607
3608                     if ((nc = check_compose(compose_char, ch)) == -1) {
3609                         MessageBeep(MB_ICONHAND);
3610                         return 0;
3611                     }
3612                     keybuf = nc;
3613                     luni_send(&keybuf, 1, 1);
3614                     continue;
3615                 }
3616
3617                 compose_state = 0;
3618
3619                 if (!key_down) {
3620                     if (alt_sum) {
3621                         if (in_utf || dbcs_screenfont) {
3622                             keybuf = alt_sum;
3623                             luni_send(&keybuf, 1, 1);
3624                         } else {
3625                             ch = (char) alt_sum;
3626                             /*
3627                              * We need not bother about stdin
3628                              * backlogs here, because in GUI PuTTY
3629                              * we can't do anything about it
3630                              * anyway; there's no means of asking
3631                              * Windows to hold off on KEYDOWN
3632                              * messages. We _have_ to buffer
3633                              * everything we're sent.
3634                              */
3635                             ldisc_send(&ch, 1, 1);
3636                         }
3637                         alt_sum = 0;
3638                     } else
3639                         lpage_send(kbd_codepage, &ch, 1, 1);
3640                 } else {
3641                     if(capsOn && ch < 0x80) {
3642                         WCHAR cbuf[2];
3643                         cbuf[0] = 27;
3644                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3645                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3646                     } else {
3647                         char cbuf[2];
3648                         cbuf[0] = '\033';
3649                         cbuf[1] = ch;
3650                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3651                     }
3652                 }
3653                 show_mouseptr(0);
3654             }
3655
3656             /* This is so the ALT-Numpad and dead keys work correctly. */
3657             keys[0] = 0;
3658
3659             return p - output;
3660         }
3661         /* If we're definitly not building up an ALT-54321 then clear it */
3662         if (!left_alt)
3663             keys[0] = 0;
3664         /* If we will be using alt_sum fix the 256s */
3665         else if (keys[0] && (in_utf || dbcs_screenfont))
3666             keys[0] = 10;
3667     }
3668
3669     /*
3670      * ALT alone may or may not want to bring up the System menu.
3671      * If it's not meant to, we return 0 on presses or releases of
3672      * ALT, to show that we've swallowed the keystroke. Otherwise
3673      * we return -1, which means Windows will give the keystroke
3674      * its default handling (i.e. bring up the System menu).
3675      */
3676     if (wParam == VK_MENU && !cfg.alt_only)
3677         return 0;
3678
3679     return -1;
3680 }
3681
3682 void set_title(char *title)
3683 {
3684     sfree(window_name);
3685     window_name = smalloc(1 + strlen(title));
3686     strcpy(window_name, title);
3687     if (cfg.win_name_always || !IsIconic(hwnd))
3688         SetWindowText(hwnd, title);
3689 }
3690
3691 void set_icon(char *title)
3692 {
3693     sfree(icon_name);
3694     icon_name = smalloc(1 + strlen(title));
3695     strcpy(icon_name, title);
3696     if (!cfg.win_name_always && IsIconic(hwnd))
3697         SetWindowText(hwnd, title);
3698 }
3699
3700 void set_sbar(int total, int start, int page)
3701 {
3702     SCROLLINFO si;
3703
3704     if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3705         return;
3706
3707     si.cbSize = sizeof(si);
3708     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3709     si.nMin = 0;
3710     si.nMax = total - 1;
3711     si.nPage = page;
3712     si.nPos = start;
3713     if (hwnd)
3714         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3715 }
3716
3717 Context get_ctx(void)
3718 {
3719     HDC hdc;
3720     if (hwnd) {
3721         hdc = GetDC(hwnd);
3722         if (hdc && pal)
3723             SelectPalette(hdc, pal, FALSE);
3724         return hdc;
3725     } else
3726         return NULL;
3727 }
3728
3729 void free_ctx(Context ctx)
3730 {
3731     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3732     ReleaseDC(hwnd, ctx);
3733 }
3734
3735 static void real_palette_set(int n, int r, int g, int b)
3736 {
3737     if (pal) {
3738         logpal->palPalEntry[n].peRed = r;
3739         logpal->palPalEntry[n].peGreen = g;
3740         logpal->palPalEntry[n].peBlue = b;
3741         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3742         colours[n] = PALETTERGB(r, g, b);
3743         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3744     } else
3745         colours[n] = RGB(r, g, b);
3746 }
3747
3748 void palette_set(int n, int r, int g, int b)
3749 {
3750     static const int first[21] = {
3751         0, 2, 4, 6, 8, 10, 12, 14,
3752         1, 3, 5, 7, 9, 11, 13, 15,
3753         16, 17, 18, 20, 22
3754     };
3755     real_palette_set(first[n], r, g, b);
3756     if (first[n] >= 18)
3757         real_palette_set(first[n] + 1, r, g, b);
3758     if (pal) {
3759         HDC hdc = get_ctx();
3760         UnrealizeObject(pal);
3761         RealizePalette(hdc);
3762         free_ctx(hdc);
3763     }
3764 }
3765
3766 void palette_reset(void)
3767 {
3768     int i;
3769
3770     for (i = 0; i < NCOLOURS; i++) {
3771         if (pal) {
3772             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3773             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3774             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3775             logpal->palPalEntry[i].peFlags = 0;
3776             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3777                                     defpal[i].rgbtGreen,
3778                                     defpal[i].rgbtBlue);
3779         } else
3780             colours[i] = RGB(defpal[i].rgbtRed,
3781                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3782     }
3783
3784     if (pal) {
3785         HDC hdc;
3786         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3787         hdc = get_ctx();
3788         RealizePalette(hdc);
3789         free_ctx(hdc);
3790     }
3791 }
3792
3793 void write_aclip(char *data, int len, int must_deselect)
3794 {
3795     HGLOBAL clipdata;
3796     void *lock;
3797
3798     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3799     if (!clipdata)
3800         return;
3801     lock = GlobalLock(clipdata);
3802     if (!lock)
3803         return;
3804     memcpy(lock, data, len);
3805     ((unsigned char *) lock)[len] = 0;
3806     GlobalUnlock(clipdata);
3807
3808     if (!must_deselect)
3809         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3810
3811     if (OpenClipboard(hwnd)) {
3812         EmptyClipboard();
3813         SetClipboardData(CF_TEXT, clipdata);
3814         CloseClipboard();
3815     } else
3816         GlobalFree(clipdata);
3817
3818     if (!must_deselect)
3819         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3820 }
3821
3822 /*
3823  * Note: unlike write_aclip() this will not append a nul.
3824  */
3825 void write_clip(wchar_t * data, int len, int must_deselect)
3826 {
3827     HGLOBAL clipdata, clipdata2, clipdata3;
3828     int len2;
3829     void *lock, *lock2, *lock3;
3830
3831     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3832
3833     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3834                            len * sizeof(wchar_t));
3835     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3836
3837     if (!clipdata || !clipdata2) {
3838         if (clipdata)
3839             GlobalFree(clipdata);
3840         if (clipdata2)
3841             GlobalFree(clipdata2);
3842         return;
3843     }
3844     if (!(lock = GlobalLock(clipdata)))
3845         return;
3846     if (!(lock2 = GlobalLock(clipdata2)))
3847         return;
3848
3849     memcpy(lock, data, len * sizeof(wchar_t));
3850     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3851
3852     if (cfg.rtf_paste) {
3853         wchar_t unitab[256];
3854         char *rtf = NULL;
3855         unsigned char *tdata = (unsigned char *)lock2;
3856         wchar_t *udata = (wchar_t *)lock;
3857         int rtflen = 0, uindex = 0, tindex = 0;
3858         int rtfsize = 0;
3859         int multilen, blen, alen, totallen, i;
3860         char before[16], after[4];
3861
3862         get_unitab(CP_ACP, unitab, 0);
3863
3864         rtfsize = 100 + strlen(cfg.font);
3865         rtf = smalloc(rtfsize);
3866         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3867                 GetACP(), cfg.font);
3868         rtflen = strlen(rtf);
3869
3870         /*
3871          * We want to construct a piece of RTF that specifies the
3872          * same Unicode text. To do this we will read back in
3873          * parallel from the Unicode data in `udata' and the
3874          * non-Unicode data in `tdata'. For each character in
3875          * `tdata' which becomes the right thing in `udata' when
3876          * looked up in `unitab', we just copy straight over from
3877          * tdata. For each one that doesn't, we must WCToMB it
3878          * individually and produce a \u escape sequence.
3879          * 
3880          * It would probably be more robust to just bite the bullet
3881          * and WCToMB each individual Unicode character one by one,
3882          * then MBToWC each one back to see if it was an accurate
3883          * translation; but that strikes me as a horrifying number
3884          * of Windows API calls so I want to see if this faster way
3885          * will work. If it screws up badly we can always revert to
3886          * the simple and slow way.
3887          */
3888         while (tindex < len2 && uindex < len &&
3889                tdata[tindex] && udata[uindex]) {
3890             if (tindex + 1 < len2 &&
3891                 tdata[tindex] == '\r' &&
3892                 tdata[tindex+1] == '\n') {
3893                 tindex++;
3894                 uindex++;
3895             }
3896             if (unitab[tdata[tindex]] == udata[uindex]) {
3897                 multilen = 1;
3898                 before[0] = '\0';
3899                 after[0] = '\0';
3900                 blen = alen = 0;
3901             } else {
3902                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3903                                                NULL, 0, NULL, NULL);
3904                 if (multilen != 1) {
3905                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3906                                    udata[uindex]);
3907                     alen = 1; strcpy(after, "}");
3908                 } else {
3909                     blen = sprintf(before, "\\u%d", udata[uindex]);
3910                     alen = 0; after[0] = '\0';
3911                 }
3912             }
3913             assert(tindex + multilen <= len2);
3914             totallen = blen + alen;
3915             for (i = 0; i < multilen; i++) {
3916                 if (tdata[tindex+i] == '\\' ||
3917                     tdata[tindex+i] == '{' ||
3918                     tdata[tindex+i] == '}')
3919                     totallen += 2;
3920                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3921                     totallen += 6;     /* \par\r\n */
3922                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3923                     totallen += 4;
3924                 else
3925                     totallen++;
3926             }
3927
3928             if (rtfsize < rtflen + totallen + 3) {
3929                 rtfsize = rtflen + totallen + 512;
3930                 rtf = srealloc(rtf, rtfsize);
3931             }
3932
3933             strcpy(rtf + rtflen, before); rtflen += blen;
3934             for (i = 0; i < multilen; i++) {
3935                 if (tdata[tindex+i] == '\\' ||
3936                     tdata[tindex+i] == '{' ||
3937                     tdata[tindex+i] == '}') {
3938                     rtf[rtflen++] = '\\';
3939                     rtf[rtflen++] = tdata[tindex+i];
3940                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3941                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3942                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3943                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3944                 } else {
3945                     rtf[rtflen++] = tdata[tindex+i];
3946                 }
3947             }
3948             strcpy(rtf + rtflen, after); rtflen += alen;
3949
3950             tindex += multilen;
3951             uindex++;
3952         }
3953
3954         strcpy(rtf + rtflen, "}");
3955         rtflen += 2;
3956
3957         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3958         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3959             strcpy(lock3, rtf);
3960             GlobalUnlock(clipdata3);
3961         }
3962         sfree(rtf);
3963     } else
3964         clipdata3 = NULL;
3965
3966     GlobalUnlock(clipdata);
3967     GlobalUnlock(clipdata2);
3968
3969     if (!must_deselect)
3970         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3971
3972     if (OpenClipboard(hwnd)) {
3973         EmptyClipboard();
3974         SetClipboardData(CF_UNICODETEXT, clipdata);
3975         SetClipboardData(CF_TEXT, clipdata2);
3976         if (clipdata3)
3977             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3978         CloseClipboard();
3979     } else {
3980         GlobalFree(clipdata);
3981         GlobalFree(clipdata2);
3982     }
3983
3984     if (!must_deselect)
3985         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3986 }
3987
3988 void get_clip(wchar_t ** p, int *len)
3989 {
3990     static HGLOBAL clipdata = NULL;
3991     static wchar_t *converted = 0;
3992     wchar_t *p2;
3993
3994     if (converted) {
3995         sfree(converted);
3996         converted = 0;
3997     }
3998     if (!p) {
3999         if (clipdata)
4000             GlobalUnlock(clipdata);
4001         clipdata = NULL;
4002         return;
4003     } else if (OpenClipboard(NULL)) {
4004         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4005             CloseClipboard();
4006             *p = GlobalLock(clipdata);
4007             if (*p) {
4008                 for (p2 = *p; *p2; p2++);
4009                 *len = p2 - *p;
4010                 return;
4011             }
4012         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4013             char *s;
4014             int i;
4015             CloseClipboard();
4016             s = GlobalLock(clipdata);
4017             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4018             *p = converted = smalloc(i * sizeof(wchar_t));
4019             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4020             *len = i - 1;
4021             return;
4022         } else
4023             CloseClipboard();
4024     }
4025
4026     *p = NULL;
4027     *len = 0;
4028 }
4029
4030 #if 0
4031 /*
4032  * Move `lines' lines from position `from' to position `to' in the
4033  * window.
4034  */
4035 void optimised_move(int to, int from, int lines)
4036 {
4037     RECT r;
4038     int min, max;
4039
4040     min = (to < from ? to : from);
4041     max = to + from - min;
4042
4043     r.left = offset_width;
4044     r.right = offset_width + cols * font_width;
4045     r.top = offset_height + min * font_height;
4046     r.bottom = offset_height + (max + lines) * font_height;
4047     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4048 }
4049 #endif
4050
4051 /*
4052  * Print a message box and perform a fatal exit.
4053  */
4054 void fatalbox(char *fmt, ...)
4055 {
4056     va_list ap;
4057     char stuff[200];
4058
4059     va_start(ap, fmt);
4060     vsprintf(stuff, fmt, ap);
4061     va_end(ap);
4062     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4063     exit(1);
4064 }
4065
4066 /*
4067  * Manage window caption / taskbar flashing, if enabled.
4068  * 0 = stop, 1 = maintain, 2 = start
4069  */
4070 static void flash_window(int mode)
4071 {
4072     static long last_flash = 0;
4073     static int flashing = 0;
4074     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4075         /* stop */
4076         if (flashing) {
4077             FlashWindow(hwnd, FALSE);
4078             flashing = 0;
4079         }
4080
4081     } else if (mode == 2) {
4082         /* start */
4083         if (!flashing) {
4084             last_flash = GetTickCount();
4085             flashing = 1;
4086             FlashWindow(hwnd, TRUE);
4087         }
4088
4089     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4090         /* maintain */
4091         if (flashing) {
4092             long now = GetTickCount();
4093             long fdiff = now - last_flash;
4094             if (fdiff < 0 || fdiff > 450) {
4095                 last_flash = now;
4096                 FlashWindow(hwnd, TRUE);        /* toggle */
4097             }
4098         }
4099     }
4100 }
4101
4102 /*
4103  * Beep.
4104  */
4105 void beep(int mode)
4106 {
4107     if (mode == BELL_DEFAULT) {
4108         /*
4109          * For MessageBeep style bells, we want to be careful of
4110          * timing, because they don't have the nice property of
4111          * PlaySound bells that each one cancels the previous
4112          * active one. So we limit the rate to one per 50ms or so.
4113          */
4114         static long lastbeep = 0;
4115         long beepdiff;
4116
4117         beepdiff = GetTickCount() - lastbeep;
4118         if (beepdiff >= 0 && beepdiff < 50)
4119             return;
4120         MessageBeep(MB_OK);
4121         /*
4122          * The above MessageBeep call takes time, so we record the
4123          * time _after_ it finishes rather than before it starts.
4124          */
4125         lastbeep = GetTickCount();
4126     } else if (mode == BELL_WAVEFILE) {
4127         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4128             char buf[sizeof(cfg.bell_wavefile) + 80];
4129             sprintf(buf, "Unable to play sound file\n%s\n"
4130                     "Using default sound instead", cfg.bell_wavefile);
4131             MessageBox(hwnd, buf, "PuTTY Sound Error",
4132                        MB_OK | MB_ICONEXCLAMATION);
4133             cfg.beep = BELL_DEFAULT;
4134         }
4135     }
4136     /* Otherwise, either visual bell or disabled; do nothing here */
4137     if (!has_focus) {
4138         flash_window(2);               /* start */
4139     }
4140 }
4141
4142 /*
4143  * Minimise or restore the window in response to a server-side
4144  * request.
4145  */
4146 void set_iconic(int iconic)
4147 {
4148     if (IsIconic(hwnd)) {
4149         if (!iconic)
4150             ShowWindow(hwnd, SW_RESTORE);
4151     } else {
4152         if (iconic)
4153             ShowWindow(hwnd, SW_MINIMIZE);
4154     }
4155 }
4156
4157 /*
4158  * Move the window in response to a server-side request.
4159  */
4160 void move_window(int x, int y)
4161 {
4162     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4163 }
4164
4165 /*
4166  * Move the window to the top or bottom of the z-order in response
4167  * to a server-side request.
4168  */
4169 void set_zorder(int top)
4170 {
4171     if (cfg.alwaysontop)
4172         return;                        /* ignore */
4173     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4174                  SWP_NOMOVE | SWP_NOSIZE);
4175 }
4176
4177 /*
4178  * Refresh the window in response to a server-side request.
4179  */
4180 void refresh_window(void)
4181 {
4182     InvalidateRect(hwnd, NULL, TRUE);
4183 }
4184
4185 /*
4186  * Maximise or restore the window in response to a server-side
4187  * request.
4188  */
4189 void set_zoomed(int zoomed)
4190 {
4191     if (IsZoomed(hwnd)) {
4192         if (!zoomed)
4193             ShowWindow(hwnd, SW_RESTORE);
4194     } else {
4195         if (zoomed)
4196             ShowWindow(hwnd, SW_MAXIMIZE);
4197     }
4198 }
4199
4200 /*
4201  * Report whether the window is iconic, for terminal reports.
4202  */
4203 int is_iconic(void)
4204 {
4205     return IsIconic(hwnd);
4206 }
4207
4208 /*
4209  * Report the window's position, for terminal reports.
4210  */
4211 void get_window_pos(int *x, int *y)
4212 {
4213     RECT r;
4214     GetWindowRect(hwnd, &r);
4215     *x = r.left;
4216     *y = r.top;
4217 }
4218
4219 /*
4220  * Report the window's pixel size, for terminal reports.
4221  */
4222 void get_window_pixels(int *x, int *y)
4223 {
4224     RECT r;
4225     GetWindowRect(hwnd, &r);
4226     *x = r.right - r.left;
4227     *y = r.bottom - r.top;
4228 }
4229
4230 /*
4231  * Return the window or icon title.
4232  */
4233 char *get_window_title(int icon)
4234 {
4235     return icon ? icon_name : window_name;
4236 }
4237
4238 /*
4239  * See if we're in full-screen mode.
4240  */
4241 int is_full_screen()
4242 {
4243     if (!IsZoomed(hwnd))
4244         return FALSE;
4245     if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4246         return FALSE;
4247     return TRUE;
4248 }
4249
4250 /*
4251  * Go full-screen. This should only be called when we are already
4252  * maximised.
4253  */
4254 void make_full_screen()
4255 {
4256     DWORD style;
4257     int x, y, w, h;
4258
4259     assert(IsZoomed(hwnd));
4260
4261     /* Remove the window furniture. */
4262     style = GetWindowLong(hwnd, GWL_STYLE);
4263     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4264     if (cfg.scrollbar_in_fullscreen)
4265         style |= WS_VSCROLL;
4266     else
4267         style &= ~WS_VSCROLL;
4268     SetWindowLong(hwnd, GWL_STYLE, style);
4269
4270     /* Resize ourselves to exactly cover the nearest monitor. */
4271 #ifdef MONITOR_DEFAULTTONEAREST
4272     {
4273         HMONITOR mon;
4274         MONITORINFO mi;
4275         mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4276         mi.cbSize = sizeof(mi);
4277         GetMonitorInfo(mon, &mi);
4278         x = mi.rcMonitor.left;
4279         y = mi.rcMonitor.top;
4280         w = mi.rcMonitor.right;
4281         h = mi.rcMonitor.bottom;
4282     }
4283 #else
4284     x = y = 0;
4285     w = GetSystemMetrics(SM_CXSCREEN);
4286     h = GetSystemMetrics(SM_CYSCREEN);
4287 #endif
4288     SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4289
4290     /* Tick the menu item in the System menu. */
4291     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4292                   MF_CHECKED);
4293 }
4294
4295 /*
4296  * Clear the full-screen attributes.
4297  */
4298 void clear_full_screen()
4299 {
4300     DWORD oldstyle, style;
4301
4302     /* Reinstate the window furniture. */
4303     style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4304     style |= WS_CAPTION | WS_BORDER;
4305     if (cfg.resize_action == RESIZE_DISABLED)
4306         style &= ~WS_THICKFRAME;
4307     else
4308         style |= WS_THICKFRAME;
4309     if (cfg.scrollbar)
4310         style |= WS_VSCROLL;
4311     else
4312         style &= ~WS_VSCROLL;
4313     if (style != oldstyle) {
4314         SetWindowLong(hwnd, GWL_STYLE, style);
4315         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4316                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4317                      SWP_FRAMECHANGED);
4318     }
4319
4320     /* Untick the menu item in the System menu. */
4321     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4322                   MF_UNCHECKED);
4323 }
4324
4325 /*
4326  * Toggle full-screen mode.
4327  */
4328 void flip_full_screen()
4329 {
4330     if (is_full_screen()) {
4331         ShowWindow(hwnd, SW_RESTORE);
4332     } else if (IsZoomed(hwnd)) {
4333         make_full_screen();
4334     } else {
4335         SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4336         ShowWindow(hwnd, SW_MAXIMIZE);
4337     }
4338 }