]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
Fix compiler warnings
[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      * Finally show the window!
637      */
638     ShowWindow(hwnd, show);
639     SetForegroundWindow(hwnd);
640
641     /*
642      * Open the initial log file if there is one.
643      */
644     logfopen();
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_DISABLED)
1657                     term_size(cfg.height, cfg.width, cfg.savelines);
1658
1659                 /* Enable or disable the scroll bar, etc */
1660                 {
1661                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1662                     LONG nexflag, exflag =
1663                         GetWindowLong(hwnd, GWL_EXSTYLE);
1664
1665                     nexflag = exflag;
1666                     if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1667                         if (cfg.alwaysontop) {
1668                             nexflag |= WS_EX_TOPMOST;
1669                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1670                                          SWP_NOMOVE | SWP_NOSIZE);
1671                         } else {
1672                             nexflag &= ~(WS_EX_TOPMOST);
1673                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1674                                          SWP_NOMOVE | SWP_NOSIZE);
1675                         }
1676                     }
1677                     if (cfg.sunken_edge)
1678                         nexflag |= WS_EX_CLIENTEDGE;
1679                     else
1680                         nexflag &= ~(WS_EX_CLIENTEDGE);
1681
1682                     nflg = flag;
1683                     if (is_full_screen() ?
1684                         cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1685                         nflg |= WS_VSCROLL;
1686                     else
1687                         nflg &= ~WS_VSCROLL;
1688                     if (cfg.resize_action == RESIZE_DISABLED)
1689                         nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1690                     else
1691                         nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1692
1693                     if (nflg != flag || nexflag != exflag) {
1694                         if (nflg != flag)
1695                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1696                         if (nexflag != exflag)
1697                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1698
1699                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1700                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1701                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1702                                      SWP_FRAMECHANGED);
1703
1704                         init_lvl = 2;
1705                     }
1706                 }
1707
1708                 /* Oops */
1709                 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1710                     force_normal(hwnd);
1711                     init_lvl = 2;
1712                 }
1713
1714                 set_title(cfg.wintitle);
1715                 if (IsIconic(hwnd)) {
1716                     SetWindowText(hwnd,
1717                                   cfg.win_name_always ? window_name :
1718                                   icon_name);
1719                 }
1720
1721                 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1722                     strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1723                     cfg.fontisbold != prev_cfg.fontisbold ||
1724                     cfg.fontheight != prev_cfg.fontheight ||
1725                     cfg.fontcharset != prev_cfg.fontcharset ||
1726                     cfg.vtmode != prev_cfg.vtmode ||
1727                     cfg.bold_colour != prev_cfg.bold_colour ||
1728                     cfg.resize_action == RESIZE_DISABLED ||
1729                     cfg.resize_action == RESIZE_EITHER ||
1730                     (cfg.resize_action != prev_cfg.resize_action))
1731                     init_lvl = 2;
1732
1733                 InvalidateRect(hwnd, NULL, TRUE);
1734                 reset_window(init_lvl);
1735                 net_pending_errors();
1736             }
1737             break;
1738           case IDM_COPYALL:
1739             term_copyall();
1740             break;
1741           case IDM_CLRSB:
1742             term_clrsb();
1743             break;
1744           case IDM_RESET:
1745             term_pwron();
1746             break;
1747           case IDM_TEL_AYT:
1748             back->special(TS_AYT);
1749             net_pending_errors();
1750             break;
1751           case IDM_TEL_BRK:
1752             back->special(TS_BRK);
1753             net_pending_errors();
1754             break;
1755           case IDM_TEL_SYNCH:
1756             back->special(TS_SYNCH);
1757             net_pending_errors();
1758             break;
1759           case IDM_TEL_EC:
1760             back->special(TS_EC);
1761             net_pending_errors();
1762             break;
1763           case IDM_TEL_EL:
1764             back->special(TS_EL);
1765             net_pending_errors();
1766             break;
1767           case IDM_TEL_GA:
1768             back->special(TS_GA);
1769             net_pending_errors();
1770             break;
1771           case IDM_TEL_NOP:
1772             back->special(TS_NOP);
1773             net_pending_errors();
1774             break;
1775           case IDM_TEL_ABORT:
1776             back->special(TS_ABORT);
1777             net_pending_errors();
1778             break;
1779           case IDM_TEL_AO:
1780             back->special(TS_AO);
1781             net_pending_errors();
1782             break;
1783           case IDM_TEL_IP:
1784             back->special(TS_IP);
1785             net_pending_errors();
1786             break;
1787           case IDM_TEL_SUSP:
1788             back->special(TS_SUSP);
1789             net_pending_errors();
1790             break;
1791           case IDM_TEL_EOR:
1792             back->special(TS_EOR);
1793             net_pending_errors();
1794             break;
1795           case IDM_TEL_EOF:
1796             back->special(TS_EOF);
1797             net_pending_errors();
1798             break;
1799           case IDM_ABOUT:
1800             showabout(hwnd);
1801             break;
1802           case IDM_HELP:
1803             WinHelp(hwnd, help_path,
1804                     help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1805             break;
1806           case SC_MOUSEMENU:
1807             /*
1808              * We get this if the System menu has been activated
1809              * using the mouse.
1810              */
1811             show_mouseptr(1);
1812             break;
1813           case SC_KEYMENU:
1814             /*
1815              * We get this if the System menu has been activated
1816              * using the keyboard. This might happen from within
1817              * TranslateKey, in which case it really wants to be
1818              * followed by a `space' character to actually _bring
1819              * the menu up_ rather than just sitting there in
1820              * `ready to appear' state.
1821              */
1822             show_mouseptr(1);          /* make sure pointer is visible */
1823             if( lParam == 0 )
1824                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1825             break;
1826           case IDM_FULLSCREEN:
1827             flip_full_screen();
1828             break;
1829           default:
1830             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1831                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1832             }
1833         }
1834         break;
1835
1836 #define X_POS(l) ((int)(short)LOWORD(l))
1837 #define Y_POS(l) ((int)(short)HIWORD(l))
1838
1839 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1840 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1841 #define WHEEL_DELTA 120
1842       case WM_MOUSEWHEEL:
1843         {
1844             wheel_accumulator += (short) HIWORD(wParam);
1845             wParam = LOWORD(wParam);
1846
1847             /* process events when the threshold is reached */
1848             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1849                 int b;
1850
1851                 /* reduce amount for next time */
1852                 if (wheel_accumulator > 0) {
1853                     b = MBT_WHEEL_UP;
1854                     wheel_accumulator -= WHEEL_DELTA;
1855                 } else if (wheel_accumulator < 0) {
1856                     b = MBT_WHEEL_DOWN;
1857                     wheel_accumulator += WHEEL_DELTA;
1858                 } else
1859                     break;
1860
1861                 if (send_raw_mouse) {
1862                     /* send a mouse-down followed by a mouse up */
1863                     
1864                     term_mouse(b,
1865                                MA_CLICK,
1866                                TO_CHR_X(X_POS(lParam)),
1867                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1868                                wParam & MK_CONTROL, is_alt_pressed());
1869                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1870                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1871                                wParam & MK_CONTROL, is_alt_pressed());
1872                 } else {
1873                     /* trigger a scroll */
1874                     term_scroll(0,
1875                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1876                 }
1877             }
1878             return 0;
1879         }
1880       case WM_LBUTTONDOWN:
1881       case WM_MBUTTONDOWN:
1882       case WM_RBUTTONDOWN:
1883       case WM_LBUTTONUP:
1884       case WM_MBUTTONUP:
1885       case WM_RBUTTONUP:
1886         {
1887             int button, press;
1888
1889             switch (message) {
1890               case WM_LBUTTONDOWN:
1891                 button = MBT_LEFT;
1892                 press = 1;
1893                 break;
1894               case WM_MBUTTONDOWN:
1895                 button = MBT_MIDDLE;
1896                 press = 1;
1897                 break;
1898               case WM_RBUTTONDOWN:
1899                 button = MBT_RIGHT;
1900                 press = 1;
1901                 break;
1902               case WM_LBUTTONUP:
1903                 button = MBT_LEFT;
1904                 press = 0;
1905                 break;
1906               case WM_MBUTTONUP:
1907                 button = MBT_MIDDLE;
1908                 press = 0;
1909                 break;
1910               case WM_RBUTTONUP:
1911                 button = MBT_RIGHT;
1912                 press = 0;
1913                 break;
1914               default:
1915                 button = press = 0;    /* shouldn't happen */
1916             }
1917             show_mouseptr(1);
1918             /*
1919              * Special case: in full-screen mode, if the left
1920              * button is clicked in the very top left corner of the
1921              * window, we put up the System menu instead of doing
1922              * selection.
1923              */
1924             if (is_full_screen() && press && button == MBT_LEFT &&
1925                 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1926                 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1927                 return 0;
1928             }
1929             if (press) {
1930                 click(button,
1931                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1932                       wParam & MK_SHIFT, wParam & MK_CONTROL,
1933                       is_alt_pressed());
1934                 SetCapture(hwnd);
1935             } else {
1936                 term_mouse(button, MA_RELEASE,
1937                            TO_CHR_X(X_POS(lParam)),
1938                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1939                            wParam & MK_CONTROL, is_alt_pressed());
1940                 ReleaseCapture();
1941             }
1942         }
1943         return 0;
1944       case WM_MOUSEMOVE:
1945         show_mouseptr(1);
1946         /*
1947          * Add the mouse position and message time to the random
1948          * number noise.
1949          */
1950         noise_ultralight(lParam);
1951
1952         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1953             Mouse_Button b;
1954             if (wParam & MK_LBUTTON)
1955                 b = MBT_LEFT;
1956             else if (wParam & MK_MBUTTON)
1957                 b = MBT_MIDDLE;
1958             else
1959                 b = MBT_RIGHT;
1960             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1961                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1962                        wParam & MK_CONTROL, is_alt_pressed());
1963         }
1964         return 0;
1965       case WM_NCMOUSEMOVE:
1966         show_mouseptr(1);
1967         noise_ultralight(lParam);
1968         return 0;
1969       case WM_IGNORE_CLIP:
1970         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1971         break;
1972       case WM_DESTROYCLIPBOARD:
1973         if (!ignore_clip)
1974             term_deselect();
1975         ignore_clip = FALSE;
1976         return 0;
1977       case WM_PAINT:
1978         {
1979             PAINTSTRUCT p;
1980             HideCaret(hwnd);
1981             hdc = BeginPaint(hwnd, &p);
1982             if (pal) {
1983                 SelectPalette(hdc, pal, TRUE);
1984                 RealizePalette(hdc);
1985             }
1986             term_paint(hdc, 
1987                        (p.rcPaint.left-offset_width)/font_width,
1988                        (p.rcPaint.top-offset_height)/font_height,
1989                        (p.rcPaint.right-offset_width-1)/font_width,
1990                        (p.rcPaint.bottom-offset_height-1)/font_height);
1991
1992             if (p.fErase ||
1993                 p.rcPaint.left  < offset_width  ||
1994                 p.rcPaint.top   < offset_height ||
1995                 p.rcPaint.right >= offset_width + font_width*cols ||
1996                 p.rcPaint.bottom>= offset_height + font_height*rows)
1997             {
1998                 HBRUSH fillcolour, oldbrush;
1999                 HPEN   edge, oldpen;
2000                 fillcolour = CreateSolidBrush (
2001                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2002                 oldbrush = SelectObject(hdc, fillcolour);
2003                 edge = CreatePen(PS_SOLID, 0, 
2004                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2005                 oldpen = SelectObject(hdc, edge);
2006
2007                 ExcludeClipRect(hdc, 
2008                         offset_width, offset_height,
2009                         offset_width+font_width*cols,
2010                         offset_height+font_height*rows);
2011
2012                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
2013                           p.rcPaint.right, p.rcPaint.bottom);
2014
2015                 // SelectClipRgn(hdc, NULL);
2016
2017                 SelectObject(hdc, oldbrush);
2018                 DeleteObject(fillcolour);
2019                 SelectObject(hdc, oldpen);
2020                 DeleteObject(edge);
2021             }
2022             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2023             SelectObject(hdc, GetStockObject(WHITE_PEN));
2024             EndPaint(hwnd, &p);
2025             ShowCaret(hwnd);
2026         }
2027         return 0;
2028       case WM_NETEVENT:
2029         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2030          * but the only one that's likely to try to overload us is FD_READ.
2031          * This means buffering just one is fine.
2032          */
2033         if (pending_netevent)
2034             enact_pending_netevent();
2035
2036         pending_netevent = TRUE;
2037         pend_netevent_wParam = wParam;
2038         pend_netevent_lParam = lParam;
2039         if (WSAGETSELECTEVENT(lParam) != FD_READ)
2040             enact_pending_netevent();
2041
2042         time(&last_movement);
2043         return 0;
2044       case WM_SETFOCUS:
2045         has_focus = TRUE;
2046         CreateCaret(hwnd, caretbm, font_width, font_height);
2047         ShowCaret(hwnd);
2048         flash_window(0);               /* stop */
2049         compose_state = 0;
2050         term_out();
2051         term_update();
2052         break;
2053       case WM_KILLFOCUS:
2054         show_mouseptr(1);
2055         has_focus = FALSE;
2056         DestroyCaret();
2057         term_out();
2058         term_update();
2059         break;
2060       case WM_ENTERSIZEMOVE:
2061 #ifdef RDB_DEBUG_PATCH
2062         debug((27, "WM_ENTERSIZEMOVE"));
2063 #endif
2064         EnableSizeTip(1);
2065         resizing = TRUE;
2066         need_backend_resize = FALSE;
2067         break;
2068       case WM_EXITSIZEMOVE:
2069         EnableSizeTip(0);
2070         resizing = FALSE;
2071 #ifdef RDB_DEBUG_PATCH
2072         debug((27, "WM_EXITSIZEMOVE"));
2073 #endif
2074         if (need_backend_resize) {
2075             term_size(cfg.height, cfg.width, cfg.savelines);
2076             InvalidateRect(hwnd, NULL, TRUE);
2077         }
2078         break;
2079       case WM_SIZING:
2080         /*
2081          * This does two jobs:
2082          * 1) Keep the sizetip uptodate
2083          * 2) Make sure the window size is _stepped_ in units of the font size.
2084          */
2085         if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2086             int width, height, w, h, ew, eh;
2087             LPRECT r = (LPRECT) lParam;
2088
2089             if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2090                     (cfg.height != rows || cfg.width != cols )) {
2091                 /* 
2092                  * Great! It seems that both the terminal size and the
2093                  * font size have been changed and the user is now dragging.
2094                  * 
2095                  * It will now be difficult to get back to the configured
2096                  * font size!
2097                  *
2098                  * This would be easier but it seems to be too confusing.
2099
2100                 term_size(cfg.height, cfg.width, cfg.savelines);
2101                 reset_window(2);
2102                  */
2103                 cfg.height=rows; cfg.width=cols;
2104
2105                 InvalidateRect(hwnd, NULL, TRUE);
2106                 need_backend_resize = TRUE;
2107             }
2108
2109             width = r->right - r->left - extra_width;
2110             height = r->bottom - r->top - extra_height;
2111             w = (width + font_width / 2) / font_width;
2112             if (w < 1)
2113                 w = 1;
2114             h = (height + font_height / 2) / font_height;
2115             if (h < 1)
2116                 h = 1;
2117             UpdateSizeTip(hwnd, w, h);
2118             ew = width - w * font_width;
2119             eh = height - h * font_height;
2120             if (ew != 0) {
2121                 if (wParam == WMSZ_LEFT ||
2122                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2123                     r->left += ew;
2124                 else
2125                     r->right -= ew;
2126             }
2127             if (eh != 0) {
2128                 if (wParam == WMSZ_TOP ||
2129                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2130                     r->top += eh;
2131                 else
2132                     r->bottom -= eh;
2133             }
2134             if (ew || eh)
2135                 return 1;
2136             else
2137                 return 0;
2138         } else {
2139             int width, height, w, h, rv = 0;
2140             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2141             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2142             LPRECT r = (LPRECT) lParam;
2143
2144             width = r->right - r->left - ex_width;
2145             height = r->bottom - r->top - ex_height;
2146
2147             w = (width + cols/2)/cols;
2148             h = (height + rows/2)/rows;
2149             if ( r->right != r->left + w*cols + ex_width) 
2150                 rv = 1;
2151
2152             if (wParam == WMSZ_LEFT ||
2153                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2154                 r->left = r->right - w*cols - ex_width;
2155             else
2156                 r->right = r->left + w*cols + ex_width;
2157
2158             if (r->bottom != r->top + h*rows + ex_height)
2159                 rv = 1;
2160
2161             if (wParam == WMSZ_TOP ||
2162                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2163                 r->top = r->bottom - h*rows - ex_height;
2164             else
2165                 r->bottom = r->top + h*rows + ex_height;
2166
2167             return rv;
2168         }
2169         /* break;  (never reached) */
2170       case WM_FULLSCR_ON_MAX:
2171         fullscr_on_max = TRUE;
2172         break;
2173       case WM_SIZE:
2174 #ifdef RDB_DEBUG_PATCH
2175         debug((27, "WM_SIZE %s (%d,%d)",
2176                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2177                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2178                 (wParam == SIZE_RESTORED && resizing) ? "to":
2179                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2180                 "...",
2181             LOWORD(lParam), HIWORD(lParam)));
2182 #endif
2183         if (wParam == SIZE_MINIMIZED)
2184             SetWindowText(hwnd,
2185                           cfg.win_name_always ? window_name : icon_name);
2186         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2187             SetWindowText(hwnd, window_name);
2188
2189         if (cfg.resize_action == RESIZE_DISABLED) {
2190             /* A resize, well it better be a minimize. */
2191             reset_window(-1);
2192         } else {
2193
2194             int width, height, w, h;
2195
2196             width = LOWORD(lParam);
2197             height = HIWORD(lParam);
2198
2199             if (!resizing) {
2200                 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2201                     was_zoomed = 1;
2202                     prev_rows = rows;
2203                     prev_cols = cols;
2204                     if (cfg.resize_action == RESIZE_TERM) {
2205                         w = width / font_width;
2206                         if (w < 1) w = 1;
2207                         h = height / font_height;
2208                         if (h < 1) h = 1;
2209
2210                         term_size(h, w, cfg.savelines);
2211                     }
2212                     if (fullscr_on_max)
2213                         make_full_screen();
2214                     fullscr_on_max = FALSE;
2215                     reset_window(0);
2216                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2217                     was_zoomed = 0;
2218                     clear_full_screen();
2219                     if (cfg.resize_action == RESIZE_TERM)
2220                         term_size(prev_rows, prev_cols, cfg.savelines);
2221                     if (cfg.resize_action != RESIZE_FONT)
2222                         reset_window(2);
2223                     else
2224                         reset_window(0);
2225                 }
2226                 /* This is an unexpected resize, these will normally happen
2227                  * if the window is too large. Probably either the user
2228                  * selected a huge font or the screen size has changed.
2229                  *
2230                  * This is also called with minimize.
2231                  */
2232                 else reset_window(-1);
2233             }
2234
2235             /*
2236              * Don't call back->size in mid-resize. (To prevent
2237              * massive numbers of resize events getting sent
2238              * down the connection during an NT opaque drag.)
2239              */
2240             if (resizing) {
2241                 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2242                     need_backend_resize = TRUE;
2243                     w = (width-cfg.window_border*2) / font_width;
2244                     if (w < 1) w = 1;
2245                     h = (height-cfg.window_border*2) / font_height;
2246                     if (h < 1) h = 1;
2247
2248                     cfg.height = h;
2249                     cfg.width = w;
2250                 } else 
2251                     reset_window(0);
2252             }
2253         }
2254         return 0;
2255       case WM_VSCROLL:
2256         switch (LOWORD(wParam)) {
2257           case SB_BOTTOM:
2258             term_scroll(-1, 0);
2259             break;
2260           case SB_TOP:
2261             term_scroll(+1, 0);
2262             break;
2263           case SB_LINEDOWN:
2264             term_scroll(0, +1);
2265             break;
2266           case SB_LINEUP:
2267             term_scroll(0, -1);
2268             break;
2269           case SB_PAGEDOWN:
2270             term_scroll(0, +rows / 2);
2271             break;
2272           case SB_PAGEUP:
2273             term_scroll(0, -rows / 2);
2274             break;
2275           case SB_THUMBPOSITION:
2276           case SB_THUMBTRACK:
2277             term_scroll(1, HIWORD(wParam));
2278             break;
2279         }
2280         break;
2281       case WM_PALETTECHANGED:
2282         if ((HWND) wParam != hwnd && pal != NULL) {
2283             HDC hdc = get_ctx();
2284             if (hdc) {
2285                 if (RealizePalette(hdc) > 0)
2286                     UpdateColors(hdc);
2287                 free_ctx(hdc);
2288             }
2289         }
2290         break;
2291       case WM_QUERYNEWPALETTE:
2292         if (pal != NULL) {
2293             HDC hdc = get_ctx();
2294             if (hdc) {
2295                 if (RealizePalette(hdc) > 0)
2296                     UpdateColors(hdc);
2297                 free_ctx(hdc);
2298                 return TRUE;
2299             }
2300         }
2301         return FALSE;
2302       case WM_KEYDOWN:
2303       case WM_SYSKEYDOWN:
2304       case WM_KEYUP:
2305       case WM_SYSKEYUP:
2306         /*
2307          * Add the scan code and keypress timing to the random
2308          * number noise.
2309          */
2310         noise_ultralight(lParam);
2311
2312         /*
2313          * We don't do TranslateMessage since it disassociates the
2314          * resulting CHAR message from the KEYDOWN that sparked it,
2315          * which we occasionally don't want. Instead, we process
2316          * KEYDOWN, and call the Win32 translator functions so that
2317          * we get the translations under _our_ control.
2318          */
2319         {
2320             unsigned char buf[20];
2321             int len;
2322
2323             if (wParam == VK_PROCESSKEY) {
2324                 MSG m;
2325                 m.hwnd = hwnd;
2326                 m.message = WM_KEYDOWN;
2327                 m.wParam = wParam;
2328                 m.lParam = lParam & 0xdfff;
2329                 TranslateMessage(&m);
2330             } else {
2331                 len = TranslateKey(message, wParam, lParam, buf);
2332                 if (len == -1)
2333                     return DefWindowProc(hwnd, message, wParam, lParam);
2334
2335                 if (len != 0) {
2336                     /*
2337                      * Interrupt an ongoing paste. I'm not sure
2338                      * this is sensible, but for the moment it's
2339                      * preferable to having to faff about buffering
2340                      * things.
2341                      */
2342                     term_nopaste();
2343
2344                     /*
2345                      * We need not bother about stdin backlogs
2346                      * here, because in GUI PuTTY we can't do
2347                      * anything about it anyway; there's no means
2348                      * of asking Windows to hold off on KEYDOWN
2349                      * messages. We _have_ to buffer everything
2350                      * we're sent.
2351                      */
2352                     ldisc_send(buf, len, 1);
2353                     show_mouseptr(0);
2354                 }
2355             }
2356         }
2357         net_pending_errors();
2358         return 0;
2359       case WM_INPUTLANGCHANGE:
2360         /* wParam == Font number */
2361         /* lParam == Locale */
2362         set_input_locale((HKL)lParam);
2363         break;
2364       case WM_IME_NOTIFY:
2365         if(wParam == IMN_SETOPENSTATUS) {
2366             HIMC hImc = ImmGetContext(hwnd);
2367             ImmSetCompositionFont(hImc, &lfont);
2368             ImmReleaseContext(hwnd, hImc);
2369             return 0;
2370         }
2371         break;
2372       case WM_IME_COMPOSITION:
2373         {
2374             HIMC hIMC;
2375             int n;
2376             char *buff;
2377    
2378             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2379                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2380
2381             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2382                 break; /* fall back to DefWindowProc */
2383
2384             hIMC = ImmGetContext(hwnd);
2385             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2386
2387             if (n > 0) {
2388                 buff = (char*) smalloc(n);
2389                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2390                 luni_send((unsigned short *)buff, n / 2, 1);
2391                 free(buff);
2392             }
2393             ImmReleaseContext(hwnd, hIMC);
2394             return 1;
2395         }
2396
2397       case WM_IME_CHAR:
2398         if (wParam & 0xFF00) {
2399             unsigned char buf[2];
2400
2401             buf[1] = wParam;
2402             buf[0] = wParam >> 8;
2403             lpage_send(kbd_codepage, buf, 2, 1);
2404         } else {
2405             char c = (unsigned char) wParam;
2406             lpage_send(kbd_codepage, &c, 1, 1);
2407         }
2408         return (0);
2409       case WM_CHAR:
2410       case WM_SYSCHAR:
2411         /*
2412          * Nevertheless, we are prepared to deal with WM_CHAR
2413          * messages, should they crop up. So if someone wants to
2414          * post the things to us as part of a macro manoeuvre,
2415          * we're ready to cope.
2416          */
2417         {
2418             char c = (unsigned char)wParam;
2419             lpage_send(CP_ACP, &c, 1, 1);
2420         }
2421         return 0;
2422       case WM_SETCURSOR:
2423         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2424             SetCursor(LoadCursor(NULL, IDC_ARROW));
2425             return TRUE;
2426         }
2427     }
2428
2429     return DefWindowProc(hwnd, message, wParam, lParam);
2430 }
2431
2432 /*
2433  * Move the system caret. (We maintain one, even though it's
2434  * invisible, for the benefit of blind people: apparently some
2435  * helper software tracks the system caret, so we should arrange to
2436  * have one.)
2437  */
2438 void sys_cursor(int x, int y)
2439 {
2440     COMPOSITIONFORM cf;
2441     HIMC hIMC;
2442
2443     if (!has_focus) return;
2444     
2445     SetCaretPos(x * font_width + offset_width,
2446                 y * font_height + offset_height);
2447
2448     /* IMM calls on Win98 and beyond only */
2449     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2450     
2451     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2452             osVersion.dwMinorVersion == 0) return; /* 95 */
2453
2454     /* we should have the IMM functions */
2455     hIMC = ImmGetContext(hwnd);
2456     cf.dwStyle = CFS_POINT;
2457     cf.ptCurrentPos.x = x * font_width + offset_width;
2458     cf.ptCurrentPos.y = y * font_height + offset_height;
2459     ImmSetCompositionWindow(hIMC, &cf);
2460
2461     ImmReleaseContext(hwnd, hIMC);
2462 }
2463
2464 /*
2465  * Draw a line of text in the window, at given character
2466  * coordinates, in given attributes.
2467  *
2468  * We are allowed to fiddle with the contents of `text'.
2469  */
2470 void do_text(Context ctx, int x, int y, char *text, int len,
2471              unsigned long attr, int lattr)
2472 {
2473     COLORREF fg, bg, t;
2474     int nfg, nbg, nfont;
2475     HDC hdc = ctx;
2476     RECT line_box;
2477     int force_manual_underline = 0;
2478     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2479     int char_width = fnt_width;
2480     int text_adjust = 0;
2481     static int *IpDx = 0, IpDxLEN = 0;
2482
2483     if (attr & ATTR_WIDE)
2484         char_width *= 2;
2485
2486     if (len > IpDxLEN || IpDx[0] != char_width) {
2487         int i;
2488         if (len > IpDxLEN) {
2489             sfree(IpDx);
2490             IpDx = smalloc((len + 16) * sizeof(int));
2491             IpDxLEN = (len + 16);
2492         }
2493         for (i = 0; i < IpDxLEN; i++)
2494             IpDx[i] = char_width;
2495     }
2496
2497     /* Only want the left half of double width lines */
2498     if (lattr != LATTR_NORM && x*2 >= cols)
2499         return;
2500
2501     x *= fnt_width;
2502     y *= font_height;
2503     x += offset_width;
2504     y += offset_height;
2505
2506     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2507         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2508         attr ^= ATTR_CUR_XOR;
2509     }
2510
2511     nfont = 0;
2512     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2513         /* Assume a poorman font is borken in other ways too. */
2514         lattr = LATTR_WIDE;
2515     } else
2516         switch (lattr) {
2517           case LATTR_NORM:
2518             break;
2519           case LATTR_WIDE:
2520             nfont |= FONT_WIDE;
2521             break;
2522           default:
2523             nfont |= FONT_WIDE + FONT_HIGH;
2524             break;
2525         }
2526     if (attr & ATTR_NARROW)
2527         nfont |= FONT_NARROW;
2528
2529     /* Special hack for the VT100 linedraw glyphs. */
2530     if ((attr & CSET_MASK) == 0x2300) {
2531         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2532             switch ((unsigned char) (text[0])) {
2533               case 0xBA:
2534                 text_adjust = -2 * font_height / 5;
2535                 break;
2536               case 0xBB:
2537                 text_adjust = -1 * font_height / 5;
2538                 break;
2539               case 0xBC:
2540                 text_adjust = font_height / 5;
2541                 break;
2542               case 0xBD:
2543                 text_adjust = 2 * font_height / 5;
2544                 break;
2545             }
2546             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2547                 text_adjust *= 2;
2548             attr &= ~CSET_MASK;
2549             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2550             attr |= (unitab_xterm['q'] & CSET_MASK);
2551             if (attr & ATTR_UNDER) {
2552                 attr &= ~ATTR_UNDER;
2553                 force_manual_underline = 1;
2554             }
2555         }
2556     }
2557
2558     /* Anything left as an original character set is unprintable. */
2559     if (DIRECT_CHAR(attr)) {
2560         attr &= ~CSET_MASK;
2561         attr |= 0xFF00;
2562         memset(text, 0xFD, len);
2563     }
2564
2565     /* OEM CP */
2566     if ((attr & CSET_MASK) == ATTR_OEMCP)
2567         nfont |= FONT_OEM;
2568
2569     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2570     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2571     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2572         nfont |= FONT_BOLD;
2573     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2574         nfont |= FONT_UNDERLINE;
2575     another_font(nfont);
2576     if (!fonts[nfont]) {
2577         if (nfont & FONT_UNDERLINE)
2578             force_manual_underline = 1;
2579         /* Don't do the same for manual bold, it could be bad news. */
2580
2581         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2582     }
2583     another_font(nfont);
2584     if (!fonts[nfont])
2585         nfont = FONT_NORMAL;
2586     if (attr & ATTR_REVERSE) {
2587         t = nfg;
2588         nfg = nbg;
2589         nbg = t;
2590     }
2591     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2592         nfg++;
2593     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2594         nbg++;
2595     fg = colours[nfg];
2596     bg = colours[nbg];
2597     SelectObject(hdc, fonts[nfont]);
2598     SetTextColor(hdc, fg);
2599     SetBkColor(hdc, bg);
2600     SetBkMode(hdc, OPAQUE);
2601     line_box.left = x;
2602     line_box.top = y;
2603     line_box.right = x + char_width * len;
2604     line_box.bottom = y + font_height;
2605
2606     /* Only want the left half of double width lines */
2607     if (line_box.right > font_width*cols+offset_width)
2608         line_box.right = font_width*cols+offset_width;
2609
2610     /* We're using a private area for direct to font. (512 chars.) */
2611     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2612         /* Ho Hum, dbcs fonts are a PITA! */
2613         /* To display on W9x I have to convert to UCS */
2614         static wchar_t *uni_buf = 0;
2615         static int uni_len = 0;
2616         int nlen, mptr;
2617         if (len > uni_len) {
2618             sfree(uni_buf);
2619             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2620         }
2621
2622         for(nlen = mptr = 0; mptr<len; mptr++) {
2623             uni_buf[nlen] = 0xFFFD;
2624             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2625                 IpDx[nlen] += char_width;
2626                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2627                                    text+mptr, 2, uni_buf+nlen, 1);
2628                 mptr++;
2629             }
2630             else
2631             {
2632                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2633                                    text+mptr, 1, uni_buf+nlen, 1);
2634             }
2635             nlen++;
2636         }
2637         if (nlen <= 0)
2638             return;                    /* Eeek! */
2639
2640         ExtTextOutW(hdc, x,
2641                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2642                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2643         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2644             SetBkMode(hdc, TRANSPARENT);
2645             ExtTextOutW(hdc, x - 1,
2646                         y - font_height * (lattr ==
2647                                            LATTR_BOT) + text_adjust,
2648                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2649         }
2650
2651         IpDx[0] = -1;
2652     } else if (DIRECT_FONT(attr)) {
2653         ExtTextOut(hdc, x,
2654                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2655                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2656         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2657             SetBkMode(hdc, TRANSPARENT);
2658
2659             /* GRR: This draws the character outside it's box and can leave
2660              * 'droppings' even with the clip box! I suppose I could loop it
2661              * one character at a time ... yuk. 
2662              * 
2663              * Or ... I could do a test print with "W", and use +1 or -1 for this
2664              * shift depending on if the leftmost column is blank...
2665              */
2666             ExtTextOut(hdc, x - 1,
2667                        y - font_height * (lattr ==
2668                                           LATTR_BOT) + text_adjust,
2669                        ETO_CLIPPED, &line_box, text, len, IpDx);
2670         }
2671     } else {
2672         /* And 'normal' unicode characters */
2673         static WCHAR *wbuf = NULL;
2674         static int wlen = 0;
2675         int i;
2676         if (wlen < len) {
2677             sfree(wbuf);
2678             wlen = len;
2679             wbuf = smalloc(wlen * sizeof(WCHAR));
2680         }
2681         for (i = 0; i < len; i++)
2682             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2683
2684         ExtTextOutW(hdc, x,
2685                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2686                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2687
2688         /* And the shadow bold hack. */
2689         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2690             SetBkMode(hdc, TRANSPARENT);
2691             ExtTextOutW(hdc, x - 1,
2692                         y - font_height * (lattr ==
2693                                            LATTR_BOT) + text_adjust,
2694                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2695         }
2696     }
2697     if (lattr != LATTR_TOP && (force_manual_underline ||
2698                                (und_mode == UND_LINE
2699                                 && (attr & ATTR_UNDER)))) {
2700         HPEN oldpen;
2701         int dec = descent;
2702         if (lattr == LATTR_BOT)
2703             dec = dec * 2 - font_height;
2704
2705         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2706         MoveToEx(hdc, x, y + dec, NULL);
2707         LineTo(hdc, x + len * char_width, y + dec);
2708         oldpen = SelectObject(hdc, oldpen);
2709         DeleteObject(oldpen);
2710     }
2711 }
2712
2713 void do_cursor(Context ctx, int x, int y, char *text, int len,
2714                unsigned long attr, int lattr)
2715 {
2716
2717     int fnt_width;
2718     int char_width;
2719     HDC hdc = ctx;
2720     int ctype = cfg.cursor_type;
2721
2722     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2723         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2724             do_text(ctx, x, y, text, len, attr, lattr);
2725             return;
2726         }
2727         ctype = 2;
2728         attr |= TATTR_RIGHTCURS;
2729     }
2730
2731     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2732     if (attr & ATTR_WIDE)
2733         char_width *= 2;
2734     x *= fnt_width;
2735     y *= font_height;
2736     x += offset_width;
2737     y += offset_height;
2738
2739     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2740         POINT pts[5];
2741         HPEN oldpen;
2742         pts[0].x = pts[1].x = pts[4].x = x;
2743         pts[2].x = pts[3].x = x + char_width - 1;
2744         pts[0].y = pts[3].y = pts[4].y = y;
2745         pts[1].y = pts[2].y = y + font_height - 1;
2746         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2747         Polyline(hdc, pts, 5);
2748         oldpen = SelectObject(hdc, oldpen);
2749         DeleteObject(oldpen);
2750     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2751         int startx, starty, dx, dy, length, i;
2752         if (ctype == 1) {
2753             startx = x;
2754             starty = y + descent;
2755             dx = 1;
2756             dy = 0;
2757             length = char_width;
2758         } else {
2759             int xadjust = 0;
2760             if (attr & TATTR_RIGHTCURS)
2761                 xadjust = char_width - 1;
2762             startx = x + xadjust;
2763             starty = y;
2764             dx = 0;
2765             dy = 1;
2766             length = font_height;
2767         }
2768         if (attr & TATTR_ACTCURS) {
2769             HPEN oldpen;
2770             oldpen =
2771                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2772             MoveToEx(hdc, startx, starty, NULL);
2773             LineTo(hdc, startx + dx * length, starty + dy * length);
2774             oldpen = SelectObject(hdc, oldpen);
2775             DeleteObject(oldpen);
2776         } else {
2777             for (i = 0; i < length; i++) {
2778                 if (i % 2 == 0) {
2779                     SetPixel(hdc, startx, starty, colours[23]);
2780                 }
2781                 startx += dx;
2782                 starty += dy;
2783             }
2784         }
2785     }
2786 }
2787
2788 /* This function gets the actual width of a character in the normal font.
2789  */
2790 int CharWidth(Context ctx, int uc) {
2791     HDC hdc = ctx;
2792     int ibuf = 0;
2793
2794     /* If the font max is the same as the font ave width then this
2795      * function is a no-op.
2796      */
2797     if (!font_dualwidth) return 1;
2798
2799     switch (uc & CSET_MASK) {
2800       case ATTR_ASCII:
2801         uc = unitab_line[uc & 0xFF];
2802         break;
2803       case ATTR_LINEDRW:
2804         uc = unitab_xterm[uc & 0xFF];
2805         break;
2806       case ATTR_SCOACS:
2807         uc = unitab_scoacs[uc & 0xFF];
2808         break;
2809     }
2810     if (DIRECT_FONT(uc)) {
2811         if (dbcs_screenfont) return 1;
2812
2813         /* Speedup, I know of no font where ascii is the wrong width */
2814         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2815             return 1;
2816
2817         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2818             SelectObject(hdc, fonts[FONT_NORMAL]);
2819         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2820             another_font(FONT_OEM);
2821             if (!fonts[FONT_OEM]) return 0;
2822
2823             SelectObject(hdc, fonts[FONT_OEM]);
2824         } else
2825             return 0;
2826
2827         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2828              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2829             return 0;
2830     } else {
2831         /* Speedup, I know of no font where ascii is the wrong width */
2832         if (uc >= ' ' && uc <= '~') return 1;
2833
2834         SelectObject(hdc, fonts[FONT_NORMAL]);
2835         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2836             /* Okay that one worked */ ;
2837         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2838             /* This should work on 9x too, but it's "less accurate" */ ;
2839         else
2840             return 0;
2841     }
2842
2843     ibuf += font_width / 2 -1;
2844     ibuf /= font_width;
2845
2846     return ibuf;
2847 }
2848
2849 /*
2850  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2851  * codes. Returns number of bytes used or zero to drop the message
2852  * or -1 to forward the message to windows.
2853  */
2854 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2855                         unsigned char *output)
2856 {
2857     BYTE keystate[256];
2858     int scan, left_alt = 0, key_down, shift_state;
2859     int r, i, code;
2860     unsigned char *p = output;
2861     static int alt_sum = 0;
2862
2863     HKL kbd_layout = GetKeyboardLayout(0);
2864
2865     static WORD keys[3];
2866     static int compose_char = 0;
2867     static WPARAM compose_key = 0;
2868
2869     r = GetKeyboardState(keystate);
2870     if (!r)
2871         memset(keystate, 0, sizeof(keystate));
2872     else {
2873 #if 0
2874 #define SHOW_TOASCII_RESULT
2875         {                              /* Tell us all about key events */
2876             static BYTE oldstate[256];
2877             static int first = 1;
2878             static int scan;
2879             int ch;
2880             if (first)
2881                 memcpy(oldstate, keystate, sizeof(oldstate));
2882             first = 0;
2883
2884             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2885                 debug(("+"));
2886             } else if ((HIWORD(lParam) & KF_UP)
2887                        && scan == (HIWORD(lParam) & 0xFF)) {
2888                 debug((". U"));
2889             } else {
2890                 debug((".\n"));
2891                 if (wParam >= VK_F1 && wParam <= VK_F20)
2892                     debug(("K_F%d", wParam + 1 - VK_F1));
2893                 else
2894                     switch (wParam) {
2895                       case VK_SHIFT:
2896                         debug(("SHIFT"));
2897                         break;
2898                       case VK_CONTROL:
2899                         debug(("CTRL"));
2900                         break;
2901                       case VK_MENU:
2902                         debug(("ALT"));
2903                         break;
2904                       default:
2905                         debug(("VK_%02x", wParam));
2906                     }
2907                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2908                     debug(("*"));
2909                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2910
2911                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2912                 if (ch >= ' ' && ch <= '~')
2913                     debug((", '%c'", ch));
2914                 else if (ch)
2915                     debug((", $%02x", ch));
2916
2917                 if (keys[0])
2918                     debug((", KB0=%02x", keys[0]));
2919                 if (keys[1])
2920                     debug((", KB1=%02x", keys[1]));
2921                 if (keys[2])
2922                     debug((", KB2=%02x", keys[2]));
2923
2924                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2925                     debug((", S"));
2926                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2927                     debug((", C"));
2928                 if ((HIWORD(lParam) & KF_EXTENDED))
2929                     debug((", E"));
2930                 if ((HIWORD(lParam) & KF_UP))
2931                     debug((", U"));
2932             }
2933
2934             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2935             else if ((HIWORD(lParam) & KF_UP))
2936                 oldstate[wParam & 0xFF] ^= 0x80;
2937             else
2938                 oldstate[wParam & 0xFF] ^= 0x81;
2939
2940             for (ch = 0; ch < 256; ch++)
2941                 if (oldstate[ch] != keystate[ch])
2942                     debug((", M%02x=%02x", ch, keystate[ch]));
2943
2944             memcpy(oldstate, keystate, sizeof(oldstate));
2945         }
2946 #endif
2947
2948         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2949             keystate[VK_RMENU] = keystate[VK_MENU];
2950         }
2951
2952
2953         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2954         if ((cfg.funky_type == 3 ||
2955              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2956             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2957
2958             wParam = VK_EXECUTE;
2959
2960             /* UnToggle NUMLock */
2961             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2962                 keystate[VK_NUMLOCK] ^= 1;
2963         }
2964
2965         /* And write back the 'adjusted' state */
2966         SetKeyboardState(keystate);
2967     }
2968
2969     /* Disable Auto repeat if required */
2970     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2971         return 0;
2972
2973     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2974         left_alt = 1;
2975
2976     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2977
2978     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2979     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2980         if (cfg.ctrlaltkeys)
2981             keystate[VK_MENU] = 0;
2982         else {
2983             keystate[VK_RMENU] = 0x80;
2984             left_alt = 0;
2985         }
2986     }
2987
2988     alt_pressed = (left_alt && key_down);
2989
2990     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2991     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2992         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2993
2994     /* Note if AltGr was pressed and if it was used as a compose key */
2995     if (!compose_state) {
2996         compose_key = 0x100;
2997         if (cfg.compose_key) {
2998             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2999                 compose_key = wParam;
3000         }
3001         if (wParam == VK_APPS)
3002             compose_key = wParam;
3003     }
3004
3005     if (wParam == compose_key) {
3006         if (compose_state == 0
3007             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3008                 1;
3009         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3010             compose_state = 2;
3011         else
3012             compose_state = 0;
3013     } else if (compose_state == 1 && wParam != VK_CONTROL)
3014         compose_state = 0;
3015
3016     /* 
3017      * Record that we pressed key so the scroll window can be reset, but
3018      * be careful to avoid Shift-UP/Down
3019      */
3020     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3021         wParam != VK_MENU && wParam != VK_CONTROL) {
3022         seen_key_event = 1;
3023     }
3024
3025     if (compose_state > 1 && left_alt)
3026         compose_state = 0;
3027
3028     /* Sanitize the number pad if not using a PC NumPad */
3029     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3030                      && cfg.funky_type != 2)
3031         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3032         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3033             int nParam = 0;
3034             switch (wParam) {
3035               case VK_INSERT:
3036                 nParam = VK_NUMPAD0;
3037                 break;
3038               case VK_END:
3039                 nParam = VK_NUMPAD1;
3040                 break;
3041               case VK_DOWN:
3042                 nParam = VK_NUMPAD2;
3043                 break;
3044               case VK_NEXT:
3045                 nParam = VK_NUMPAD3;
3046                 break;
3047               case VK_LEFT:
3048                 nParam = VK_NUMPAD4;
3049                 break;
3050               case VK_CLEAR:
3051                 nParam = VK_NUMPAD5;
3052                 break;
3053               case VK_RIGHT:
3054                 nParam = VK_NUMPAD6;
3055                 break;
3056               case VK_HOME:
3057                 nParam = VK_NUMPAD7;
3058                 break;
3059               case VK_UP:
3060                 nParam = VK_NUMPAD8;
3061                 break;
3062               case VK_PRIOR:
3063                 nParam = VK_NUMPAD9;
3064                 break;
3065               case VK_DELETE:
3066                 nParam = VK_DECIMAL;
3067                 break;
3068             }
3069             if (nParam) {
3070                 if (keystate[VK_NUMLOCK] & 1)
3071                     shift_state |= 1;
3072                 wParam = nParam;
3073             }
3074         }
3075     }
3076
3077     /* If a key is pressed and AltGr is not active */
3078     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3079         /* Okay, prepare for most alts then ... */
3080         if (left_alt)
3081             *p++ = '\033';
3082
3083         /* Lets see if it's a pattern we know all about ... */
3084         if (wParam == VK_PRIOR && shift_state == 1) {
3085             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3086             return 0;
3087         }
3088         if (wParam == VK_NEXT && shift_state == 1) {
3089             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3090             return 0;
3091         }
3092         if (wParam == VK_INSERT && shift_state == 1) {
3093             term_do_paste();
3094             return 0;
3095         }
3096         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3097             return -1;
3098         }
3099         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3100             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3101             return -1;
3102         }
3103         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3104             (cfg.resize_action != RESIZE_DISABLED)) {
3105             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3106                 flip_full_screen();
3107             return -1;
3108         }
3109         /* Control-Numlock for app-keypad mode switch */
3110         if (wParam == VK_PAUSE && shift_state == 2) {
3111             app_keypad_keys ^= 1;
3112             return 0;
3113         }
3114
3115         /* Nethack keypad */
3116         if (cfg.nethack_keypad && !left_alt) {
3117             switch (wParam) {
3118               case VK_NUMPAD1:
3119                 *p++ = shift_state ? 'B' : 'b';
3120                 return p - output;
3121               case VK_NUMPAD2:
3122                 *p++ = shift_state ? 'J' : 'j';
3123                 return p - output;
3124               case VK_NUMPAD3:
3125                 *p++ = shift_state ? 'N' : 'n';
3126                 return p - output;
3127               case VK_NUMPAD4:
3128                 *p++ = shift_state ? 'H' : 'h';
3129                 return p - output;
3130               case VK_NUMPAD5:
3131                 *p++ = shift_state ? '.' : '.';
3132                 return p - output;
3133               case VK_NUMPAD6:
3134                 *p++ = shift_state ? 'L' : 'l';
3135                 return p - output;
3136               case VK_NUMPAD7:
3137                 *p++ = shift_state ? 'Y' : 'y';
3138                 return p - output;
3139               case VK_NUMPAD8:
3140                 *p++ = shift_state ? 'K' : 'k';
3141                 return p - output;
3142               case VK_NUMPAD9:
3143                 *p++ = shift_state ? 'U' : 'u';
3144                 return p - output;
3145             }
3146         }
3147
3148         /* Application Keypad */
3149         if (!left_alt) {
3150             int xkey = 0;
3151
3152             if (cfg.funky_type == 3 ||
3153                 (cfg.funky_type <= 1 &&
3154                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3155                   case VK_EXECUTE:
3156                     xkey = 'P';
3157                     break;
3158                   case VK_DIVIDE:
3159                     xkey = 'Q';
3160                     break;
3161                   case VK_MULTIPLY:
3162                     xkey = 'R';
3163                     break;
3164                   case VK_SUBTRACT:
3165                     xkey = 'S';
3166                     break;
3167                 }
3168             if (app_keypad_keys && !cfg.no_applic_k)
3169                 switch (wParam) {
3170                   case VK_NUMPAD0:
3171                     xkey = 'p';
3172                     break;
3173                   case VK_NUMPAD1:
3174                     xkey = 'q';
3175                     break;
3176                   case VK_NUMPAD2:
3177                     xkey = 'r';
3178                     break;
3179                   case VK_NUMPAD3:
3180                     xkey = 's';
3181                     break;
3182                   case VK_NUMPAD4:
3183                     xkey = 't';
3184                     break;
3185                   case VK_NUMPAD5:
3186                     xkey = 'u';
3187                     break;
3188                   case VK_NUMPAD6:
3189                     xkey = 'v';
3190                     break;
3191                   case VK_NUMPAD7:
3192                     xkey = 'w';
3193                     break;
3194                   case VK_NUMPAD8:
3195                     xkey = 'x';
3196                     break;
3197                   case VK_NUMPAD9:
3198                     xkey = 'y';
3199                     break;
3200
3201                   case VK_DECIMAL:
3202                     xkey = 'n';
3203                     break;
3204                   case VK_ADD:
3205                     if (cfg.funky_type == 2) {
3206                         if (shift_state)
3207                             xkey = 'l';
3208                         else
3209                             xkey = 'k';
3210                     } else if (shift_state)
3211                         xkey = 'm';
3212                     else
3213                         xkey = 'l';
3214                     break;
3215
3216                   case VK_DIVIDE:
3217                     if (cfg.funky_type == 2)
3218                         xkey = 'o';
3219                     break;
3220                   case VK_MULTIPLY:
3221                     if (cfg.funky_type == 2)
3222                         xkey = 'j';
3223                     break;
3224                   case VK_SUBTRACT:
3225                     if (cfg.funky_type == 2)
3226                         xkey = 'm';
3227                     break;
3228
3229                   case VK_RETURN:
3230                     if (HIWORD(lParam) & KF_EXTENDED)
3231                         xkey = 'M';
3232                     break;
3233                 }
3234             if (xkey) {
3235                 if (vt52_mode) {
3236                     if (xkey >= 'P' && xkey <= 'S')
3237                         p += sprintf((char *) p, "\x1B%c", xkey);
3238                     else
3239                         p += sprintf((char *) p, "\x1B?%c", xkey);
3240                 } else
3241                     p += sprintf((char *) p, "\x1BO%c", xkey);
3242                 return p - output;
3243             }
3244         }
3245
3246         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3247             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3248             *p++ = 0;
3249             return -2;
3250         }
3251         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3252             *p++ = 0x1B;
3253             *p++ = '[';
3254             *p++ = 'Z';
3255             return p - output;
3256         }
3257         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3258             *p++ = 0;
3259             return p - output;
3260         }
3261         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3262             *p++ = 160;
3263             return p - output;
3264         }
3265         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3266             *p++ = 3;
3267             *p++ = 0;
3268             return -2;
3269         }
3270         if (wParam == VK_PAUSE) {      /* Break/Pause */
3271             *p++ = 26;
3272             *p++ = 0;
3273             return -2;
3274         }
3275         /* Control-2 to Control-8 are special */
3276         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3277             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3278             return p - output;
3279         }
3280         if (shift_state == 2 && wParam == 0xBD) {
3281             *p++ = 0x1F;
3282             return p - output;
3283         }
3284         if (shift_state == 2 && wParam == 0xDF) {
3285             *p++ = 0x1C;
3286             return p - output;
3287         }
3288         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3289             *p++ = '\r';
3290             *p++ = '\n';
3291             return p - output;
3292         }
3293
3294         /*
3295          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3296          * for integer decimal nn.)
3297          *
3298          * We also deal with the weird ones here. Linux VCs replace F1
3299          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3300          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3301          * respectively.
3302          */
3303         code = 0;
3304         switch (wParam) {
3305           case VK_F1:
3306             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3307             break;
3308           case VK_F2:
3309             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3310             break;
3311           case VK_F3:
3312             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3313             break;
3314           case VK_F4:
3315             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3316             break;
3317           case VK_F5:
3318             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3319             break;
3320           case VK_F6:
3321             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3322             break;
3323           case VK_F7:
3324             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3325             break;
3326           case VK_F8:
3327             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3328             break;
3329           case VK_F9:
3330             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3331             break;
3332           case VK_F10:
3333             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3334             break;
3335           case VK_F11:
3336             code = 23;
3337             break;
3338           case VK_F12:
3339             code = 24;
3340             break;
3341           case VK_F13:
3342             code = 25;
3343             break;
3344           case VK_F14:
3345             code = 26;
3346             break;
3347           case VK_F15:
3348             code = 28;
3349             break;
3350           case VK_F16:
3351             code = 29;
3352             break;
3353           case VK_F17:
3354             code = 31;
3355             break;
3356           case VK_F18:
3357             code = 32;
3358             break;
3359           case VK_F19:
3360             code = 33;
3361             break;
3362           case VK_F20:
3363             code = 34;
3364             break;
3365         }
3366         if ((shift_state&2) == 0) switch (wParam) {
3367           case VK_HOME:
3368             code = 1;
3369             break;
3370           case VK_INSERT:
3371             code = 2;
3372             break;
3373           case VK_DELETE:
3374             code = 3;
3375             break;
3376           case VK_END:
3377             code = 4;
3378             break;
3379           case VK_PRIOR:
3380             code = 5;
3381             break;
3382           case VK_NEXT:
3383             code = 6;
3384             break;
3385         }
3386         /* Reorder edit keys to physical order */
3387         if (cfg.funky_type == 3 && code <= 6)
3388             code = "\0\2\1\4\5\3\6"[code];
3389
3390         if (vt52_mode && code > 0 && code <= 6) {
3391             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3392             return p - output;
3393         }
3394
3395         if (cfg.funky_type == 5 &&     /* SCO function keys */
3396             code >= 11 && code <= 34) {
3397             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3398             int index = 0;
3399             switch (wParam) {
3400               case VK_F1: index = 0; break;
3401               case VK_F2: index = 1; break;
3402               case VK_F3: index = 2; break;
3403               case VK_F4: index = 3; break;
3404               case VK_F5: index = 4; break;
3405               case VK_F6: index = 5; break;
3406               case VK_F7: index = 6; break;
3407               case VK_F8: index = 7; break;
3408               case VK_F9: index = 8; break;
3409               case VK_F10: index = 9; break;
3410               case VK_F11: index = 10; break;
3411               case VK_F12: index = 11; break;
3412             }
3413             if (keystate[VK_SHIFT] & 0x80) index += 12;
3414             if (keystate[VK_CONTROL] & 0x80) index += 24;
3415             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3416             return p - output;
3417         }
3418         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3419             code >= 1 && code <= 6) {
3420             char codes[] = "HL.FIG";
3421             if (code == 3) {
3422                 *p++ = '\x7F';
3423             } else {
3424                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3425             }
3426             return p - output;
3427         }
3428         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3429             int offt = 0;
3430             if (code > 15)
3431                 offt++;
3432             if (code > 21)
3433                 offt++;
3434             if (vt52_mode)
3435                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3436             else
3437                 p +=
3438                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3439             return p - output;
3440         }
3441         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3442             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3443             return p - output;
3444         }
3445         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3446             if (vt52_mode)
3447                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3448             else
3449                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3450             return p - output;
3451         }
3452         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3453             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3454             return p - output;
3455         }
3456         if (code) {
3457             p += sprintf((char *) p, "\x1B[%d~", code);
3458             return p - output;
3459         }
3460
3461         /*
3462          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3463          * some reason seems to send VK_CLEAR to Windows...).
3464          */
3465         {
3466             char xkey = 0;
3467             switch (wParam) {
3468               case VK_UP:
3469                 xkey = 'A';
3470                 break;
3471               case VK_DOWN:
3472                 xkey = 'B';
3473                 break;
3474               case VK_RIGHT:
3475                 xkey = 'C';
3476                 break;
3477               case VK_LEFT:
3478                 xkey = 'D';
3479                 break;
3480               case VK_CLEAR:
3481                 xkey = 'G';
3482                 break;
3483             }
3484             if (xkey) {
3485                 if (vt52_mode)
3486                     p += sprintf((char *) p, "\x1B%c", xkey);
3487                 else {
3488                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3489                     /* VT100 & VT102 manuals both state the app cursor keys
3490                      * only work if the app keypad is on.
3491                      */
3492                     if (!app_keypad_keys)
3493                         app_flg = 0;
3494                     /* Useful mapping of Ctrl-arrows */
3495                     if (shift_state == 2)
3496                         app_flg = !app_flg;
3497
3498                     if (app_flg)
3499                         p += sprintf((char *) p, "\x1BO%c", xkey);
3500                     else
3501                         p += sprintf((char *) p, "\x1B[%c", xkey);
3502                 }
3503                 return p - output;
3504             }
3505         }
3506
3507         /*
3508          * Finally, deal with Return ourselves. (Win95 seems to
3509          * foul it up when Alt is pressed, for some reason.)
3510          */
3511         if (wParam == VK_RETURN) {     /* Return */
3512             *p++ = 0x0D;
3513             *p++ = 0;
3514             return -2;
3515         }
3516
3517         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3518             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3519         else
3520             alt_sum = 0;
3521     }
3522
3523     /* Okay we've done everything interesting; let windows deal with 
3524      * the boring stuff */
3525     {
3526         BOOL capsOn=0;
3527
3528         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3529         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3530             capsOn= !left_alt;
3531             keystate[VK_CAPITAL] = 0;
3532         }
3533
3534         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3535 #ifdef SHOW_TOASCII_RESULT
3536         if (r == 1 && !key_down) {
3537             if (alt_sum) {
3538                 if (in_utf || dbcs_screenfont)
3539                     debug((", (U+%04x)", alt_sum));
3540                 else
3541                     debug((", LCH(%d)", alt_sum));
3542             } else {
3543                 debug((", ACH(%d)", keys[0]));
3544             }
3545         } else if (r > 0) {
3546             int r1;
3547             debug((", ASC("));
3548             for (r1 = 0; r1 < r; r1++) {
3549                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3550             }
3551             debug((")"));
3552         }
3553 #endif
3554         if (r > 0) {
3555             WCHAR keybuf;
3556
3557             /*
3558              * Interrupt an ongoing paste. I'm not sure this is
3559              * sensible, but for the moment it's preferable to
3560              * having to faff about buffering things.
3561              */
3562             term_nopaste();
3563
3564             p = output;
3565             for (i = 0; i < r; i++) {
3566                 unsigned char ch = (unsigned char) keys[i];
3567
3568                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3569                     compose_char = ch;
3570                     compose_state++;
3571                     continue;
3572                 }
3573                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3574                     int nc;
3575                     compose_state = 0;
3576
3577                     if ((nc = check_compose(compose_char, ch)) == -1) {
3578                         MessageBeep(MB_ICONHAND);
3579                         return 0;
3580                     }
3581                     keybuf = nc;
3582                     luni_send(&keybuf, 1, 1);
3583                     continue;
3584                 }
3585
3586                 compose_state = 0;
3587
3588                 if (!key_down) {
3589                     if (alt_sum) {
3590                         if (in_utf || dbcs_screenfont) {
3591                             keybuf = alt_sum;
3592                             luni_send(&keybuf, 1, 1);
3593                         } else {
3594                             ch = (char) alt_sum;
3595                             /*
3596                              * We need not bother about stdin
3597                              * backlogs here, because in GUI PuTTY
3598                              * we can't do anything about it
3599                              * anyway; there's no means of asking
3600                              * Windows to hold off on KEYDOWN
3601                              * messages. We _have_ to buffer
3602                              * everything we're sent.
3603                              */
3604                             ldisc_send(&ch, 1, 1);
3605                         }
3606                         alt_sum = 0;
3607                     } else
3608                         lpage_send(kbd_codepage, &ch, 1, 1);
3609                 } else {
3610                     if(capsOn && ch < 0x80) {
3611                         WCHAR cbuf[2];
3612                         cbuf[0] = 27;
3613                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3614                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3615                     } else {
3616                         char cbuf[2];
3617                         cbuf[0] = '\033';
3618                         cbuf[1] = ch;
3619                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3620                     }
3621                 }
3622                 show_mouseptr(0);
3623             }
3624
3625             /* This is so the ALT-Numpad and dead keys work correctly. */
3626             keys[0] = 0;
3627
3628             return p - output;
3629         }
3630         /* If we're definitly not building up an ALT-54321 then clear it */
3631         if (!left_alt)
3632             keys[0] = 0;
3633         /* If we will be using alt_sum fix the 256s */
3634         else if (keys[0] && (in_utf || dbcs_screenfont))
3635             keys[0] = 10;
3636     }
3637
3638     /*
3639      * ALT alone may or may not want to bring up the System menu.
3640      * If it's not meant to, we return 0 on presses or releases of
3641      * ALT, to show that we've swallowed the keystroke. Otherwise
3642      * we return -1, which means Windows will give the keystroke
3643      * its default handling (i.e. bring up the System menu).
3644      */
3645     if (wParam == VK_MENU && !cfg.alt_only)
3646         return 0;
3647
3648     return -1;
3649 }
3650
3651 void set_title(char *title)
3652 {
3653     sfree(window_name);
3654     window_name = smalloc(1 + strlen(title));
3655     strcpy(window_name, title);
3656     if (cfg.win_name_always || !IsIconic(hwnd))
3657         SetWindowText(hwnd, title);
3658 }
3659
3660 void set_icon(char *title)
3661 {
3662     sfree(icon_name);
3663     icon_name = smalloc(1 + strlen(title));
3664     strcpy(icon_name, title);
3665     if (!cfg.win_name_always && IsIconic(hwnd))
3666         SetWindowText(hwnd, title);
3667 }
3668
3669 void set_sbar(int total, int start, int page)
3670 {
3671     SCROLLINFO si;
3672
3673     if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3674         return;
3675
3676     si.cbSize = sizeof(si);
3677     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3678     si.nMin = 0;
3679     si.nMax = total - 1;
3680     si.nPage = page;
3681     si.nPos = start;
3682     if (hwnd)
3683         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3684 }
3685
3686 Context get_ctx(void)
3687 {
3688     HDC hdc;
3689     if (hwnd) {
3690         hdc = GetDC(hwnd);
3691         if (hdc && pal)
3692             SelectPalette(hdc, pal, FALSE);
3693         return hdc;
3694     } else
3695         return NULL;
3696 }
3697
3698 void free_ctx(Context ctx)
3699 {
3700     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3701     ReleaseDC(hwnd, ctx);
3702 }
3703
3704 static void real_palette_set(int n, int r, int g, int b)
3705 {
3706     if (pal) {
3707         logpal->palPalEntry[n].peRed = r;
3708         logpal->palPalEntry[n].peGreen = g;
3709         logpal->palPalEntry[n].peBlue = b;
3710         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3711         colours[n] = PALETTERGB(r, g, b);
3712         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3713     } else
3714         colours[n] = RGB(r, g, b);
3715 }
3716
3717 void palette_set(int n, int r, int g, int b)
3718 {
3719     static const int first[21] = {
3720         0, 2, 4, 6, 8, 10, 12, 14,
3721         1, 3, 5, 7, 9, 11, 13, 15,
3722         16, 17, 18, 20, 22
3723     };
3724     real_palette_set(first[n], r, g, b);
3725     if (first[n] >= 18)
3726         real_palette_set(first[n] + 1, r, g, b);
3727     if (pal) {
3728         HDC hdc = get_ctx();
3729         UnrealizeObject(pal);
3730         RealizePalette(hdc);
3731         free_ctx(hdc);
3732     }
3733 }
3734
3735 void palette_reset(void)
3736 {
3737     int i;
3738
3739     for (i = 0; i < NCOLOURS; i++) {
3740         if (pal) {
3741             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3742             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3743             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3744             logpal->palPalEntry[i].peFlags = 0;
3745             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3746                                     defpal[i].rgbtGreen,
3747                                     defpal[i].rgbtBlue);
3748         } else
3749             colours[i] = RGB(defpal[i].rgbtRed,
3750                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3751     }
3752
3753     if (pal) {
3754         HDC hdc;
3755         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3756         hdc = get_ctx();
3757         RealizePalette(hdc);
3758         free_ctx(hdc);
3759     }
3760 }
3761
3762 void write_aclip(char *data, int len, int must_deselect)
3763 {
3764     HGLOBAL clipdata;
3765     void *lock;
3766
3767     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3768     if (!clipdata)
3769         return;
3770     lock = GlobalLock(clipdata);
3771     if (!lock)
3772         return;
3773     memcpy(lock, data, len);
3774     ((unsigned char *) lock)[len] = 0;
3775     GlobalUnlock(clipdata);
3776
3777     if (!must_deselect)
3778         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3779
3780     if (OpenClipboard(hwnd)) {
3781         EmptyClipboard();
3782         SetClipboardData(CF_TEXT, clipdata);
3783         CloseClipboard();
3784     } else
3785         GlobalFree(clipdata);
3786
3787     if (!must_deselect)
3788         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3789 }
3790
3791 /*
3792  * Note: unlike write_aclip() this will not append a nul.
3793  */
3794 void write_clip(wchar_t * data, int len, int must_deselect)
3795 {
3796     HGLOBAL clipdata, clipdata2, clipdata3;
3797     int len2;
3798     void *lock, *lock2, *lock3;
3799
3800     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3801
3802     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3803                            len * sizeof(wchar_t));
3804     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3805
3806     if (!clipdata || !clipdata2) {
3807         if (clipdata)
3808             GlobalFree(clipdata);
3809         if (clipdata2)
3810             GlobalFree(clipdata2);
3811         return;
3812     }
3813     if (!(lock = GlobalLock(clipdata)))
3814         return;
3815     if (!(lock2 = GlobalLock(clipdata2)))
3816         return;
3817
3818     memcpy(lock, data, len * sizeof(wchar_t));
3819     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3820
3821     if (cfg.rtf_paste) {
3822         wchar_t unitab[256];
3823         char *rtf = NULL;
3824         unsigned char *tdata = (unsigned char *)lock2;
3825         wchar_t *udata = (wchar_t *)lock;
3826         int rtflen = 0, uindex = 0, tindex = 0;
3827         int rtfsize = 0;
3828         int multilen, blen, alen, totallen, i;
3829         char before[16], after[4];
3830
3831         get_unitab(CP_ACP, unitab, 0);
3832
3833         rtfsize = 100 + strlen(cfg.font);
3834         rtf = smalloc(rtfsize);
3835         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3836                 GetACP(), cfg.font);
3837         rtflen = strlen(rtf);
3838
3839         /*
3840          * We want to construct a piece of RTF that specifies the
3841          * same Unicode text. To do this we will read back in
3842          * parallel from the Unicode data in `udata' and the
3843          * non-Unicode data in `tdata'. For each character in
3844          * `tdata' which becomes the right thing in `udata' when
3845          * looked up in `unitab', we just copy straight over from
3846          * tdata. For each one that doesn't, we must WCToMB it
3847          * individually and produce a \u escape sequence.
3848          * 
3849          * It would probably be more robust to just bite the bullet
3850          * and WCToMB each individual Unicode character one by one,
3851          * then MBToWC each one back to see if it was an accurate
3852          * translation; but that strikes me as a horrifying number
3853          * of Windows API calls so I want to see if this faster way
3854          * will work. If it screws up badly we can always revert to
3855          * the simple and slow way.
3856          */
3857         while (tindex < len2 && uindex < len &&
3858                tdata[tindex] && udata[uindex]) {
3859             if (tindex + 1 < len2 &&
3860                 tdata[tindex] == '\r' &&
3861                 tdata[tindex+1] == '\n') {
3862                 tindex++;
3863                 uindex++;
3864             }
3865             if (unitab[tdata[tindex]] == udata[uindex]) {
3866                 multilen = 1;
3867                 before[0] = '\0';
3868                 after[0] = '\0';
3869                 blen = alen = 0;
3870             } else {
3871                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3872                                                NULL, 0, NULL, NULL);
3873                 if (multilen != 1) {
3874                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3875                                    udata[uindex]);
3876                     alen = 1; strcpy(after, "}");
3877                 } else {
3878                     blen = sprintf(before, "\\u%d", udata[uindex]);
3879                     alen = 0; after[0] = '\0';
3880                 }
3881             }
3882             assert(tindex + multilen <= len2);
3883             totallen = blen + alen;
3884             for (i = 0; i < multilen; i++) {
3885                 if (tdata[tindex+i] == '\\' ||
3886                     tdata[tindex+i] == '{' ||
3887                     tdata[tindex+i] == '}')
3888                     totallen += 2;
3889                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3890                     totallen += 6;     /* \par\r\n */
3891                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3892                     totallen += 4;
3893                 else
3894                     totallen++;
3895             }
3896
3897             if (rtfsize < rtflen + totallen + 3) {
3898                 rtfsize = rtflen + totallen + 512;
3899                 rtf = srealloc(rtf, rtfsize);
3900             }
3901
3902             strcpy(rtf + rtflen, before); rtflen += blen;
3903             for (i = 0; i < multilen; i++) {
3904                 if (tdata[tindex+i] == '\\' ||
3905                     tdata[tindex+i] == '{' ||
3906                     tdata[tindex+i] == '}') {
3907                     rtf[rtflen++] = '\\';
3908                     rtf[rtflen++] = tdata[tindex+i];
3909                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3910                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3911                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3912                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3913                 } else {
3914                     rtf[rtflen++] = tdata[tindex+i];
3915                 }
3916             }
3917             strcpy(rtf + rtflen, after); rtflen += alen;
3918
3919             tindex += multilen;
3920             uindex++;
3921         }
3922
3923         strcpy(rtf + rtflen, "}");
3924         rtflen += 2;
3925
3926         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3927         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3928             strcpy(lock3, rtf);
3929             GlobalUnlock(clipdata3);
3930         }
3931         sfree(rtf);
3932     } else
3933         clipdata3 = NULL;
3934
3935     GlobalUnlock(clipdata);
3936     GlobalUnlock(clipdata2);
3937
3938     if (!must_deselect)
3939         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3940
3941     if (OpenClipboard(hwnd)) {
3942         EmptyClipboard();
3943         SetClipboardData(CF_UNICODETEXT, clipdata);
3944         SetClipboardData(CF_TEXT, clipdata2);
3945         if (clipdata3)
3946             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3947         CloseClipboard();
3948     } else {
3949         GlobalFree(clipdata);
3950         GlobalFree(clipdata2);
3951     }
3952
3953     if (!must_deselect)
3954         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3955 }
3956
3957 void get_clip(wchar_t ** p, int *len)
3958 {
3959     static HGLOBAL clipdata = NULL;
3960     static wchar_t *converted = 0;
3961     wchar_t *p2;
3962
3963     if (converted) {
3964         sfree(converted);
3965         converted = 0;
3966     }
3967     if (!p) {
3968         if (clipdata)
3969             GlobalUnlock(clipdata);
3970         clipdata = NULL;
3971         return;
3972     } else if (OpenClipboard(NULL)) {
3973         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3974             CloseClipboard();
3975             *p = GlobalLock(clipdata);
3976             if (*p) {
3977                 for (p2 = *p; *p2; p2++);
3978                 *len = p2 - *p;
3979                 return;
3980             }
3981         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3982             char *s;
3983             int i;
3984             CloseClipboard();
3985             s = GlobalLock(clipdata);
3986             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3987             *p = converted = smalloc(i * sizeof(wchar_t));
3988             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3989             *len = i - 1;
3990             return;
3991         } else
3992             CloseClipboard();
3993     }
3994
3995     *p = NULL;
3996     *len = 0;
3997 }
3998
3999 #if 0
4000 /*
4001  * Move `lines' lines from position `from' to position `to' in the
4002  * window.
4003  */
4004 void optimised_move(int to, int from, int lines)
4005 {
4006     RECT r;
4007     int min, max;
4008
4009     min = (to < from ? to : from);
4010     max = to + from - min;
4011
4012     r.left = offset_width;
4013     r.right = offset_width + cols * font_width;
4014     r.top = offset_height + min * font_height;
4015     r.bottom = offset_height + (max + lines) * font_height;
4016     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4017 }
4018 #endif
4019
4020 /*
4021  * Print a message box and perform a fatal exit.
4022  */
4023 void fatalbox(char *fmt, ...)
4024 {
4025     va_list ap;
4026     char stuff[200];
4027
4028     va_start(ap, fmt);
4029     vsprintf(stuff, fmt, ap);
4030     va_end(ap);
4031     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4032     exit(1);
4033 }
4034
4035 /*
4036  * Manage window caption / taskbar flashing, if enabled.
4037  * 0 = stop, 1 = maintain, 2 = start
4038  */
4039 static void flash_window(int mode)
4040 {
4041     static long last_flash = 0;
4042     static int flashing = 0;
4043     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4044         /* stop */
4045         if (flashing) {
4046             FlashWindow(hwnd, FALSE);
4047             flashing = 0;
4048         }
4049
4050     } else if (mode == 2) {
4051         /* start */
4052         if (!flashing) {
4053             last_flash = GetTickCount();
4054             flashing = 1;
4055             FlashWindow(hwnd, TRUE);
4056         }
4057
4058     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4059         /* maintain */
4060         if (flashing) {
4061             long now = GetTickCount();
4062             long fdiff = now - last_flash;
4063             if (fdiff < 0 || fdiff > 450) {
4064                 last_flash = now;
4065                 FlashWindow(hwnd, TRUE);        /* toggle */
4066             }
4067         }
4068     }
4069 }
4070
4071 /*
4072  * Beep.
4073  */
4074 void beep(int mode)
4075 {
4076     if (mode == BELL_DEFAULT) {
4077         /*
4078          * For MessageBeep style bells, we want to be careful of
4079          * timing, because they don't have the nice property of
4080          * PlaySound bells that each one cancels the previous
4081          * active one. So we limit the rate to one per 50ms or so.
4082          */
4083         static long lastbeep = 0;
4084         long beepdiff;
4085
4086         beepdiff = GetTickCount() - lastbeep;
4087         if (beepdiff >= 0 && beepdiff < 50)
4088             return;
4089         MessageBeep(MB_OK);
4090         /*
4091          * The above MessageBeep call takes time, so we record the
4092          * time _after_ it finishes rather than before it starts.
4093          */
4094         lastbeep = GetTickCount();
4095     } else if (mode == BELL_WAVEFILE) {
4096         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4097             char buf[sizeof(cfg.bell_wavefile) + 80];
4098             sprintf(buf, "Unable to play sound file\n%s\n"
4099                     "Using default sound instead", cfg.bell_wavefile);
4100             MessageBox(hwnd, buf, "PuTTY Sound Error",
4101                        MB_OK | MB_ICONEXCLAMATION);
4102             cfg.beep = BELL_DEFAULT;
4103         }
4104     }
4105     /* Otherwise, either visual bell or disabled; do nothing here */
4106     if (!has_focus) {
4107         flash_window(2);               /* start */
4108     }
4109 }
4110
4111 /*
4112  * Minimise or restore the window in response to a server-side
4113  * request.
4114  */
4115 void set_iconic(int iconic)
4116 {
4117     if (IsIconic(hwnd)) {
4118         if (!iconic)
4119             ShowWindow(hwnd, SW_RESTORE);
4120     } else {
4121         if (iconic)
4122             ShowWindow(hwnd, SW_MINIMIZE);
4123     }
4124 }
4125
4126 /*
4127  * Move the window in response to a server-side request.
4128  */
4129 void move_window(int x, int y)
4130 {
4131     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4132 }
4133
4134 /*
4135  * Move the window to the top or bottom of the z-order in response
4136  * to a server-side request.
4137  */
4138 void set_zorder(int top)
4139 {
4140     if (cfg.alwaysontop)
4141         return;                        /* ignore */
4142     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4143                  SWP_NOMOVE | SWP_NOSIZE);
4144 }
4145
4146 /*
4147  * Refresh the window in response to a server-side request.
4148  */
4149 void refresh_window(void)
4150 {
4151     InvalidateRect(hwnd, NULL, TRUE);
4152 }
4153
4154 /*
4155  * Maximise or restore the window in response to a server-side
4156  * request.
4157  */
4158 void set_zoomed(int zoomed)
4159 {
4160     if (IsZoomed(hwnd)) {
4161         if (!zoomed)
4162             ShowWindow(hwnd, SW_RESTORE);
4163     } else {
4164         if (zoomed)
4165             ShowWindow(hwnd, SW_MAXIMIZE);
4166     }
4167 }
4168
4169 /*
4170  * Report whether the window is iconic, for terminal reports.
4171  */
4172 int is_iconic(void)
4173 {
4174     return IsIconic(hwnd);
4175 }
4176
4177 /*
4178  * Report the window's position, for terminal reports.
4179  */
4180 void get_window_pos(int *x, int *y)
4181 {
4182     RECT r;
4183     GetWindowRect(hwnd, &r);
4184     *x = r.left;
4185     *y = r.top;
4186 }
4187
4188 /*
4189  * Report the window's pixel size, for terminal reports.
4190  */
4191 void get_window_pixels(int *x, int *y)
4192 {
4193     RECT r;
4194     GetWindowRect(hwnd, &r);
4195     *x = r.right - r.left;
4196     *y = r.bottom - r.top;
4197 }
4198
4199 /*
4200  * Return the window or icon title.
4201  */
4202 char *get_window_title(int icon)
4203 {
4204     return icon ? icon_name : window_name;
4205 }
4206
4207 /*
4208  * See if we're in full-screen mode.
4209  */
4210 int is_full_screen()
4211 {
4212     if (!IsZoomed(hwnd))
4213         return FALSE;
4214     if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4215         return FALSE;
4216     return TRUE;
4217 }
4218
4219 /*
4220  * Go full-screen. This should only be called when we are already
4221  * maximised.
4222  */
4223 void make_full_screen()
4224 {
4225     DWORD style;
4226     int x, y, w, h;
4227
4228     assert(IsZoomed(hwnd));
4229
4230     /* Remove the window furniture. */
4231     style = GetWindowLong(hwnd, GWL_STYLE);
4232     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4233     if (cfg.scrollbar_in_fullscreen)
4234         style |= WS_VSCROLL;
4235     else
4236         style &= ~WS_VSCROLL;
4237     SetWindowLong(hwnd, GWL_STYLE, style);
4238
4239     /* Resize ourselves to exactly cover the nearest monitor. */
4240 #ifdef MONITOR_DEFAULTTONEAREST
4241     {
4242         HMONITOR mon;
4243         MONITORINFO mi;
4244         mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4245         mi.cbSize = sizeof(mi);
4246         GetMonitorInfo(mon, &mi);
4247         x = mi.rcMonitor.left;
4248         y = mi.rcMonitor.top;
4249         w = mi.rcMonitor.right;
4250         h = mi.rcMonitor.bottom;
4251     }
4252 #else
4253     x = y = 0;
4254     w = GetSystemMetrics(SM_CXSCREEN);
4255     h = GetSystemMetrics(SM_CYSCREEN);
4256 #endif
4257     SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4258
4259     /* Tick the menu item in the System menu. */
4260     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4261                   MF_CHECKED);
4262 }
4263
4264 /*
4265  * Clear the full-screen attributes.
4266  */
4267 void clear_full_screen()
4268 {
4269     DWORD oldstyle, style;
4270
4271     /* Reinstate the window furniture. */
4272     style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4273     style |= WS_CAPTION | WS_BORDER | WS_THICKFRAME;
4274     if (cfg.scrollbar)
4275         style |= WS_VSCROLL;
4276     else
4277         style &= ~WS_VSCROLL;
4278     if (style != oldstyle) {
4279         SetWindowLong(hwnd, GWL_STYLE, style);
4280         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4281                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4282                      SWP_FRAMECHANGED);
4283     }
4284
4285     /* Untick the menu item in the System menu. */
4286     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4287                   MF_UNCHECKED);
4288 }
4289
4290 /*
4291  * Toggle full-screen mode.
4292  */
4293 void flip_full_screen()
4294 {
4295     if (is_full_screen()) {
4296         ShowWindow(hwnd, SW_RESTORE);
4297     } else if (IsZoomed(hwnd)) {
4298         make_full_screen();
4299     } else {
4300         SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4301         ShowWindow(hwnd, SW_MAXIMIZE);
4302     }
4303 }