]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - windows/window.c
Turn the Windows PuTTY main window into a Unicode window.
[PuTTY.git] / windows / window.c
1 /*
2  * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal
3  * emulator and backend in a window.
4  */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <ctype.h>
9 #include <time.h>
10 #include <limits.h>
11 #include <assert.h>
12
13 #ifndef NO_MULTIMON
14 #define COMPILE_MULTIMON_STUBS
15 #endif
16
17 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
18 #include "putty.h"
19 #include "terminal.h"
20 #include "storage.h"
21 #include "win_res.h"
22
23 #ifndef NO_MULTIMON
24 #include <multimon.h>
25 #endif
26
27 #include <imm.h>
28 #include <commctrl.h>
29 #include <richedit.h>
30 #include <mmsystem.h>
31
32 /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
33  * wParam are used by Windows, and should be masked off, so we shouldn't
34  * attempt to store information in them. Hence all these identifiers have
35  * the low 4 bits clear. Also, identifiers should < 0xF000. */
36
37 #define IDM_SHOWLOG   0x0010
38 #define IDM_NEWSESS   0x0020
39 #define IDM_DUPSESS   0x0030
40 #define IDM_RESTART   0x0040
41 #define IDM_RECONF    0x0050
42 #define IDM_CLRSB     0x0060
43 #define IDM_RESET     0x0070
44 #define IDM_HELP      0x0140
45 #define IDM_ABOUT     0x0150
46 #define IDM_SAVEDSESS 0x0160
47 #define IDM_COPYALL   0x0170
48 #define IDM_FULLSCREEN  0x0180
49 #define IDM_PASTE     0x0190
50 #define IDM_SPECIALSEP 0x0200
51
52 #define IDM_SPECIAL_MIN 0x0400
53 #define IDM_SPECIAL_MAX 0x0800
54
55 #define IDM_SAVED_MIN 0x1000
56 #define IDM_SAVED_MAX 0x5000
57 #define MENU_SAVED_STEP 16
58 /* Maximum number of sessions on saved-session submenu */
59 #define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP)
60
61 #define WM_IGNORE_CLIP (WM_APP + 2)
62 #define WM_FULLSCR_ON_MAX (WM_APP + 3)
63 #define WM_AGENT_CALLBACK (WM_APP + 4)
64 #define WM_GOT_CLIPDATA (WM_APP + 6)
65
66 /* Needed for Chinese support and apparently not always defined. */
67 #ifndef VK_PROCESSKEY
68 #define VK_PROCESSKEY 0xE5
69 #endif
70
71 /* Mouse wheel support. */
72 #ifndef WM_MOUSEWHEEL
73 #define WM_MOUSEWHEEL 0x020A           /* not defined in earlier SDKs */
74 #endif
75 #ifndef WHEEL_DELTA
76 #define WHEEL_DELTA 120
77 #endif
78
79 static Mouse_Button translate_button(Mouse_Button button);
80 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
81 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
82                         unsigned char *output);
83 static void conftopalette(void);
84 static void systopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL);
90 static void update_savedsess_menu(void);
91 static void init_winfuncs(void);
92
93 static int is_full_screen(void);
94 static void make_full_screen(void);
95 static void clear_full_screen(void);
96 static void flip_full_screen(void);
97 static int process_clipdata(HGLOBAL clipdata, int unicode);
98
99 /* Window layout information */
100 static void reset_window(int);
101 static int extra_width, extra_height;
102 static int font_width, font_height, font_dualwidth, font_varpitch;
103 static int offset_width, offset_height;
104 static int was_zoomed = 0;
105 static int prev_rows, prev_cols;
106   
107 static void flash_window(int mode);
108 static void sys_cursor_update(void);
109 static int get_fullscreen_rect(RECT * ss);
110
111 static int caret_x = -1, caret_y = -1;
112
113 static int kbd_codepage;
114
115 static void *ldisc;
116 static Backend *back;
117 static void *backhandle;
118
119 static struct unicode_data ucsdata;
120 static int session_closed;
121 static int reconfiguring = FALSE;
122
123 static const struct telnet_special *specials = NULL;
124 static HMENU specials_menu = NULL;
125 static int n_specials = 0;
126
127 static wchar_t *clipboard_contents;
128 static size_t clipboard_length;
129
130 #define TIMING_TIMER_ID 1234
131 static long timing_next_time;
132
133 static struct {
134     HMENU menu;
135 } popup_menus[2];
136 enum { SYSMENU, CTXMENU };
137 static HMENU savedsess_menu;
138
139 struct wm_netevent_params {
140     /* Used to pass data to wm_netevent_callback */
141     WPARAM wParam;
142     LPARAM lParam;
143 };
144
145 Conf *conf;                            /* exported to windlg.c */
146
147 static void conf_cache_data(void);
148 int cursor_type;
149 int vtmode;
150
151 static struct sesslist sesslist;       /* for saved-session menu */
152
153 struct agent_callback {
154     void (*callback)(void *, void *, int);
155     void *callback_ctx;
156     void *data;
157     int len;
158 };
159
160 #define FONT_NORMAL 0
161 #define FONT_BOLD 1
162 #define FONT_UNDERLINE 2
163 #define FONT_BOLDUND 3
164 #define FONT_WIDE       0x04
165 #define FONT_HIGH       0x08
166 #define FONT_NARROW     0x10
167
168 #define FONT_OEM        0x20
169 #define FONT_OEMBOLD    0x21
170 #define FONT_OEMUND     0x22
171 #define FONT_OEMBOLDUND 0x23
172
173 #define FONT_MAXNO      0x40
174 #define FONT_SHIFT      5
175 static HFONT fonts[FONT_MAXNO];
176 static LOGFONT lfont;
177 static int fontflag[FONT_MAXNO];
178 static enum {
179     BOLD_NONE, BOLD_SHADOW, BOLD_FONT
180 } bold_font_mode;
181 static int bold_colours;
182 static enum {
183     UND_LINE, UND_FONT
184 } und_mode;
185 static int descent;
186
187 #define NCFGCOLOURS 22
188 #define NEXTCOLOURS 240
189 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
190 static COLORREF colours[NALLCOLOURS];
191 static HPALETTE pal;
192 static LPLOGPALETTE logpal;
193 static RGBTRIPLE defpal[NALLCOLOURS];
194
195 static HBITMAP caretbm;
196
197 static int dbltime, lasttime, lastact;
198 static Mouse_Button lastbtn;
199
200 /* this allows xterm-style mouse handling. */
201 static int send_raw_mouse = 0;
202 static int wheel_accumulator = 0;
203
204 static int busy_status = BUSY_NOT;
205
206 static char *window_name, *icon_name;
207
208 static int compose_state = 0;
209
210 static UINT wm_mousewheel = WM_MOUSEWHEEL;
211
212 #define IS_HIGH_VARSEL(wch1, wch2) \
213     ((wch1) == 0xDB40 && ((wch2) >= 0xDD00 && (wch2) <= 0xDDEF))
214 #define IS_LOW_VARSEL(wch) \
215     (((wch) >= 0x180B && (wch) <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR */ \
216      ((wch) >= 0xFE00 && (wch) <= 0xFE0F)) /* VARIATION SELECTOR 1-16 */
217
218 const int share_can_be_downstream = TRUE;
219 const int share_can_be_upstream = TRUE;
220
221 /* Dummy routine, only required in plink. */
222 void ldisc_update(void *frontend, int echo, int edit)
223 {
224 }
225
226 char *get_ttymode(void *frontend, const char *mode)
227 {
228     return term_get_ttymode(term, mode);
229 }
230
231 static void start_backend(void)
232 {
233     const char *error;
234     char msg[1024], *title;
235     char *realhost;
236     int i;
237
238     /*
239      * Select protocol. This is farmed out into a table in a
240      * separate file to enable an ssh-free variant.
241      */
242     back = backend_from_proto(conf_get_int(conf, CONF_protocol));
243     if (back == NULL) {
244         char *str = dupprintf("%s Internal Error", appname);
245         MessageBox(NULL, "Unsupported protocol number found",
246                    str, MB_OK | MB_ICONEXCLAMATION);
247         sfree(str);
248         cleanup_exit(1);
249     }
250
251     error = back->init(NULL, &backhandle, conf,
252                        conf_get_str(conf, CONF_host),
253                        conf_get_int(conf, CONF_port),
254                        &realhost,
255                        conf_get_int(conf, CONF_tcp_nodelay),
256                        conf_get_int(conf, CONF_tcp_keepalives));
257     back->provide_logctx(backhandle, logctx);
258     if (error) {
259         char *str = dupprintf("%s Error", appname);
260         sprintf(msg, "Unable to open connection to\n"
261                 "%.800s\n" "%s", conf_dest(conf), error);
262         MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK);
263         sfree(str);
264         exit(0);
265     }
266     window_name = icon_name = NULL;
267     title = conf_get_str(conf, CONF_wintitle);
268     if (!*title) {
269         sprintf(msg, "%s - %s", realhost, appname);
270         title = msg;
271     }
272     sfree(realhost);
273     set_title(NULL, title);
274     set_icon(NULL, title);
275
276     /*
277      * Connect the terminal to the backend for resize purposes.
278      */
279     term_provide_resize_fn(term, back->size, backhandle);
280
281     /*
282      * Set up a line discipline.
283      */
284     ldisc = ldisc_create(conf, term, back, backhandle, NULL);
285
286     /*
287      * Destroy the Restart Session menu item. (This will return
288      * failure if it's already absent, as it will be the very first
289      * time we call this function. We ignore that, because as long
290      * as the menu item ends up not being there, we don't care
291      * whether it was us who removed it or not!)
292      */
293     for (i = 0; i < lenof(popup_menus); i++) {
294         DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
295     }
296
297     session_closed = FALSE;
298 }
299
300 static void close_session(void *ignored_context)
301 {
302     char morestuff[100];
303     int i;
304
305     session_closed = TRUE;
306     sprintf(morestuff, "%.70s (inactive)", appname);
307     set_icon(NULL, morestuff);
308     set_title(NULL, morestuff);
309
310     if (ldisc) {
311         ldisc_free(ldisc);
312         ldisc = NULL;
313     }
314     if (back) {
315         back->free(backhandle);
316         backhandle = NULL;
317         back = NULL;
318         term_provide_resize_fn(term, NULL, NULL);
319         update_specials_menu(NULL);
320     }
321
322     /*
323      * Show the Restart Session menu item. Do a precautionary
324      * delete first to ensure we never end up with more than one.
325      */
326     for (i = 0; i < lenof(popup_menus); i++) {
327         DeleteMenu(popup_menus[i].menu, IDM_RESTART, MF_BYCOMMAND);
328         InsertMenu(popup_menus[i].menu, IDM_DUPSESS, MF_BYCOMMAND | MF_ENABLED,
329                    IDM_RESTART, "&Restart Session");
330     }
331 }
332
333 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
334 {
335     MSG msg;
336     HRESULT hr;
337     int guess_width, guess_height;
338
339     hinst = inst;
340     hwnd = NULL;
341     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
342
343     sk_init();
344
345     InitCommonControls();
346
347     /* Ensure a Maximize setting in Explorer doesn't maximise the
348      * config box. */
349     defuse_showwindow();
350
351     if (!init_winver())
352     {
353         char *str = dupprintf("%s Fatal Error", appname);
354         MessageBox(NULL, "Windows refuses to report a version",
355                    str, MB_OK | MB_ICONEXCLAMATION);
356         sfree(str);
357         return 1;
358     }
359
360     /*
361      * If we're running a version of Windows that doesn't support
362      * WM_MOUSEWHEEL, find out what message number we should be
363      * using instead.
364      */
365     if (osVersion.dwMajorVersion < 4 ||
366         (osVersion.dwMajorVersion == 4 && 
367          osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
368         wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
369
370     init_help();
371
372     init_winfuncs();
373
374     conf = conf_new();
375
376     /*
377      * Initialize COM.
378      */
379     hr = CoInitialize(NULL);
380     if (hr != S_OK && hr != S_FALSE) {
381         char *str = dupprintf("%s Fatal Error", appname);
382         MessageBox(NULL, "Failed to initialize COM subsystem",
383                    str, MB_OK | MB_ICONEXCLAMATION);
384         sfree(str);
385         return 1;
386     }
387
388     /*
389      * Process the command line.
390      */
391     {
392         char *p;
393         int got_host = 0;
394         /* By default, we bring up the config dialog, rather than launching
395          * a session. This gets set to TRUE if something happens to change
396          * that (e.g., a hostname is specified on the command-line). */
397         int allow_launch = FALSE;
398
399         default_protocol = be_default_protocol;
400         /* Find the appropriate default port. */
401         {
402             Backend *b = backend_from_proto(default_protocol);
403             default_port = 0; /* illegal */
404             if (b)
405                 default_port = b->default_port;
406         }
407         conf_set_int(conf, CONF_logtype, LGTYP_NONE);
408
409         do_defaults(NULL, conf);
410
411         p = cmdline;
412
413         /*
414          * Process a couple of command-line options which are more
415          * easily dealt with before the line is broken up into words.
416          * These are the old-fashioned but convenient @sessionname and
417          * the internal-use-only &sharedmemoryhandle, neither of which
418          * are combined with anything else.
419          */
420         while (*p && isspace(*p))
421             p++;
422         if (*p == '@') {
423             /*
424              * An initial @ means that the whole of the rest of the
425              * command line should be treated as the name of a saved
426              * session, with _no quoting or escaping_. This makes it a
427              * very convenient means of automated saved-session
428              * launching, via IDM_SAVEDSESS or Windows 7 jump lists.
429              */
430             int i = strlen(p);
431             while (i > 1 && isspace(p[i - 1]))
432                 i--;
433             p[i] = '\0';
434             do_defaults(p + 1, conf);
435             if (!conf_launchable(conf) && !do_config()) {
436                 cleanup_exit(0);
437             }
438             allow_launch = TRUE;    /* allow it to be launched directly */
439         } else if (*p == '&') {
440             /*
441              * An initial & means we've been given a command line
442              * containing the hex value of a HANDLE for a file
443              * mapping object, which we must then interpret as a
444              * serialised Conf.
445              */
446             HANDLE filemap;
447             void *cp;
448             unsigned cpsize;
449             if (sscanf(p + 1, "%p:%u", &filemap, &cpsize) == 2 &&
450                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
451                                     0, 0, cpsize)) != NULL) {
452                 conf_deserialise(conf, cp, cpsize);
453                 UnmapViewOfFile(cp);
454                 CloseHandle(filemap);
455             } else if (!do_config()) {
456                 cleanup_exit(0);
457             }
458             allow_launch = TRUE;
459         } else {
460             /*
461              * Otherwise, break up the command line and deal with
462              * it sensibly.
463              */
464             int argc, i;
465             char **argv;
466             
467             split_into_argv(cmdline, &argc, &argv, NULL);
468
469             for (i = 0; i < argc; i++) {
470                 char *p = argv[i];
471                 int ret;
472
473                 ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
474                                             1, conf);
475                 if (ret == -2) {
476                     cmdline_error("option \"%s\" requires an argument", p);
477                 } else if (ret == 2) {
478                     i++;               /* skip next argument */
479                 } else if (ret == 1) {
480                     continue;          /* nothing further needs doing */
481                 } else if (!strcmp(p, "-cleanup") ||
482                            !strcmp(p, "-cleanup-during-uninstall")) {
483                     /*
484                      * `putty -cleanup'. Remove all registry
485                      * entries associated with PuTTY, and also find
486                      * and delete the random seed file.
487                      */
488                     char *s1, *s2;
489                     /* Are we being invoked from an uninstaller? */
490                     if (!strcmp(p, "-cleanup-during-uninstall")) {
491                         s1 = dupprintf("Remove saved sessions and random seed file?\n"
492                                        "\n"
493                                        "If you hit Yes, ALL Registry entries associated\n"
494                                        "with %s will be removed, as well as the\n"
495                                        "random seed file. THIS PROCESS WILL\n"
496                                        "DESTROY YOUR SAVED SESSIONS.\n"
497                                        "(This only affects the currently logged-in user.)\n"
498                                        "\n"
499                                        "If you hit No, uninstallation will proceed, but\n"
500                                        "saved sessions etc will be left on the machine.",
501                                        appname);
502                         s2 = dupprintf("%s Uninstallation", appname);
503                     } else {
504                         s1 = dupprintf("This procedure will remove ALL Registry entries\n"
505                                        "associated with %s, and will also remove\n"
506                                        "the random seed file. (This only affects the\n"
507                                        "currently logged-in user.)\n"
508                                        "\n"
509                                        "THIS PROCESS WILL DESTROY YOUR SAVED SESSIONS.\n"
510                                        "Are you really sure you want to continue?",
511                                        appname);
512                         s2 = dupprintf("%s Warning", appname);
513                     }
514                     if (message_box(s1, s2,
515                                     MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2,
516                                     HELPCTXID(option_cleanup)) == IDYES) {
517                         cleanup_all();
518                     }
519                     sfree(s1);
520                     sfree(s2);
521                     exit(0);
522                 } else if (!strcmp(p, "-pgpfp")) {
523                     pgp_fingerprints();
524                     exit(1);
525                 } else if (*p != '-') {
526                     char *q = p;
527                     if (got_host) {
528                         /*
529                          * If we already have a host name, treat
530                          * this argument as a port number. NB we
531                          * have to treat this as a saved -P
532                          * argument, so that it will be deferred
533                          * until it's a good moment to run it.
534                          */
535                         int ret = cmdline_process_param("-P", p, 1, conf);
536                         assert(ret == 2);
537                     } else if (!strncmp(q, "telnet:", 7)) {
538                         /*
539                          * If the hostname starts with "telnet:",
540                          * set the protocol to Telnet and process
541                          * the string as a Telnet URL.
542                          */
543                         char c;
544
545                         q += 7;
546                         if (q[0] == '/' && q[1] == '/')
547                             q += 2;
548                         conf_set_int(conf, CONF_protocol, PROT_TELNET);
549                         p = q;
550                         p += host_strcspn(p, ":/");
551                         c = *p;
552                         if (*p)
553                             *p++ = '\0';
554                         if (c == ':')
555                             conf_set_int(conf, CONF_port, atoi(p));
556                         else
557                             conf_set_int(conf, CONF_port, -1);
558                         conf_set_str(conf, CONF_host, q);
559                         got_host = 1;
560                     } else {
561                         /*
562                          * Otherwise, treat this argument as a host
563                          * name.
564                          */
565                         while (*p && !isspace(*p))
566                             p++;
567                         if (*p)
568                             *p++ = '\0';
569                         conf_set_str(conf, CONF_host, q);
570                         got_host = 1;
571                     }
572                 } else {
573                     cmdline_error("unknown option \"%s\"", p);
574                 }
575             }
576         }
577
578         cmdline_run_saved(conf);
579
580         if (loaded_session || got_host)
581             allow_launch = TRUE;
582
583         if ((!allow_launch || !conf_launchable(conf)) && !do_config()) {
584             cleanup_exit(0);
585         }
586
587         /*
588          * Muck about with the hostname in various ways.
589          */
590         {
591             char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
592             char *host = hostbuf;
593             char *p, *q;
594
595             /*
596              * Trim leading whitespace.
597              */
598             host += strspn(host, " \t");
599
600             /*
601              * See if host is of the form user@host, and separate
602              * out the username if so.
603              */
604             if (host[0] != '\0') {
605                 char *atsign = strrchr(host, '@');
606                 if (atsign) {
607                     *atsign = '\0';
608                     conf_set_str(conf, CONF_username, host);
609                     host = atsign + 1;
610                 }
611             }
612
613             /*
614              * Trim a colon suffix off the hostname if it's there. In
615              * order to protect unbracketed IPv6 address literals
616              * against this treatment, we do not do this if there's
617              * _more_ than one colon.
618              */
619             {
620                 char *c = host_strchr(host, ':');
621  
622                 if (c) {
623                     char *d = host_strchr(c+1, ':');
624                     if (!d)
625                         *c = '\0';
626                 }
627             }
628
629             /*
630              * Remove any remaining whitespace.
631              */
632             p = hostbuf;
633             q = host;
634             while (*q) {
635                 if (*q != ' ' && *q != '\t')
636                     *p++ = *q;
637                 q++;
638             }
639             *p = '\0';
640
641             conf_set_str(conf, CONF_host, hostbuf);
642             sfree(hostbuf);
643         }
644     }
645
646     if (!prev) {
647         WNDCLASSW wndclass;
648
649         wndclass.style = 0;
650         wndclass.lpfnWndProc = WndProc;
651         wndclass.cbClsExtra = 0;
652         wndclass.cbWndExtra = 0;
653         wndclass.hInstance = inst;
654         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
655         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
656         wndclass.hbrBackground = NULL;
657         wndclass.lpszMenuName = NULL;
658         wndclass.lpszClassName = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
659
660         RegisterClassW(&wndclass);
661     }
662
663     memset(&ucsdata, 0, sizeof(ucsdata));
664
665     conf_cache_data();
666
667     conftopalette();
668
669     /*
670      * Guess some defaults for the window size. This all gets
671      * updated later, so we don't really care too much. However, we
672      * do want the font width/height guesses to correspond to a
673      * large font rather than a small one...
674      */
675
676     font_width = 10;
677     font_height = 20;
678     extra_width = 25;
679     extra_height = 28;
680     guess_width = extra_width + font_width * conf_get_int(conf, CONF_width);
681     guess_height = extra_height + font_height*conf_get_int(conf, CONF_height);
682     {
683         RECT r;
684         get_fullscreen_rect(&r);
685         if (guess_width > r.right - r.left)
686             guess_width = r.right - r.left;
687         if (guess_height > r.bottom - r.top)
688             guess_height = r.bottom - r.top;
689     }
690
691     {
692         int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
693         int exwinmode = 0;
694         wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
695         if (!conf_get_int(conf, CONF_scrollbar))
696             winmode &= ~(WS_VSCROLL);
697         if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
698             winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
699         if (conf_get_int(conf, CONF_alwaysontop))
700             exwinmode |= WS_EX_TOPMOST;
701         if (conf_get_int(conf, CONF_sunken_edge))
702             exwinmode |= WS_EX_CLIENTEDGE;
703         hwnd = CreateWindowExW(exwinmode, uappname, uappname,
704                                winmode, CW_USEDEFAULT, CW_USEDEFAULT,
705                                guess_width, guess_height,
706                                NULL, NULL, inst, NULL);
707         sfree(uappname);
708     }
709
710     /*
711      * Initialise the fonts, simultaneously correcting the guesses
712      * for font_{width,height}.
713      */
714     init_fonts(0,0);
715
716     /*
717      * Initialise the terminal. (We have to do this _after_
718      * creating the window, since the terminal is the first thing
719      * which will call schedule_timer(), which will in turn call
720      * timer_change_notify() which will expect hwnd to exist.)
721      */
722     term = term_init(conf, &ucsdata, NULL);
723     logctx = log_init(NULL, conf);
724     term_provide_logctx(term, logctx);
725     term_size(term, conf_get_int(conf, CONF_height),
726               conf_get_int(conf, CONF_width),
727               conf_get_int(conf, CONF_savelines));
728
729     /*
730      * Correct the guesses for extra_{width,height}.
731      */
732     {
733         RECT cr, wr;
734         GetWindowRect(hwnd, &wr);
735         GetClientRect(hwnd, &cr);
736         offset_width = offset_height = conf_get_int(conf, CONF_window_border);
737         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
738         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
739     }
740
741     /*
742      * Resize the window, now we know what size we _really_ want it
743      * to be.
744      */
745     guess_width = extra_width + font_width * term->cols;
746     guess_height = extra_height + font_height * term->rows;
747     SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
748                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
749
750     /*
751      * Set up a caret bitmap, with no content.
752      */
753     {
754         char *bits;
755         int size = (font_width + 15) / 16 * 2 * font_height;
756         bits = snewn(size, char);
757         memset(bits, 0, size);
758         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
759         sfree(bits);
760     }
761     CreateCaret(hwnd, caretbm, font_width, font_height);
762
763     /*
764      * Initialise the scroll bar.
765      */
766     {
767         SCROLLINFO si;
768
769         si.cbSize = sizeof(si);
770         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
771         si.nMin = 0;
772         si.nMax = term->rows - 1;
773         si.nPage = term->rows;
774         si.nPos = 0;
775         SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
776     }
777
778     /*
779      * Prepare the mouse handler.
780      */
781     lastact = MA_NOTHING;
782     lastbtn = MBT_NOTHING;
783     dbltime = GetDoubleClickTime();
784
785     /*
786      * Set up the session-control options on the system menu.
787      */
788     {
789         HMENU m;
790         int j;
791         char *str;
792
793         popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, FALSE);
794         popup_menus[CTXMENU].menu = CreatePopupMenu();
795         AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste");
796
797         savedsess_menu = CreateMenu();
798         get_sesslist(&sesslist, TRUE);
799         update_savedsess_menu();
800
801         for (j = 0; j < lenof(popup_menus); j++) {
802             m = popup_menus[j].menu;
803
804             AppendMenu(m, MF_SEPARATOR, 0, 0);
805             AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
806             AppendMenu(m, MF_SEPARATOR, 0, 0);
807             AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
808             AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
809             AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) savedsess_menu,
810                        "Sa&ved Sessions");
811             AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
812             AppendMenu(m, MF_SEPARATOR, 0, 0);
813             AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
814             AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
815             AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
816             AppendMenu(m, MF_SEPARATOR, 0, 0);
817             AppendMenu(m, (conf_get_int(conf, CONF_resize_action)
818                            == RESIZE_DISABLED) ? MF_GRAYED : MF_ENABLED,
819                        IDM_FULLSCREEN, "&Full Screen");
820             AppendMenu(m, MF_SEPARATOR, 0, 0);
821             if (has_help())
822                 AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
823             str = dupprintf("&About %s", appname);
824             AppendMenu(m, MF_ENABLED, IDM_ABOUT, str);
825             sfree(str);
826         }
827     }
828
829     start_backend();
830
831     /*
832      * Set up the initial input locale.
833      */
834     set_input_locale(GetKeyboardLayout(0));
835
836     /*
837      * Finally show the window!
838      */
839     ShowWindow(hwnd, show);
840     SetForegroundWindow(hwnd);
841
842     /*
843      * Set the palette up.
844      */
845     pal = NULL;
846     logpal = NULL;
847     init_palette();
848
849     term_set_focus(term, GetForegroundWindow() == hwnd);
850     UpdateWindow(hwnd);
851
852     while (1) {
853         HANDLE *handles;
854         int nhandles, n;
855         DWORD timeout;
856
857         if (toplevel_callback_pending() ||
858             PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
859             /*
860              * If we have anything we'd like to do immediately, set
861              * the timeout for MsgWaitForMultipleObjects to zero so
862              * that we'll only do a quick check of our handles and
863              * then get on with whatever that was.
864              *
865              * One such option is a pending toplevel callback. The
866              * other is a non-empty Windows message queue, which you'd
867              * think we could leave to MsgWaitForMultipleObjects to
868              * check for us along with all the handles, but in fact we
869              * can't because once PeekMessage in one iteration of this
870              * loop has removed a message from the queue, the whole
871              * queue is considered uninteresting by the next
872              * invocation of MWFMO. So we check ourselves whether the
873              * message queue is non-empty, and if so, set this timeout
874              * to zero to ensure MWFMO doesn't block.
875              */
876             timeout = 0;
877         } else {
878             timeout = INFINITE;
879             /* The messages seem unreliable; especially if we're being tricky */
880             term_set_focus(term, GetForegroundWindow() == hwnd);
881         }
882
883         handles = handle_get_events(&nhandles);
884
885         n = MsgWaitForMultipleObjects(nhandles, handles, FALSE,
886                                       timeout, QS_ALLINPUT);
887
888         if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
889             handle_got_event(handles[n - WAIT_OBJECT_0]);
890             sfree(handles);
891         } else
892             sfree(handles);
893
894         while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
895             if (msg.message == WM_QUIT)
896                 goto finished;         /* two-level break */
897
898             if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
899                 DispatchMessageW(&msg);
900
901             /*
902              * WM_NETEVENT messages seem to jump ahead of others in
903              * the message queue. I'm not sure why; the docs for
904              * PeekMessage mention that messages are prioritised in
905              * some way, but I'm unclear on which priorities go where.
906              *
907              * Anyway, in practice I observe that WM_NETEVENT seems to
908              * jump to the head of the queue, which means that if we
909              * were to only process one message every time round this
910              * loop, we'd get nothing but NETEVENTs if the server
911              * flooded us with data, and stop responding to any other
912              * kind of window message. So instead, we keep on round
913              * this loop until we've consumed at least one message
914              * that _isn't_ a NETEVENT, or run out of messages
915              * completely (whichever comes first). And we don't go to
916              * run_toplevel_callbacks (which is where the netevents
917              * are actually processed, causing fresh NETEVENT messages
918              * to appear) until we've done this.
919              */
920             if (msg.message != WM_NETEVENT)
921                 break;
922         }
923
924         run_toplevel_callbacks();
925     }
926
927     finished:
928     cleanup_exit(msg.wParam);          /* this doesn't return... */
929     return msg.wParam;                 /* ... but optimiser doesn't know */
930 }
931
932 /*
933  * Clean up and exit.
934  */
935 void cleanup_exit(int code)
936 {
937     /*
938      * Clean up.
939      */
940     deinit_fonts();
941     sfree(logpal);
942     if (pal)
943         DeleteObject(pal);
944     sk_cleanup();
945
946     if (conf_get_int(conf, CONF_protocol) == PROT_SSH) {
947         random_save_seed();
948 #ifdef MSCRYPTOAPI
949         crypto_wrapup();
950 #endif
951     }
952     shutdown_help();
953
954     /* Clean up COM. */
955     CoUninitialize();
956
957     exit(code);
958 }
959
960 /*
961  * Set up, or shut down, an AsyncSelect. Called from winnet.c.
962  */
963 char *do_select(SOCKET skt, int startup)
964 {
965     int msg, events;
966     if (startup) {
967         msg = WM_NETEVENT;
968         events = (FD_CONNECT | FD_READ | FD_WRITE |
969                   FD_OOB | FD_CLOSE | FD_ACCEPT);
970     } else {
971         msg = events = 0;
972     }
973     if (!hwnd)
974         return "do_select(): internal error (hwnd==NULL)";
975     if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
976         switch (p_WSAGetLastError()) {
977           case WSAENETDOWN:
978             return "Network is down";
979           default:
980             return "WSAAsyncSelect(): unknown error";
981         }
982     }
983     return NULL;
984 }
985
986 /*
987  * Refresh the saved-session submenu from `sesslist'.
988  */
989 static void update_savedsess_menu(void)
990 {
991     int i;
992     while (DeleteMenu(savedsess_menu, 0, MF_BYPOSITION)) ;
993     /* skip sesslist.sessions[0] == Default Settings */
994     for (i = 1;
995          i < ((sesslist.nsessions <= MENU_SAVED_MAX+1) ? sesslist.nsessions
996                                                        : MENU_SAVED_MAX+1);
997          i++)
998         AppendMenu(savedsess_menu, MF_ENABLED,
999                    IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP,
1000                    sesslist.sessions[i]);
1001     if (sesslist.nsessions <= 1)
1002         AppendMenu(savedsess_menu, MF_GRAYED, IDM_SAVED_MIN, "(No sessions)");
1003 }
1004
1005 /*
1006  * Update the Special Commands submenu.
1007  */
1008 void update_specials_menu(void *frontend)
1009 {
1010     HMENU new_menu;
1011     int i, j;
1012
1013     if (back)
1014         specials = back->get_specials(backhandle);
1015     else
1016         specials = NULL;
1017
1018     if (specials) {
1019         /* We can't use Windows to provide a stack for submenus, so
1020          * here's a lame "stack" that will do for now. */
1021         HMENU saved_menu = NULL;
1022         int nesting = 1;
1023         new_menu = CreatePopupMenu();
1024         for (i = 0; nesting > 0; i++) {
1025             assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX);
1026             switch (specials[i].code) {
1027               case TS_SEP:
1028                 AppendMenu(new_menu, MF_SEPARATOR, 0, 0);
1029                 break;
1030               case TS_SUBMENU:
1031                 assert(nesting < 2);
1032                 nesting++;
1033                 saved_menu = new_menu; /* XXX lame stacking */
1034                 new_menu = CreatePopupMenu();
1035                 AppendMenu(saved_menu, MF_POPUP | MF_ENABLED,
1036                            (UINT) new_menu, specials[i].name);
1037                 break;
1038               case TS_EXITMENU:
1039                 nesting--;
1040                 if (nesting) {
1041                     new_menu = saved_menu; /* XXX lame stacking */
1042                     saved_menu = NULL;
1043                 }
1044                 break;
1045               default:
1046                 AppendMenu(new_menu, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i,
1047                            specials[i].name);
1048                 break;
1049             }
1050         }
1051         /* Squirrel the highest special. */
1052         n_specials = i - 1;
1053     } else {
1054         new_menu = NULL;
1055         n_specials = 0;
1056     }
1057
1058     for (j = 0; j < lenof(popup_menus); j++) {
1059         if (specials_menu) {
1060             /* XXX does this free up all submenus? */
1061             DeleteMenu(popup_menus[j].menu, (UINT)specials_menu, MF_BYCOMMAND);
1062             DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND);
1063         }
1064         if (new_menu) {
1065             InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
1066                        MF_BYCOMMAND | MF_POPUP | MF_ENABLED,
1067                        (UINT) new_menu, "S&pecial Command");
1068             InsertMenu(popup_menus[j].menu, IDM_SHOWLOG,
1069                        MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0);
1070         }
1071     }
1072     specials_menu = new_menu;
1073 }
1074
1075 static void update_mouse_pointer(void)
1076 {
1077     LPTSTR curstype;
1078     int force_visible = FALSE;
1079     static int forced_visible = FALSE;
1080     switch (busy_status) {
1081       case BUSY_NOT:
1082         if (send_raw_mouse)
1083             curstype = IDC_ARROW;
1084         else
1085             curstype = IDC_IBEAM;
1086         break;
1087       case BUSY_WAITING:
1088         curstype = IDC_APPSTARTING; /* this may be an abuse */
1089         force_visible = TRUE;
1090         break;
1091       case BUSY_CPU:
1092         curstype = IDC_WAIT;
1093         force_visible = TRUE;
1094         break;
1095       default:
1096         assert(0);
1097     }
1098     {
1099         HCURSOR cursor = LoadCursor(NULL, curstype);
1100         SetClassLongPtr(hwnd, GCLP_HCURSOR, (LONG_PTR)cursor);
1101         SetCursor(cursor); /* force redraw of cursor at current posn */
1102     }
1103     if (force_visible != forced_visible) {
1104         /* We want some cursor shapes to be visible always.
1105          * Along with show_mouseptr(), this manages the ShowCursor()
1106          * counter such that if we switch back to a non-force_visible
1107          * cursor, the previous visibility state is restored. */
1108         ShowCursor(force_visible);
1109         forced_visible = force_visible;
1110     }
1111 }
1112
1113 void set_busy_status(void *frontend, int status)
1114 {
1115     busy_status = status;
1116     update_mouse_pointer();
1117 }
1118
1119 /*
1120  * set or clear the "raw mouse message" mode
1121  */
1122 void set_raw_mouse_mode(void *frontend, int activate)
1123 {
1124     activate = activate && !conf_get_int(conf, CONF_no_mouse_rep);
1125     send_raw_mouse = activate;
1126     update_mouse_pointer();
1127 }
1128
1129 /*
1130  * Print a message box and close the connection.
1131  */
1132 void connection_fatal(void *frontend, char *fmt, ...)
1133 {
1134     va_list ap;
1135     char *stuff, morestuff[100];
1136
1137     va_start(ap, fmt);
1138     stuff = dupvprintf(fmt, ap);
1139     va_end(ap);
1140     sprintf(morestuff, "%.70s Fatal Error", appname);
1141     MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
1142     sfree(stuff);
1143
1144     if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON)
1145         PostQuitMessage(1);
1146     else {
1147         queue_toplevel_callback(close_session, NULL);
1148     }
1149 }
1150
1151 /*
1152  * Report an error at the command-line parsing stage.
1153  */
1154 void cmdline_error(char *fmt, ...)
1155 {
1156     va_list ap;
1157     char *stuff, morestuff[100];
1158
1159     va_start(ap, fmt);
1160     stuff = dupvprintf(fmt, ap);
1161     va_end(ap);
1162     sprintf(morestuff, "%.70s Command Line Error", appname);
1163     MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
1164     sfree(stuff);
1165     exit(1);
1166 }
1167
1168 /*
1169  * Actually do the job requested by a WM_NETEVENT
1170  */
1171 static void wm_netevent_callback(void *vctx)
1172 {
1173     struct wm_netevent_params *params = (struct wm_netevent_params *)vctx;
1174     select_result(params->wParam, params->lParam);
1175     sfree(vctx);
1176 }
1177
1178 /*
1179  * Copy the colour palette from the configuration data into defpal.
1180  * This is non-trivial because the colour indices are different.
1181  */
1182 static void conftopalette(void)
1183 {
1184     int i;
1185     static const int ww[] = {
1186         256, 257, 258, 259, 260, 261,
1187         0, 8, 1, 9, 2, 10, 3, 11,
1188         4, 12, 5, 13, 6, 14, 7, 15
1189     };
1190
1191     for (i = 0; i < 22; i++) {
1192         int w = ww[i];
1193         defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0);
1194         defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1);
1195         defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2);
1196     }
1197     for (i = 0; i < NEXTCOLOURS; i++) {
1198         if (i < 216) {
1199             int r = i / 36, g = (i / 6) % 6, b = i % 6;
1200             defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0;
1201             defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0;
1202             defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0;
1203         } else {
1204             int shade = i - 216;
1205             shade = shade * 10 + 8;
1206             defpal[i+16].rgbtRed = defpal[i+16].rgbtGreen =
1207                 defpal[i+16].rgbtBlue = shade;
1208         }
1209     }
1210
1211     /* Override with system colours if appropriate */
1212     if (conf_get_int(conf, CONF_system_colour))
1213         systopalette();
1214 }
1215
1216 /*
1217  * Override bit of defpal with colours from the system.
1218  * (NB that this takes a copy the system colours at the time this is called,
1219  * so subsequent colour scheme changes don't take effect. To fix that we'd
1220  * probably want to be using GetSysColorBrush() and the like.)
1221  */
1222 static void systopalette(void)
1223 {
1224     int i;
1225     static const struct { int nIndex; int norm; int bold; } or[] =
1226     {
1227         { COLOR_WINDOWTEXT,     256, 257 }, /* Default Foreground */
1228         { COLOR_WINDOW,         258, 259 }, /* Default Background */
1229         { COLOR_HIGHLIGHTTEXT,  260, 260 }, /* Cursor Text */
1230         { COLOR_HIGHLIGHT,      261, 261 }, /* Cursor Colour */
1231     };
1232
1233     for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) {
1234         COLORREF colour = GetSysColor(or[i].nIndex);
1235         defpal[or[i].norm].rgbtRed =
1236            defpal[or[i].bold].rgbtRed = GetRValue(colour);
1237         defpal[or[i].norm].rgbtGreen =
1238            defpal[or[i].bold].rgbtGreen = GetGValue(colour);
1239         defpal[or[i].norm].rgbtBlue =
1240            defpal[or[i].bold].rgbtBlue = GetBValue(colour);
1241     }
1242 }
1243
1244 /*
1245  * Set up the colour palette.
1246  */
1247 static void init_palette(void)
1248 {
1249     int i;
1250     HDC hdc = GetDC(hwnd);
1251     if (hdc) {
1252         if (conf_get_int(conf, CONF_try_palette) &&
1253             GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
1254             /*
1255              * This is a genuine case where we must use smalloc
1256              * because the snew macros can't cope.
1257              */
1258             logpal = smalloc(sizeof(*logpal)
1259                              - sizeof(logpal->palPalEntry)
1260                              + NALLCOLOURS * sizeof(PALETTEENTRY));
1261             logpal->palVersion = 0x300;
1262             logpal->palNumEntries = NALLCOLOURS;
1263             for (i = 0; i < NALLCOLOURS; i++) {
1264                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1265                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1266                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1267                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1268             }
1269             pal = CreatePalette(logpal);
1270             if (pal) {
1271                 SelectPalette(hdc, pal, FALSE);
1272                 RealizePalette(hdc);
1273                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1274             }
1275         }
1276         ReleaseDC(hwnd, hdc);
1277     }
1278     if (pal)
1279         for (i = 0; i < NALLCOLOURS; i++)
1280             colours[i] = PALETTERGB(defpal[i].rgbtRed,
1281                                     defpal[i].rgbtGreen,
1282                                     defpal[i].rgbtBlue);
1283     else
1284         for (i = 0; i < NALLCOLOURS; i++)
1285             colours[i] = RGB(defpal[i].rgbtRed,
1286                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1287 }
1288
1289 /*
1290  * This is a wrapper to ExtTextOut() to force Windows to display
1291  * the precise glyphs we give it. Otherwise it would do its own
1292  * bidi and Arabic shaping, and we would end up uncertain which
1293  * characters it had put where.
1294  */
1295 static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
1296                           unsigned short *lpString, UINT cbCount,
1297                           CONST INT *lpDx, int opaque)
1298 {
1299 #ifdef __LCC__
1300     /*
1301      * The LCC include files apparently don't supply the
1302      * GCP_RESULTSW type, but we can make do with GCP_RESULTS
1303      * proper: the differences aren't important to us (the only
1304      * variable-width string parameter is one we don't use anyway).
1305      */
1306     GCP_RESULTS gcpr;
1307 #else
1308     GCP_RESULTSW gcpr;
1309 #endif
1310     char *buffer = snewn(cbCount*2+2, char);
1311     char *classbuffer = snewn(cbCount, char);
1312     memset(&gcpr, 0, sizeof(gcpr));
1313     memset(buffer, 0, cbCount*2+2);
1314     memset(classbuffer, GCPCLASS_NEUTRAL, cbCount);
1315
1316     gcpr.lStructSize = sizeof(gcpr);
1317     gcpr.lpGlyphs = (void *)buffer;
1318     gcpr.lpClass = (void *)classbuffer;
1319     gcpr.nGlyphs = cbCount;
1320     GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
1321                            FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC);
1322
1323     ExtTextOut(hdc, x, y,
1324                ETO_GLYPH_INDEX | ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
1325                lprc, buffer, cbCount, lpDx);
1326 }
1327
1328 /*
1329  * The exact_textout() wrapper, unfortunately, destroys the useful
1330  * Windows `font linking' behaviour: automatic handling of Unicode
1331  * code points not supported in this font by falling back to a font
1332  * which does contain them. Therefore, we adopt a multi-layered
1333  * approach: for any potentially-bidi text, we use exact_textout(),
1334  * and for everything else we use a simple ExtTextOut as we did
1335  * before exact_textout() was introduced.
1336  */
1337 static void general_textout(HDC hdc, int x, int y, CONST RECT *lprc,
1338                             unsigned short *lpString, UINT cbCount,
1339                             CONST INT *lpDx, int opaque)
1340 {
1341     int i, j, xp, xn;
1342     int bkmode = 0, got_bkmode = FALSE;
1343
1344     xp = xn = x;
1345
1346     for (i = 0; i < (int)cbCount ;) {
1347         int rtl = is_rtl(lpString[i]);
1348
1349         xn += lpDx[i];
1350
1351         for (j = i+1; j < (int)cbCount; j++) {
1352             if (rtl != is_rtl(lpString[j]))
1353                 break;
1354             xn += lpDx[j];
1355         }
1356
1357         /*
1358          * Now [i,j) indicates a maximal substring of lpString
1359          * which should be displayed using the same textout
1360          * function.
1361          */
1362         if (rtl) {
1363             exact_textout(hdc, xp, y, lprc, lpString+i, j-i,
1364                           font_varpitch ? NULL : lpDx+i, opaque);
1365         } else {
1366             ExtTextOutW(hdc, xp, y, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
1367                         lprc, lpString+i, j-i,
1368                         font_varpitch ? NULL : lpDx+i);
1369         }
1370
1371         i = j;
1372         xp = xn;
1373
1374         bkmode = GetBkMode(hdc);
1375         got_bkmode = TRUE;
1376         SetBkMode(hdc, TRANSPARENT);
1377         opaque = FALSE;
1378     }
1379
1380     if (got_bkmode)
1381         SetBkMode(hdc, bkmode);
1382 }
1383
1384 static int get_font_width(HDC hdc, const TEXTMETRIC *tm)
1385 {
1386     int ret;
1387     /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */
1388     if (!(tm->tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1389         ret = tm->tmAveCharWidth;
1390     } else {
1391 #define FIRST '0'
1392 #define LAST '9'
1393         ABCFLOAT widths[LAST-FIRST + 1];
1394         int j;
1395
1396         font_varpitch = TRUE;
1397         font_dualwidth = TRUE;
1398         if (GetCharABCWidthsFloat(hdc, FIRST, LAST, widths)) {
1399             ret = 0;
1400             for (j = 0; j < lenof(widths); j++) {
1401                 int width = (int)(0.5 + widths[j].abcfA +
1402                                   widths[j].abcfB + widths[j].abcfC);
1403                 if (ret < width)
1404                     ret = width;
1405             }
1406         } else {
1407             ret = tm->tmMaxCharWidth;
1408         }
1409 #undef FIRST
1410 #undef LAST
1411     }
1412     return ret;
1413 }
1414
1415 /*
1416  * Initialise all the fonts we will need initially. There may be as many as
1417  * three or as few as one.  The other (potentially) twenty-one fonts are done
1418  * if/when they are needed.
1419  *
1420  * We also:
1421  *
1422  * - check the font width and height, correcting our guesses if
1423  *   necessary.
1424  *
1425  * - verify that the bold font is the same width as the ordinary
1426  *   one, and engage shadow bolding if not.
1427  * 
1428  * - verify that the underlined font is the same width as the
1429  *   ordinary one (manual underlining by means of line drawing can
1430  *   be done in a pinch).
1431  */
1432 static void init_fonts(int pick_width, int pick_height)
1433 {
1434     TEXTMETRIC tm;
1435     CPINFO cpinfo;
1436     FontSpec *font;
1437     int fontsize[3];
1438     int i;
1439     int quality;
1440     HDC hdc;
1441     int fw_dontcare, fw_bold;
1442
1443     for (i = 0; i < FONT_MAXNO; i++)
1444         fonts[i] = NULL;
1445
1446     bold_font_mode = conf_get_int(conf, CONF_bold_style) & 1 ?
1447         BOLD_FONT : BOLD_NONE;
1448     bold_colours = conf_get_int(conf, CONF_bold_style) & 2 ? TRUE : FALSE;
1449     und_mode = UND_FONT;
1450
1451     font = conf_get_fontspec(conf, CONF_font);
1452     if (font->isbold) {
1453         fw_dontcare = FW_BOLD;
1454         fw_bold = FW_HEAVY;
1455     } else {
1456         fw_dontcare = FW_DONTCARE;
1457         fw_bold = FW_BOLD;
1458     }
1459
1460     hdc = GetDC(hwnd);
1461
1462     if (pick_height)
1463         font_height = pick_height;
1464     else {
1465         font_height = font->height;
1466         if (font_height > 0) {
1467             font_height =
1468                 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1469         }
1470     }
1471     font_width = pick_width;
1472
1473     quality = conf_get_int(conf, CONF_font_quality);
1474 #define f(i,c,w,u) \
1475     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1476                            c, OUT_DEFAULT_PRECIS, \
1477                            CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality), \
1478                            FIXED_PITCH | FF_DONTCARE, font->name)
1479
1480     f(FONT_NORMAL, font->charset, fw_dontcare, FALSE);
1481
1482     SelectObject(hdc, fonts[FONT_NORMAL]);
1483     GetTextMetrics(hdc, &tm);
1484
1485     GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont);
1486
1487     /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */
1488     if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) {
1489         font_varpitch = FALSE;
1490         font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1491     } else {
1492         font_varpitch = TRUE;
1493         font_dualwidth = TRUE;
1494     }
1495     if (pick_width == 0 || pick_height == 0) {
1496         font_height = tm.tmHeight;
1497         font_width = get_font_width(hdc, &tm);
1498     }
1499
1500 #ifdef RDB_DEBUG_PATCH
1501     debug(23, "Primary font H=%d, AW=%d, MW=%d",
1502             tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1503 #endif
1504
1505     {
1506         CHARSETINFO info;
1507         DWORD cset = tm.tmCharSet;
1508         memset(&info, 0xFF, sizeof(info));
1509
1510         /* !!! Yes the next line is right */
1511         if (cset == OEM_CHARSET)
1512             ucsdata.font_codepage = GetOEMCP();
1513         else
1514             if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
1515                 ucsdata.font_codepage = info.ciACP;
1516         else
1517             ucsdata.font_codepage = -1;
1518
1519         GetCPInfo(ucsdata.font_codepage, &cpinfo);
1520         ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1521     }
1522
1523     f(FONT_UNDERLINE, font->charset, fw_dontcare, TRUE);
1524
1525     /*
1526      * Some fonts, e.g. 9-pt Courier, draw their underlines
1527      * outside their character cell. We successfully prevent
1528      * screen corruption by clipping the text output, but then
1529      * we lose the underline completely. Here we try to work
1530      * out whether this is such a font, and if it is, we set a
1531      * flag that causes underlines to be drawn by hand.
1532      *
1533      * Having tried other more sophisticated approaches (such
1534      * as examining the TEXTMETRIC structure or requesting the
1535      * height of a string), I think we'll do this the brute
1536      * force way: we create a small bitmap, draw an underlined
1537      * space on it, and test to see whether any pixels are
1538      * foreground-coloured. (Since we expect the underline to
1539      * go all the way across the character cell, we only search
1540      * down a single column of the bitmap, half way across.)
1541      */
1542     {
1543         HDC und_dc;
1544         HBITMAP und_bm, und_oldbm;
1545         int i, gotit;
1546         COLORREF c;
1547
1548         und_dc = CreateCompatibleDC(hdc);
1549         und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1550         und_oldbm = SelectObject(und_dc, und_bm);
1551         SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1552         SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1553         SetTextColor(und_dc, RGB(255, 255, 255));
1554         SetBkColor(und_dc, RGB(0, 0, 0));
1555         SetBkMode(und_dc, OPAQUE);
1556         ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1557         gotit = FALSE;
1558         for (i = 0; i < font_height; i++) {
1559             c = GetPixel(und_dc, font_width / 2, i);
1560             if (c != RGB(0, 0, 0))
1561                 gotit = TRUE;
1562         }
1563         SelectObject(und_dc, und_oldbm);
1564         DeleteObject(und_bm);
1565         DeleteDC(und_dc);
1566         if (!gotit) {
1567             und_mode = UND_LINE;
1568             DeleteObject(fonts[FONT_UNDERLINE]);
1569             fonts[FONT_UNDERLINE] = 0;
1570         }
1571     }
1572
1573     if (bold_font_mode == BOLD_FONT) {
1574         f(FONT_BOLD, font->charset, fw_bold, FALSE);
1575     }
1576 #undef f
1577
1578     descent = tm.tmAscent + 1;
1579     if (descent >= font_height)
1580         descent = font_height - 1;
1581
1582     for (i = 0; i < 3; i++) {
1583         if (fonts[i]) {
1584             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1585                 fontsize[i] = get_font_width(hdc, &tm) + 256 * tm.tmHeight;
1586             else
1587                 fontsize[i] = -i;
1588         } else
1589             fontsize[i] = -i;
1590     }
1591
1592     ReleaseDC(hwnd, hdc);
1593
1594     if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1595         und_mode = UND_LINE;
1596         DeleteObject(fonts[FONT_UNDERLINE]);
1597         fonts[FONT_UNDERLINE] = 0;
1598     }
1599
1600     if (bold_font_mode == BOLD_FONT &&
1601         fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1602         bold_font_mode = BOLD_SHADOW;
1603         DeleteObject(fonts[FONT_BOLD]);
1604         fonts[FONT_BOLD] = 0;
1605     }
1606     fontflag[0] = fontflag[1] = fontflag[2] = 1;
1607
1608     init_ucs(conf, &ucsdata);
1609 }
1610
1611 static void another_font(int fontno)
1612 {
1613     int basefont;
1614     int fw_dontcare, fw_bold, quality;
1615     int c, u, w, x;
1616     char *s;
1617     FontSpec *font;
1618
1619     if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1620         return;
1621
1622     basefont = (fontno & ~(FONT_BOLDUND));
1623     if (basefont != fontno && !fontflag[basefont])
1624         another_font(basefont);
1625
1626     font = conf_get_fontspec(conf, CONF_font);
1627
1628     if (font->isbold) {
1629         fw_dontcare = FW_BOLD;
1630         fw_bold = FW_HEAVY;
1631     } else {
1632         fw_dontcare = FW_DONTCARE;
1633         fw_bold = FW_BOLD;
1634     }
1635
1636     c = font->charset;
1637     w = fw_dontcare;
1638     u = FALSE;
1639     s = font->name;
1640     x = font_width;
1641
1642     if (fontno & FONT_WIDE)
1643         x *= 2;
1644     if (fontno & FONT_NARROW)
1645         x = (x+1)/2;
1646     if (fontno & FONT_OEM)
1647         c = OEM_CHARSET;
1648     if (fontno & FONT_BOLD)
1649         w = fw_bold;
1650     if (fontno & FONT_UNDERLINE)
1651         u = TRUE;
1652
1653     quality = conf_get_int(conf, CONF_font_quality);
1654
1655     fonts[fontno] =
1656         CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1657                    FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1658                    CLIP_DEFAULT_PRECIS, FONT_QUALITY(quality),
1659                    DEFAULT_PITCH | FF_DONTCARE, s);
1660
1661     fontflag[fontno] = 1;
1662 }
1663
1664 static void deinit_fonts(void)
1665 {
1666     int i;
1667     for (i = 0; i < FONT_MAXNO; i++) {
1668         if (fonts[i])
1669             DeleteObject(fonts[i]);
1670         fonts[i] = 0;
1671         fontflag[i] = 0;
1672     }
1673 }
1674
1675 void request_resize(void *frontend, int w, int h)
1676 {
1677     int width, height;
1678
1679     /* If the window is maximized supress resizing attempts */
1680     if (IsZoomed(hwnd)) {
1681         if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM)
1682             return;
1683     }
1684
1685     if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return;
1686     if (h == term->rows && w == term->cols) return;
1687
1688     /* Sanity checks ... */
1689     {
1690         static int first_time = 1;
1691         static RECT ss;
1692
1693         switch (first_time) {
1694           case 1:
1695             /* Get the size of the screen */
1696             if (get_fullscreen_rect(&ss))
1697                 /* first_time = 0 */ ;
1698             else {
1699                 first_time = 2;
1700                 break;
1701             }
1702           case 0:
1703             /* Make sure the values are sane */
1704             width = (ss.right - ss.left - extra_width) / 4;
1705             height = (ss.bottom - ss.top - extra_height) / 6;
1706
1707             if (w > width || h > height)
1708                 return;
1709             if (w < 15)
1710                 w = 15;
1711             if (h < 1)
1712                 h = 1;
1713         }
1714     }
1715
1716     term_size(term, h, w, conf_get_int(conf, CONF_savelines));
1717
1718     if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT &&
1719         !IsZoomed(hwnd)) {
1720         width = extra_width + font_width * w;
1721         height = extra_height + font_height * h;
1722
1723         SetWindowPos(hwnd, NULL, 0, 0, width, height,
1724             SWP_NOACTIVATE | SWP_NOCOPYBITS |
1725             SWP_NOMOVE | SWP_NOZORDER);
1726     } else
1727         reset_window(0);
1728
1729     InvalidateRect(hwnd, NULL, TRUE);
1730 }
1731
1732 static void reset_window(int reinit) {
1733     /*
1734      * This function decides how to resize or redraw when the 
1735      * user changes something. 
1736      *
1737      * This function doesn't like to change the terminal size but if the
1738      * font size is locked that may be it's only soluion.
1739      */
1740     int win_width, win_height, resize_action, window_border;
1741     RECT cr, wr;
1742
1743 #ifdef RDB_DEBUG_PATCH
1744     debug((27, "reset_window()"));
1745 #endif
1746
1747     /* Current window sizes ... */
1748     GetWindowRect(hwnd, &wr);
1749     GetClientRect(hwnd, &cr);
1750
1751     win_width  = cr.right - cr.left;
1752     win_height = cr.bottom - cr.top;
1753
1754     resize_action = conf_get_int(conf, CONF_resize_action);
1755     window_border = conf_get_int(conf, CONF_window_border);
1756
1757     if (resize_action == RESIZE_DISABLED)
1758         reinit = 2;
1759
1760     /* Are we being forced to reload the fonts ? */
1761     if (reinit>1) {
1762 #ifdef RDB_DEBUG_PATCH
1763         debug((27, "reset_window() -- Forced deinit"));
1764 #endif
1765         deinit_fonts();
1766         init_fonts(0,0);
1767     }
1768
1769     /* Oh, looks like we're minimised */
1770     if (win_width == 0 || win_height == 0)
1771         return;
1772
1773     /* Is the window out of position ? */
1774     if ( !reinit && 
1775             (offset_width != (win_width-font_width*term->cols)/2 ||
1776              offset_height != (win_height-font_height*term->rows)/2) ){
1777         offset_width = (win_width-font_width*term->cols)/2;
1778         offset_height = (win_height-font_height*term->rows)/2;
1779         InvalidateRect(hwnd, NULL, TRUE);
1780 #ifdef RDB_DEBUG_PATCH
1781         debug((27, "reset_window() -> Reposition terminal"));
1782 #endif
1783     }
1784
1785     if (IsZoomed(hwnd)) {
1786         /* We're fullscreen, this means we must not change the size of
1787          * the window so it's the font size or the terminal itself.
1788          */
1789
1790         extra_width = wr.right - wr.left - cr.right + cr.left;
1791         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1792
1793         if (resize_action != RESIZE_TERM) {
1794             if (font_width != win_width/term->cols || 
1795                 font_height != win_height/term->rows) {
1796                 deinit_fonts();
1797                 init_fonts(win_width/term->cols, win_height/term->rows);
1798                 offset_width = (win_width-font_width*term->cols)/2;
1799                 offset_height = (win_height-font_height*term->rows)/2;
1800                 InvalidateRect(hwnd, NULL, TRUE);
1801 #ifdef RDB_DEBUG_PATCH
1802                 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1803                         font_width, font_height));
1804 #endif
1805             }
1806         } else {
1807             if (font_width * term->cols != win_width || 
1808                 font_height * term->rows != win_height) {
1809                 /* Our only choice at this point is to change the 
1810                  * size of the terminal; Oh well.
1811                  */
1812                 term_size(term, win_height/font_height, win_width/font_width,
1813                           conf_get_int(conf, CONF_savelines));
1814                 offset_width = (win_width-font_width*term->cols)/2;
1815                 offset_height = (win_height-font_height*term->rows)/2;
1816                 InvalidateRect(hwnd, NULL, TRUE);
1817 #ifdef RDB_DEBUG_PATCH
1818                 debug((27, "reset_window() -> Zoomed term_size"));
1819 #endif
1820             }
1821         }
1822         return;
1823     }
1824
1825     /* Hmm, a force re-init means we should ignore the current window
1826      * so we resize to the default font size.
1827      */
1828     if (reinit>0) {
1829 #ifdef RDB_DEBUG_PATCH
1830         debug((27, "reset_window() -> Forced re-init"));
1831 #endif
1832
1833         offset_width = offset_height = window_border;
1834         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1835         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1836
1837         if (win_width != font_width*term->cols + offset_width*2 ||
1838             win_height != font_height*term->rows + offset_height*2) {
1839
1840             /* If this is too large windows will resize it to the maximum
1841              * allowed window size, we will then be back in here and resize
1842              * the font or terminal to fit.
1843              */
1844             SetWindowPos(hwnd, NULL, 0, 0, 
1845                          font_width*term->cols + extra_width, 
1846                          font_height*term->rows + extra_height,
1847                          SWP_NOMOVE | SWP_NOZORDER);
1848         }
1849
1850         InvalidateRect(hwnd, NULL, TRUE);
1851         return;
1852     }
1853
1854     /* Okay the user doesn't want us to change the font so we try the 
1855      * window. But that may be too big for the screen which forces us
1856      * to change the terminal.
1857      */
1858     if ((resize_action == RESIZE_TERM && reinit<=0) ||
1859         (resize_action == RESIZE_EITHER && reinit<0) ||
1860             reinit>0) {
1861         offset_width = offset_height = window_border;
1862         extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1863         extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1864
1865         if (win_width != font_width*term->cols + offset_width*2 ||
1866             win_height != font_height*term->rows + offset_height*2) {
1867
1868             static RECT ss;
1869             int width, height;
1870                 
1871                 get_fullscreen_rect(&ss);
1872
1873             width = (ss.right - ss.left - extra_width) / font_width;
1874             height = (ss.bottom - ss.top - extra_height) / font_height;
1875
1876             /* Grrr too big */
1877             if ( term->rows > height || term->cols > width ) {
1878                 if (resize_action == RESIZE_EITHER) {
1879                     /* Make the font the biggest we can */
1880                     if (term->cols > width)
1881                         font_width = (ss.right - ss.left - extra_width)
1882                             / term->cols;
1883                     if (term->rows > height)
1884                         font_height = (ss.bottom - ss.top - extra_height)
1885                             / term->rows;
1886
1887                     deinit_fonts();
1888                     init_fonts(font_width, font_height);
1889
1890                     width = (ss.right - ss.left - extra_width) / font_width;
1891                     height = (ss.bottom - ss.top - extra_height) / font_height;
1892                 } else {
1893                     if ( height > term->rows ) height = term->rows;
1894                     if ( width > term->cols )  width = term->cols;
1895                     term_size(term, height, width,
1896                               conf_get_int(conf, CONF_savelines));
1897 #ifdef RDB_DEBUG_PATCH
1898                     debug((27, "reset_window() -> term resize to (%d,%d)",
1899                                height, width));
1900 #endif
1901                 }
1902             }
1903             
1904             SetWindowPos(hwnd, NULL, 0, 0, 
1905                          font_width*term->cols + extra_width, 
1906                          font_height*term->rows + extra_height,
1907                          SWP_NOMOVE | SWP_NOZORDER);
1908
1909             InvalidateRect(hwnd, NULL, TRUE);
1910 #ifdef RDB_DEBUG_PATCH
1911             debug((27, "reset_window() -> window resize to (%d,%d)",
1912                         font_width*term->cols + extra_width,
1913                         font_height*term->rows + extra_height));
1914 #endif
1915         }
1916         return;
1917     }
1918
1919     /* We're allowed to or must change the font but do we want to ?  */
1920
1921     if (font_width != (win_width-window_border*2)/term->cols || 
1922         font_height != (win_height-window_border*2)/term->rows) {
1923
1924         deinit_fonts();
1925         init_fonts((win_width-window_border*2)/term->cols, 
1926                    (win_height-window_border*2)/term->rows);
1927         offset_width = (win_width-font_width*term->cols)/2;
1928         offset_height = (win_height-font_height*term->rows)/2;
1929
1930         extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1931         extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1932
1933         InvalidateRect(hwnd, NULL, TRUE);
1934 #ifdef RDB_DEBUG_PATCH
1935         debug((25, "reset_window() -> font resize to (%d,%d)", 
1936                    font_width, font_height));
1937 #endif
1938     }
1939 }
1940
1941 static void set_input_locale(HKL kl)
1942 {
1943     char lbuf[20];
1944
1945     GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1946                   lbuf, sizeof(lbuf));
1947
1948     kbd_codepage = atoi(lbuf);
1949 }
1950
1951 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1952 {
1953     int thistime = GetMessageTime();
1954
1955     if (send_raw_mouse &&
1956         !(shift && conf_get_int(conf, CONF_mouse_override))) {
1957         lastbtn = MBT_NOTHING;
1958         term_mouse(term, b, translate_button(b), MA_CLICK,
1959                    x, y, shift, ctrl, alt);
1960         return;
1961     }
1962
1963     if (lastbtn == b && thistime - lasttime < dbltime) {
1964         lastact = (lastact == MA_CLICK ? MA_2CLK :
1965                    lastact == MA_2CLK ? MA_3CLK :
1966                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1967     } else {
1968         lastbtn = b;
1969         lastact = MA_CLICK;
1970     }
1971     if (lastact != MA_NOTHING)
1972         term_mouse(term, b, translate_button(b), lastact,
1973                    x, y, shift, ctrl, alt);
1974     lasttime = thistime;
1975 }
1976
1977 /*
1978  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1979  * into a cooked one (SELECT, EXTEND, PASTE).
1980  */
1981 static Mouse_Button translate_button(Mouse_Button button)
1982 {
1983     if (button == MBT_LEFT)
1984         return MBT_SELECT;
1985     if (button == MBT_MIDDLE)
1986         return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ?
1987         MBT_PASTE : MBT_EXTEND;
1988     if (button == MBT_RIGHT)
1989         return conf_get_int(conf, CONF_mouse_is_xterm) == 1 ?
1990         MBT_EXTEND : MBT_PASTE;
1991     return 0;                          /* shouldn't happen */
1992 }
1993
1994 static void show_mouseptr(int show)
1995 {
1996     /* NB that the counter in ShowCursor() is also frobbed by
1997      * update_mouse_pointer() */
1998     static int cursor_visible = 1;
1999     if (!conf_get_int(conf, CONF_hide_mouseptr))
2000         show = 1;                      /* override if this feature disabled */
2001     if (cursor_visible && !show)
2002         ShowCursor(FALSE);
2003     else if (!cursor_visible && show)
2004         ShowCursor(TRUE);
2005     cursor_visible = show;
2006 }
2007
2008 static int is_alt_pressed(void)
2009 {
2010     BYTE keystate[256];
2011     int r = GetKeyboardState(keystate);
2012     if (!r)
2013         return FALSE;
2014     if (keystate[VK_MENU] & 0x80)
2015         return TRUE;
2016     if (keystate[VK_RMENU] & 0x80)
2017         return TRUE;
2018     return FALSE;
2019 }
2020
2021 static int resizing;
2022
2023 void notify_remote_exit(void *fe)
2024 {
2025     int exitcode, close_on_exit;
2026
2027     if (!session_closed &&
2028         (exitcode = back->exitcode(backhandle)) >= 0) {
2029         close_on_exit = conf_get_int(conf, CONF_close_on_exit);
2030         /* Abnormal exits will already have set session_closed and taken
2031          * appropriate action. */
2032         if (close_on_exit == FORCE_ON ||
2033             (close_on_exit == AUTO && exitcode != INT_MAX)) {
2034             PostQuitMessage(0);
2035         } else {
2036             queue_toplevel_callback(close_session, NULL);
2037             session_closed = TRUE;
2038             /* exitcode == INT_MAX indicates that the connection was closed
2039              * by a fatal error, so an error box will be coming our way and
2040              * we should not generate this informational one. */
2041             if (exitcode != INT_MAX)
2042                 MessageBox(hwnd, "Connection closed by remote host",
2043                            appname, MB_OK | MB_ICONINFORMATION);
2044         }
2045     }
2046 }
2047
2048 void timer_change_notify(unsigned long next)
2049 {
2050     unsigned long now = GETTICKCOUNT();
2051     long ticks;
2052     if (now - next < INT_MAX)
2053         ticks = 0;
2054     else
2055         ticks = next - now;
2056     KillTimer(hwnd, TIMING_TIMER_ID);
2057     SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL);
2058     timing_next_time = next;
2059 }
2060
2061 static void conf_cache_data(void)
2062 {
2063     /* Cache some items from conf to speed lookups in very hot code */
2064     cursor_type = conf_get_int(conf, CONF_cursor_type);
2065     vtmode = conf_get_int(conf, CONF_vtmode);
2066 }
2067
2068 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
2069                                 WPARAM wParam, LPARAM lParam)
2070 {
2071     HDC hdc;
2072     static int ignore_clip = FALSE;
2073     static int need_backend_resize = FALSE;
2074     static int fullscr_on_max = FALSE;
2075     static int processed_resize = FALSE;
2076     static UINT last_mousemove = 0;
2077     int resize_action;
2078
2079     switch (message) {
2080       case WM_TIMER:
2081         if ((UINT_PTR)wParam == TIMING_TIMER_ID) {
2082             unsigned long next;
2083
2084             KillTimer(hwnd, TIMING_TIMER_ID);
2085             if (run_timers(timing_next_time, &next)) {
2086                 timer_change_notify(next);
2087             } else {
2088             }
2089         }
2090         return 0;
2091       case WM_CREATE:
2092         break;
2093       case WM_CLOSE:
2094         {
2095             char *str;
2096             show_mouseptr(1);
2097             str = dupprintf("%s Exit Confirmation", appname);
2098             if (session_closed || !conf_get_int(conf, CONF_warn_on_close) ||
2099                 MessageBox(hwnd,
2100                            "Are you sure you want to close this session?",
2101                            str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1)
2102                 == IDOK)
2103                 DestroyWindow(hwnd);
2104             sfree(str);
2105         }
2106         return 0;
2107       case WM_DESTROY:
2108         show_mouseptr(1);
2109         PostQuitMessage(0);
2110         return 0;
2111       case WM_INITMENUPOPUP:
2112         if ((HMENU)wParam == savedsess_menu) {
2113             /* About to pop up Saved Sessions sub-menu.
2114              * Refresh the session list. */
2115             get_sesslist(&sesslist, FALSE); /* free */
2116             get_sesslist(&sesslist, TRUE);
2117             update_savedsess_menu();
2118             return 0;
2119         }
2120         break;
2121       case WM_COMMAND:
2122       case WM_SYSCOMMAND:
2123         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
2124           case IDM_SHOWLOG:
2125             showeventlog(hwnd);
2126             break;
2127           case IDM_NEWSESS:
2128           case IDM_DUPSESS:
2129           case IDM_SAVEDSESS:
2130             {
2131                 char b[2048];
2132                 char c[30], *cl;
2133                 int freecl = FALSE;
2134                 BOOL inherit_handles;
2135                 STARTUPINFO si;
2136                 PROCESS_INFORMATION pi;
2137                 HANDLE filemap = NULL;
2138
2139                 if (wParam == IDM_DUPSESS) {
2140                     /*
2141                      * Allocate a file-mapping memory chunk for the
2142                      * config structure.
2143                      */
2144                     SECURITY_ATTRIBUTES sa;
2145                     void *p;
2146                     int size;
2147
2148                     size = conf_serialised_size(conf);
2149
2150                     sa.nLength = sizeof(sa);
2151                     sa.lpSecurityDescriptor = NULL;
2152                     sa.bInheritHandle = TRUE;
2153                     filemap = CreateFileMapping(INVALID_HANDLE_VALUE,
2154                                                 &sa,
2155                                                 PAGE_READWRITE,
2156                                                 0, size, NULL);
2157                     if (filemap && filemap != INVALID_HANDLE_VALUE) {
2158                         p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size);
2159                         if (p) {
2160                             conf_serialise(conf, p);
2161                             UnmapViewOfFile(p);
2162                         }
2163                     }
2164                     inherit_handles = TRUE;
2165                     sprintf(c, "putty &%p:%u", filemap, (unsigned)size);
2166                     cl = c;
2167                 } else if (wParam == IDM_SAVEDSESS) {
2168                     unsigned int sessno = ((lParam - IDM_SAVED_MIN)
2169                                            / MENU_SAVED_STEP) + 1;
2170                     if (sessno < (unsigned)sesslist.nsessions) {
2171                         char *session = sesslist.sessions[sessno];
2172                         cl = dupprintf("putty @%s", session);
2173                         inherit_handles = FALSE;
2174                         freecl = TRUE;
2175                     } else
2176                         break;
2177                 } else /* IDM_NEWSESS */ {
2178                     cl = NULL;
2179                     inherit_handles = FALSE;
2180                 }
2181
2182                 GetModuleFileName(NULL, b, sizeof(b) - 1);
2183                 si.cb = sizeof(si);
2184                 si.lpReserved = NULL;
2185                 si.lpDesktop = NULL;
2186                 si.lpTitle = NULL;
2187                 si.dwFlags = 0;
2188                 si.cbReserved2 = 0;
2189                 si.lpReserved2 = NULL;
2190                 CreateProcess(b, cl, NULL, NULL, inherit_handles,
2191                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
2192                 CloseHandle(pi.hProcess);
2193                 CloseHandle(pi.hThread);
2194
2195                 if (filemap)
2196                     CloseHandle(filemap);
2197                 if (freecl)
2198                     sfree(cl);
2199             }
2200             break;
2201           case IDM_RESTART:
2202             if (!back) {
2203                 logevent(NULL, "----- Session restarted -----");
2204                 term_pwron(term, FALSE);
2205                 start_backend();
2206             }
2207
2208             break;
2209           case IDM_RECONF:
2210             {
2211                 Conf *prev_conf;
2212                 int init_lvl = 1;
2213                 int reconfig_result;
2214
2215                 if (reconfiguring)
2216                     break;
2217                 else
2218                     reconfiguring = TRUE;
2219
2220                 /*
2221                  * Copy the current window title into the stored
2222                  * previous configuration, so that doing nothing to
2223                  * the window title field in the config box doesn't
2224                  * reset the title to its startup state.
2225                  */
2226                 conf_set_str(conf, CONF_wintitle, window_name);
2227
2228                 prev_conf = conf_copy(conf);
2229
2230                 reconfig_result =
2231                     do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0);
2232                 reconfiguring = FALSE;
2233                 if (!reconfig_result) {
2234                     conf_free(prev_conf);
2235                     break;
2236                 }
2237
2238                 conf_cache_data();
2239
2240                 resize_action = conf_get_int(conf, CONF_resize_action);
2241                 {
2242                     /* Disable full-screen if resizing forbidden */
2243                     int i;
2244                     for (i = 0; i < lenof(popup_menus); i++)
2245                         EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN,
2246                                        MF_BYCOMMAND | 
2247                                        (resize_action == RESIZE_DISABLED)
2248                                        ? MF_GRAYED : MF_ENABLED);
2249                     /* Gracefully unzoom if necessary */
2250                     if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED))
2251                         ShowWindow(hwnd, SW_RESTORE);
2252                 }
2253
2254                 /* Pass new config data to the logging module */
2255                 log_reconfig(logctx, conf);
2256
2257                 sfree(logpal);
2258                 /*
2259                  * Flush the line discipline's edit buffer in the
2260                  * case where local editing has just been disabled.
2261                  */
2262                 if (ldisc) {
2263                     ldisc_configure(ldisc, conf);
2264                     ldisc_send(ldisc, NULL, 0, 0);
2265                 }
2266                 if (pal)
2267                     DeleteObject(pal);
2268                 logpal = NULL;
2269                 pal = NULL;
2270                 conftopalette();
2271                 init_palette();
2272
2273                 /* Pass new config data to the terminal */
2274                 term_reconfig(term, conf);
2275
2276                 /* Pass new config data to the back end */
2277                 if (back)
2278                     back->reconfig(backhandle, conf);
2279
2280                 /* Screen size changed ? */
2281                 if (conf_get_int(conf, CONF_height) !=
2282                     conf_get_int(prev_conf, CONF_height) ||
2283                     conf_get_int(conf, CONF_width) !=
2284                     conf_get_int(prev_conf, CONF_width) ||
2285                     conf_get_int(conf, CONF_savelines) !=
2286                     conf_get_int(prev_conf, CONF_savelines) ||
2287                     resize_action == RESIZE_FONT ||
2288                     (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
2289                     resize_action == RESIZE_DISABLED)
2290                     term_size(term, conf_get_int(conf, CONF_height),
2291                               conf_get_int(conf, CONF_width),
2292                               conf_get_int(conf, CONF_savelines));
2293
2294                 /* Enable or disable the scroll bar, etc */
2295                 {
2296                     LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE);
2297                     LONG nexflag, exflag =
2298                         GetWindowLongPtr(hwnd, GWL_EXSTYLE);
2299
2300                     nexflag = exflag;
2301                     if (conf_get_int(conf, CONF_alwaysontop) !=
2302                         conf_get_int(prev_conf, CONF_alwaysontop)) {
2303                         if (conf_get_int(conf, CONF_alwaysontop)) {
2304                             nexflag |= WS_EX_TOPMOST;
2305                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
2306                                          SWP_NOMOVE | SWP_NOSIZE);
2307                         } else {
2308                             nexflag &= ~(WS_EX_TOPMOST);
2309                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
2310                                          SWP_NOMOVE | SWP_NOSIZE);
2311                         }
2312                     }
2313                     if (conf_get_int(conf, CONF_sunken_edge))
2314                         nexflag |= WS_EX_CLIENTEDGE;
2315                     else
2316                         nexflag &= ~(WS_EX_CLIENTEDGE);
2317
2318                     nflg = flag;
2319                     if (conf_get_int(conf, is_full_screen() ?
2320                                      CONF_scrollbar_in_fullscreen :
2321                                      CONF_scrollbar))
2322                         nflg |= WS_VSCROLL;
2323                     else
2324                         nflg &= ~WS_VSCROLL;
2325
2326                     if (resize_action == RESIZE_DISABLED ||
2327                         is_full_screen())
2328                         nflg &= ~WS_THICKFRAME;
2329                     else
2330                         nflg |= WS_THICKFRAME;
2331
2332                     if (resize_action == RESIZE_DISABLED)
2333                         nflg &= ~WS_MAXIMIZEBOX;
2334                     else
2335                         nflg |= WS_MAXIMIZEBOX;
2336
2337                     if (nflg != flag || nexflag != exflag) {
2338                         if (nflg != flag)
2339                             SetWindowLongPtr(hwnd, GWL_STYLE, nflg);
2340                         if (nexflag != exflag)
2341                             SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag);
2342
2343                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
2344                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
2345                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
2346                                      SWP_FRAMECHANGED);
2347
2348                         init_lvl = 2;
2349                     }
2350                 }
2351
2352                 /* Oops */
2353                 if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
2354                     force_normal(hwnd);
2355                     init_lvl = 2;
2356                 }
2357
2358                 set_title(NULL, conf_get_str(conf, CONF_wintitle));
2359                 if (IsIconic(hwnd)) {
2360                     SetWindowText(hwnd,
2361                                   conf_get_int(conf, CONF_win_name_always) ?
2362                                   window_name : icon_name);
2363                 }
2364
2365                 {
2366                     FontSpec *font = conf_get_fontspec(conf, CONF_font);
2367                     FontSpec *prev_font = conf_get_fontspec(prev_conf,
2368                                                              CONF_font);
2369
2370                     if (!strcmp(font->name, prev_font->name) ||
2371                         !strcmp(conf_get_str(conf, CONF_line_codepage),
2372                                 conf_get_str(prev_conf, CONF_line_codepage)) ||
2373                         font->isbold != prev_font->isbold ||
2374                         font->height != prev_font->height ||
2375                         font->charset != prev_font->charset ||
2376                         conf_get_int(conf, CONF_font_quality) !=
2377                         conf_get_int(prev_conf, CONF_font_quality) ||
2378                         conf_get_int(conf, CONF_vtmode) !=
2379                         conf_get_int(prev_conf, CONF_vtmode) ||
2380                         conf_get_int(conf, CONF_bold_style) !=
2381                         conf_get_int(prev_conf, CONF_bold_style) ||
2382                         resize_action == RESIZE_DISABLED ||
2383                         resize_action == RESIZE_EITHER ||
2384                         resize_action != conf_get_int(prev_conf,
2385                                                       CONF_resize_action))
2386                         init_lvl = 2;
2387                 }
2388
2389                 InvalidateRect(hwnd, NULL, TRUE);
2390                 reset_window(init_lvl);
2391
2392                 conf_free(prev_conf);
2393             }
2394             break;
2395           case IDM_COPYALL:
2396             term_copyall(term);
2397             break;
2398           case IDM_PASTE:
2399             request_paste(NULL);
2400             break;
2401           case IDM_CLRSB:
2402             term_clrsb(term);
2403             break;
2404           case IDM_RESET:
2405             term_pwron(term, TRUE);
2406             if (ldisc)
2407                 ldisc_send(ldisc, NULL, 0, 0);
2408             break;
2409           case IDM_ABOUT:
2410             showabout(hwnd);
2411             break;
2412           case IDM_HELP:
2413             launch_help(hwnd, NULL);
2414             break;
2415           case SC_MOUSEMENU:
2416             /*
2417              * We get this if the System menu has been activated
2418              * using the mouse.
2419              */
2420             show_mouseptr(1);
2421             break;
2422           case SC_KEYMENU:
2423             /*
2424              * We get this if the System menu has been activated
2425              * using the keyboard. This might happen from within
2426              * TranslateKey, in which case it really wants to be
2427              * followed by a `space' character to actually _bring
2428              * the menu up_ rather than just sitting there in
2429              * `ready to appear' state.
2430              */
2431             show_mouseptr(1);          /* make sure pointer is visible */
2432             if( lParam == 0 )
2433                 PostMessage(hwnd, WM_CHAR, ' ', 0);
2434             break;
2435           case IDM_FULLSCREEN:
2436             flip_full_screen();
2437             break;
2438           default:
2439             if (wParam >= IDM_SAVED_MIN && wParam < IDM_SAVED_MAX) {
2440                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
2441             }
2442             if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
2443                 int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
2444                 /*
2445                  * Ensure we haven't been sent a bogus SYSCOMMAND
2446                  * which would cause us to reference invalid memory
2447                  * and crash. Perhaps I'm just too paranoid here.
2448                  */
2449                 if (i >= n_specials)
2450                     break;
2451                 if (back)
2452                     back->special(backhandle, specials[i].code);
2453             }
2454         }
2455         break;
2456
2457 #define X_POS(l) ((int)(short)LOWORD(l))
2458 #define Y_POS(l) ((int)(short)HIWORD(l))
2459
2460 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2461 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
2462       case WM_LBUTTONDOWN:
2463       case WM_MBUTTONDOWN:
2464       case WM_RBUTTONDOWN:
2465       case WM_LBUTTONUP:
2466       case WM_MBUTTONUP:
2467       case WM_RBUTTONUP:
2468         if (message == WM_RBUTTONDOWN &&
2469             ((wParam & MK_CONTROL) ||
2470              (conf_get_int(conf, CONF_mouse_is_xterm) == 2))) {
2471             POINT cursorpos;
2472
2473             show_mouseptr(1);          /* make sure pointer is visible */
2474             GetCursorPos(&cursorpos);
2475             TrackPopupMenu(popup_menus[CTXMENU].menu,
2476                            TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
2477                            cursorpos.x, cursorpos.y,
2478                            0, hwnd, NULL);
2479             break;
2480         }
2481         {
2482             int button, press;
2483
2484             switch (message) {
2485               case WM_LBUTTONDOWN:
2486                 button = MBT_LEFT;
2487                 wParam |= MK_LBUTTON;
2488                 press = 1;
2489                 break;
2490               case WM_MBUTTONDOWN:
2491                 button = MBT_MIDDLE;
2492                 wParam |= MK_MBUTTON;
2493                 press = 1;
2494                 break;
2495               case WM_RBUTTONDOWN:
2496                 button = MBT_RIGHT;
2497                 wParam |= MK_RBUTTON;
2498                 press = 1;
2499                 break;
2500               case WM_LBUTTONUP:
2501                 button = MBT_LEFT;
2502                 wParam &= ~MK_LBUTTON;
2503                 press = 0;
2504                 break;
2505               case WM_MBUTTONUP:
2506                 button = MBT_MIDDLE;
2507                 wParam &= ~MK_MBUTTON;
2508                 press = 0;
2509                 break;
2510               case WM_RBUTTONUP:
2511                 button = MBT_RIGHT;
2512                 wParam &= ~MK_RBUTTON;
2513                 press = 0;
2514                 break;
2515               default:
2516                 button = press = 0;    /* shouldn't happen */
2517             }
2518             show_mouseptr(1);
2519             /*
2520              * Special case: in full-screen mode, if the left
2521              * button is clicked in the very top left corner of the
2522              * window, we put up the System menu instead of doing
2523              * selection.
2524              */
2525             {
2526                 char mouse_on_hotspot = 0;
2527                 POINT pt;
2528
2529                 GetCursorPos(&pt);
2530 #ifndef NO_MULTIMON
2531                 {
2532                     HMONITOR mon;
2533                     MONITORINFO mi;
2534
2535                     mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
2536
2537                     if (mon != NULL) {
2538                         mi.cbSize = sizeof(MONITORINFO);
2539                         GetMonitorInfo(mon, &mi);
2540
2541                         if (mi.rcMonitor.left == pt.x &&
2542                             mi.rcMonitor.top == pt.y) {
2543                             mouse_on_hotspot = 1;
2544                         }
2545                     }
2546                 }
2547 #else
2548                 if (pt.x == 0 && pt.y == 0) {
2549                     mouse_on_hotspot = 1;
2550                 }
2551 #endif
2552                 if (is_full_screen() && press &&
2553                     button == MBT_LEFT && mouse_on_hotspot) {
2554                     SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU,
2555                                 MAKELPARAM(pt.x, pt.y));
2556                     return 0;
2557                 }
2558             }
2559
2560             if (press) {
2561                 click(button,
2562                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2563                       wParam & MK_SHIFT, wParam & MK_CONTROL,
2564                       is_alt_pressed());
2565                 SetCapture(hwnd);
2566             } else {
2567                 term_mouse(term, button, translate_button(button), MA_RELEASE,
2568                            TO_CHR_X(X_POS(lParam)),
2569                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2570                            wParam & MK_CONTROL, is_alt_pressed());
2571                 if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)))
2572                     ReleaseCapture();
2573             }
2574         }
2575         return 0;
2576       case WM_MOUSEMOVE:
2577         {
2578             /*
2579              * Windows seems to like to occasionally send MOUSEMOVE
2580              * events even if the mouse hasn't moved. Don't unhide
2581              * the mouse pointer in this case.
2582              */
2583             static WPARAM wp = 0;
2584             static LPARAM lp = 0;
2585             if (wParam != wp || lParam != lp ||
2586                 last_mousemove != WM_MOUSEMOVE) {
2587                 show_mouseptr(1);
2588                 wp = wParam; lp = lParam;
2589                 last_mousemove = WM_MOUSEMOVE;
2590             }
2591         }
2592         /*
2593          * Add the mouse position and message time to the random
2594          * number noise.
2595          */
2596         noise_ultralight(lParam);
2597
2598         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2599             GetCapture() == hwnd) {
2600             Mouse_Button b;
2601             if (wParam & MK_LBUTTON)
2602                 b = MBT_LEFT;
2603             else if (wParam & MK_MBUTTON)
2604                 b = MBT_MIDDLE;
2605             else
2606                 b = MBT_RIGHT;
2607             term_mouse(term, b, translate_button(b), MA_DRAG,
2608                        TO_CHR_X(X_POS(lParam)),
2609                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2610                        wParam & MK_CONTROL, is_alt_pressed());
2611         }
2612         return 0;
2613       case WM_NCMOUSEMOVE:
2614         {
2615             static WPARAM wp = 0;
2616             static LPARAM lp = 0;
2617             if (wParam != wp || lParam != lp ||
2618                 last_mousemove != WM_NCMOUSEMOVE) {
2619                 show_mouseptr(1);
2620                 wp = wParam; lp = lParam;
2621                 last_mousemove = WM_NCMOUSEMOVE;
2622             }
2623         }
2624         noise_ultralight(lParam);
2625         break;
2626       case WM_IGNORE_CLIP:
2627         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
2628         break;
2629       case WM_DESTROYCLIPBOARD:
2630         if (!ignore_clip)
2631             term_deselect(term);
2632         ignore_clip = FALSE;
2633         return 0;
2634       case WM_PAINT:
2635         {
2636             PAINTSTRUCT p;
2637
2638             HideCaret(hwnd);
2639             hdc = BeginPaint(hwnd, &p);
2640             if (pal) {
2641                 SelectPalette(hdc, pal, TRUE);
2642                 RealizePalette(hdc);
2643             }
2644
2645             /*
2646              * We have to be careful about term_paint(). It will
2647              * set a bunch of character cells to INVALID and then
2648              * call do_paint(), which will redraw those cells and
2649              * _then mark them as done_. This may not be accurate:
2650              * when painting in WM_PAINT context we are restricted
2651              * to the rectangle which has just been exposed - so if
2652              * that only covers _part_ of a character cell and the
2653              * rest of it was already visible, that remainder will
2654              * not be redrawn at all. Accordingly, we must not
2655              * paint any character cell in a WM_PAINT context which
2656              * already has a pending update due to terminal output.
2657              * The simplest solution to this - and many, many
2658              * thanks to Hung-Te Lin for working all this out - is
2659              * not to do any actual painting at _all_ if there's a
2660              * pending terminal update: just mark the relevant
2661              * character cells as INVALID and wait for the
2662              * scheduled full update to sort it out.
2663              * 
2664              * I have a suspicion this isn't the _right_ solution.
2665              * An alternative approach would be to have terminal.c
2666              * separately track what _should_ be on the terminal
2667              * screen and what _is_ on the terminal screen, and
2668              * have two completely different types of redraw (one
2669              * for full updates, which syncs the former with the
2670              * terminal itself, and one for WM_PAINT which syncs
2671              * the latter with the former); yet another possibility
2672              * would be to have the Windows front end do what the
2673              * GTK one already does, and maintain a bitmap of the
2674              * current terminal appearance so that WM_PAINT becomes
2675              * completely trivial. However, this should do for now.
2676              */
2677             term_paint(term, hdc, 
2678                        (p.rcPaint.left-offset_width)/font_width,
2679                        (p.rcPaint.top-offset_height)/font_height,
2680                        (p.rcPaint.right-offset_width-1)/font_width,
2681                        (p.rcPaint.bottom-offset_height-1)/font_height,
2682                        !term->window_update_pending);
2683
2684             if (p.fErase ||
2685                 p.rcPaint.left  < offset_width  ||
2686                 p.rcPaint.top   < offset_height ||
2687                 p.rcPaint.right >= offset_width + font_width*term->cols ||
2688                 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2689             {
2690                 HBRUSH fillcolour, oldbrush;
2691                 HPEN   edge, oldpen;
2692                 fillcolour = CreateSolidBrush (
2693                                     colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
2694                 oldbrush = SelectObject(hdc, fillcolour);
2695                 edge = CreatePen(PS_SOLID, 0, 
2696                                     colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
2697                 oldpen = SelectObject(hdc, edge);
2698
2699                 /*
2700                  * Jordan Russell reports that this apparently
2701                  * ineffectual IntersectClipRect() call masks a
2702                  * Windows NT/2K bug causing strange display
2703                  * problems when the PuTTY window is taller than
2704                  * the primary monitor. It seems harmless enough...
2705                  */
2706                 IntersectClipRect(hdc,
2707                         p.rcPaint.left, p.rcPaint.top,
2708                         p.rcPaint.right, p.rcPaint.bottom);
2709
2710                 ExcludeClipRect(hdc, 
2711                         offset_width, offset_height,
2712                         offset_width+font_width*term->cols,
2713                         offset_height+font_height*term->rows);
2714
2715                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
2716                           p.rcPaint.right, p.rcPaint.bottom);
2717
2718                 /* SelectClipRgn(hdc, NULL); */
2719
2720                 SelectObject(hdc, oldbrush);
2721                 DeleteObject(fillcolour);
2722                 SelectObject(hdc, oldpen);
2723                 DeleteObject(edge);
2724             }
2725             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2726             SelectObject(hdc, GetStockObject(WHITE_PEN));
2727             EndPaint(hwnd, &p);
2728             ShowCaret(hwnd);
2729         }
2730         return 0;
2731       case WM_NETEVENT:
2732         {
2733             /*
2734              * To protect against re-entrancy when Windows's recv()
2735              * immediately triggers a new WSAAsyncSelect window
2736              * message, we don't call select_result directly from this
2737              * handler but instead wait until we're back out at the
2738              * top level of the message loop.
2739              */
2740             struct wm_netevent_params *params =
2741                 snew(struct wm_netevent_params);
2742             params->wParam = wParam;
2743             params->lParam = lParam;
2744             queue_toplevel_callback(wm_netevent_callback, params);
2745         }
2746         return 0;
2747       case WM_SETFOCUS:
2748         term_set_focus(term, TRUE);
2749         CreateCaret(hwnd, caretbm, font_width, font_height);
2750         ShowCaret(hwnd);
2751         flash_window(0);               /* stop */
2752         compose_state = 0;
2753         term_update(term);
2754         break;
2755       case WM_KILLFOCUS:
2756         show_mouseptr(1);
2757         term_set_focus(term, FALSE);
2758         DestroyCaret();
2759         caret_x = caret_y = -1;        /* ensure caret is replaced next time */
2760         term_update(term);
2761         break;
2762       case WM_ENTERSIZEMOVE:
2763 #ifdef RDB_DEBUG_PATCH
2764         debug((27, "WM_ENTERSIZEMOVE"));
2765 #endif
2766         EnableSizeTip(1);
2767         resizing = TRUE;
2768         need_backend_resize = FALSE;
2769         break;
2770       case WM_EXITSIZEMOVE:
2771         EnableSizeTip(0);
2772         resizing = FALSE;
2773 #ifdef RDB_DEBUG_PATCH
2774         debug((27, "WM_EXITSIZEMOVE"));
2775 #endif
2776         if (need_backend_resize) {
2777             term_size(term, conf_get_int(conf, CONF_height),
2778                       conf_get_int(conf, CONF_width),
2779                       conf_get_int(conf, CONF_savelines));
2780             InvalidateRect(hwnd, NULL, TRUE);
2781         }
2782         break;
2783       case WM_SIZING:
2784         /*
2785          * This does two jobs:
2786          * 1) Keep the sizetip uptodate
2787          * 2) Make sure the window size is _stepped_ in units of the font size.
2788          */
2789         resize_action = conf_get_int(conf, CONF_resize_action);
2790         if (resize_action == RESIZE_TERM ||
2791             (resize_action == RESIZE_EITHER && !is_alt_pressed())) {
2792             int width, height, w, h, ew, eh;
2793             LPRECT r = (LPRECT) lParam;
2794
2795             if (!need_backend_resize && resize_action == RESIZE_EITHER &&
2796                 (conf_get_int(conf, CONF_height) != term->rows ||
2797                  conf_get_int(conf, CONF_width) != term->cols)) {
2798                 /* 
2799                  * Great! It seems that both the terminal size and the
2800                  * font size have been changed and the user is now dragging.
2801                  * 
2802                  * It will now be difficult to get back to the configured
2803                  * font size!
2804                  *
2805                  * This would be easier but it seems to be too confusing.
2806                  */
2807                 conf_set_int(conf, CONF_height, term->rows);
2808                 conf_set_int(conf, CONF_width, term->cols);
2809
2810                 InvalidateRect(hwnd, NULL, TRUE);
2811                 need_backend_resize = TRUE;
2812             }
2813
2814             width = r->right - r->left - extra_width;
2815             height = r->bottom - r->top - extra_height;
2816             w = (width + font_width / 2) / font_width;
2817             if (w < 1)
2818                 w = 1;
2819             h = (height + font_height / 2) / font_height;
2820             if (h < 1)
2821                 h = 1;
2822             UpdateSizeTip(hwnd, w, h);
2823             ew = width - w * font_width;
2824             eh = height - h * font_height;
2825             if (ew != 0) {
2826                 if (wParam == WMSZ_LEFT ||
2827                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2828                     r->left += ew;
2829                 else
2830                     r->right -= ew;
2831             }
2832             if (eh != 0) {
2833                 if (wParam == WMSZ_TOP ||
2834                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2835                     r->top += eh;
2836                 else
2837                     r->bottom -= eh;
2838             }
2839             if (ew || eh)
2840                 return 1;
2841             else
2842                 return 0;
2843         } else {
2844             int width, height, w, h, rv = 0;
2845             int window_border = conf_get_int(conf, CONF_window_border);
2846             int ex_width = extra_width + (window_border - offset_width) * 2;
2847             int ex_height = extra_height + (window_border - offset_height) * 2;
2848             LPRECT r = (LPRECT) lParam;
2849
2850             width = r->right - r->left - ex_width;
2851             height = r->bottom - r->top - ex_height;
2852
2853             w = (width + term->cols/2)/term->cols;
2854             h = (height + term->rows/2)/term->rows;
2855             if ( r->right != r->left + w*term->cols + ex_width)
2856                 rv = 1;
2857
2858             if (wParam == WMSZ_LEFT ||
2859                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2860                 r->left = r->right - w*term->cols - ex_width;
2861             else
2862                 r->right = r->left + w*term->cols + ex_width;
2863
2864             if (r->bottom != r->top + h*term->rows + ex_height)
2865                 rv = 1;
2866
2867             if (wParam == WMSZ_TOP ||
2868                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2869                 r->top = r->bottom - h*term->rows - ex_height;
2870             else
2871                 r->bottom = r->top + h*term->rows + ex_height;
2872
2873             return rv;
2874         }
2875         /* break;  (never reached) */
2876       case WM_FULLSCR_ON_MAX:
2877         fullscr_on_max = TRUE;
2878         break;
2879       case WM_MOVE:
2880         sys_cursor_update();
2881         break;
2882       case WM_SIZE:
2883         resize_action = conf_get_int(conf, CONF_resize_action);
2884 #ifdef RDB_DEBUG_PATCH
2885         debug((27, "WM_SIZE %s (%d,%d)",
2886                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2887                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2888                 (wParam == SIZE_RESTORED && resizing) ? "to":
2889                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2890                 "...",
2891             LOWORD(lParam), HIWORD(lParam)));
2892 #endif
2893         if (wParam == SIZE_MINIMIZED)
2894             SetWindowText(hwnd,
2895                           conf_get_int(conf, CONF_win_name_always) ?
2896                           window_name : icon_name);
2897         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2898             SetWindowText(hwnd, window_name);
2899         if (wParam == SIZE_RESTORED) {
2900             processed_resize = FALSE;
2901             clear_full_screen();
2902             if (processed_resize) {
2903                 /*
2904                  * Inhibit normal processing of this WM_SIZE; a
2905                  * secondary one was triggered just now by
2906                  * clear_full_screen which contained the correct
2907                  * client area size.
2908                  */
2909                 return 0;
2910             }
2911         }
2912         if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2913             fullscr_on_max = FALSE;
2914             processed_resize = FALSE;
2915             make_full_screen();
2916             if (processed_resize) {
2917                 /*
2918                  * Inhibit normal processing of this WM_SIZE; a
2919                  * secondary one was triggered just now by
2920                  * make_full_screen which contained the correct client
2921                  * area size.
2922                  */
2923                 return 0;
2924             }
2925         }
2926
2927         processed_resize = TRUE;
2928
2929         if (resize_action == RESIZE_DISABLED) {
2930             /* A resize, well it better be a minimize. */
2931             reset_window(-1);
2932         } else {
2933
2934             int width, height, w, h;
2935             int window_border = conf_get_int(conf, CONF_window_border);
2936
2937             width = LOWORD(lParam);
2938             height = HIWORD(lParam);
2939
2940             if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2941                 was_zoomed = 1;
2942                 prev_rows = term->rows;
2943                 prev_cols = term->cols;
2944                 if (resize_action == RESIZE_TERM) {
2945                     w = width / font_width;
2946                     if (w < 1) w = 1;
2947                     h = height / font_height;
2948                     if (h < 1) h = 1;
2949
2950                     if (resizing) {
2951                         /*
2952                          * As below, if we're in the middle of an
2953                          * interactive resize we don't call
2954                          * back->size. In Windows 7, this case can
2955                          * arise in maximisation as well via the Aero
2956                          * snap UI.
2957                          */
2958                         need_backend_resize = TRUE;
2959                         conf_set_int(conf, CONF_height, h);
2960                         conf_set_int(conf, CONF_width, w);
2961                     } else {
2962                         term_size(term, h, w,
2963                                   conf_get_int(conf, CONF_savelines));
2964                     }
2965                 }
2966                 reset_window(0);
2967             } else if (wParam == SIZE_RESTORED && was_zoomed) {
2968                 was_zoomed = 0;
2969                 if (resize_action == RESIZE_TERM) {
2970                     w = (width-window_border*2) / font_width;
2971                     if (w < 1) w = 1;
2972                     h = (height-window_border*2) / font_height;
2973                     if (h < 1) h = 1;
2974                     term_size(term, h, w, conf_get_int(conf, CONF_savelines));
2975                     reset_window(2);
2976                 } else if (resize_action != RESIZE_FONT)
2977                     reset_window(2);
2978                 else
2979                     reset_window(0);
2980             } else if (wParam == SIZE_MINIMIZED) {
2981                 /* do nothing */
2982             } else if (resize_action == RESIZE_TERM ||
2983                        (resize_action == RESIZE_EITHER &&
2984                         !is_alt_pressed())) {
2985                 w = (width-window_border*2) / font_width;
2986                 if (w < 1) w = 1;
2987                 h = (height-window_border*2) / font_height;
2988                 if (h < 1) h = 1;
2989
2990                 if (resizing) {
2991                     /*
2992                      * Don't call back->size in mid-resize. (To
2993                      * prevent massive numbers of resize events
2994                      * getting sent down the connection during an NT
2995                      * opaque drag.)
2996                      */
2997                     need_backend_resize = TRUE;
2998                     conf_set_int(conf, CONF_height, h);
2999                     conf_set_int(conf, CONF_width, w);
3000                 } else {
3001                     term_size(term, h, w, conf_get_int(conf, CONF_savelines));
3002                 }
3003             } else {
3004                 reset_window(0);
3005             }
3006         }
3007         sys_cursor_update();
3008         return 0;
3009       case WM_VSCROLL:
3010         switch (LOWORD(wParam)) {
3011           case SB_BOTTOM:
3012             term_scroll(term, -1, 0);
3013             break;
3014           case SB_TOP:
3015             term_scroll(term, +1, 0);
3016             break;
3017           case SB_LINEDOWN:
3018             term_scroll(term, 0, +1);
3019             break;
3020           case SB_LINEUP:
3021             term_scroll(term, 0, -1);
3022             break;
3023           case SB_PAGEDOWN:
3024             term_scroll(term, 0, +term->rows / 2);
3025             break;
3026           case SB_PAGEUP:
3027             term_scroll(term, 0, -term->rows / 2);
3028             break;
3029           case SB_THUMBPOSITION:
3030           case SB_THUMBTRACK:
3031             /*
3032              * Use GetScrollInfo instead of HIWORD(wParam) to get
3033              * 32-bit scroll position.
3034              */
3035             {
3036                 SCROLLINFO si;
3037
3038                 si.cbSize = sizeof(si);
3039                 si.fMask = SIF_TRACKPOS;
3040                 if (GetScrollInfo(hwnd, SB_VERT, &si) == 0)
3041                     si.nTrackPos = HIWORD(wParam);
3042                 term_scroll(term, 1, si.nTrackPos);
3043             }
3044             break;
3045         }
3046         break;
3047       case WM_PALETTECHANGED:
3048         if ((HWND) wParam != hwnd && pal != NULL) {
3049             HDC hdc = get_ctx(NULL);
3050             if (hdc) {
3051                 if (RealizePalette(hdc) > 0)
3052                     UpdateColors(hdc);
3053                 free_ctx(hdc);
3054             }
3055         }
3056         break;
3057       case WM_QUERYNEWPALETTE:
3058         if (pal != NULL) {
3059             HDC hdc = get_ctx(NULL);
3060             if (hdc) {
3061                 if (RealizePalette(hdc) > 0)
3062                     UpdateColors(hdc);
3063                 free_ctx(hdc);
3064                 return TRUE;
3065             }
3066         }
3067         return FALSE;
3068       case WM_KEYDOWN:
3069       case WM_SYSKEYDOWN:
3070       case WM_KEYUP:
3071       case WM_SYSKEYUP:
3072         /*
3073          * Add the scan code and keypress timing to the random
3074          * number noise.
3075          */
3076         noise_ultralight(lParam);
3077
3078         /*
3079          * We don't do TranslateMessage since it disassociates the
3080          * resulting CHAR message from the KEYDOWN that sparked it,
3081          * which we occasionally don't want. Instead, we process
3082          * KEYDOWN, and call the Win32 translator functions so that
3083          * we get the translations under _our_ control.
3084          */
3085         {
3086             unsigned char buf[20];
3087             int len;
3088
3089             if (wParam == VK_PROCESSKEY) { /* IME PROCESS key */
3090                 if (message == WM_KEYDOWN) {
3091                     MSG m;
3092                     m.hwnd = hwnd;
3093                     m.message = WM_KEYDOWN;
3094                     m.wParam = wParam;
3095                     m.lParam = lParam & 0xdfff;
3096                     TranslateMessage(&m);
3097                 } else break; /* pass to Windows for default processing */
3098             } else {
3099                 len = TranslateKey(message, wParam, lParam, buf);
3100                 if (len == -1)
3101                     return DefWindowProcW(hwnd, message, wParam, lParam);
3102
3103                 if (len != 0) {
3104                     /*
3105                      * We need not bother about stdin backlogs
3106                      * here, because in GUI PuTTY we can't do
3107                      * anything about it anyway; there's no means
3108                      * of asking Windows to hold off on KEYDOWN
3109                      * messages. We _have_ to buffer everything
3110                      * we're sent.
3111                      */
3112                     term_seen_key_event(term);
3113                     if (ldisc)
3114                         ldisc_send(ldisc, buf, len, 1);
3115                     show_mouseptr(0);
3116                 }
3117             }
3118         }
3119         return 0;
3120       case WM_INPUTLANGCHANGE:
3121         /* wParam == Font number */
3122         /* lParam == Locale */
3123         set_input_locale((HKL)lParam);
3124         sys_cursor_update();
3125         break;
3126       case WM_IME_STARTCOMPOSITION:
3127         {
3128             HIMC hImc = ImmGetContext(hwnd);
3129             ImmSetCompositionFont(hImc, &lfont);
3130             ImmReleaseContext(hwnd, hImc);
3131         }
3132         break;
3133       case WM_IME_COMPOSITION:
3134         {
3135             HIMC hIMC;
3136             int n;
3137             char *buff;
3138
3139             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
3140                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
3141
3142             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
3143                 break; /* fall back to DefWindowProc */
3144
3145             hIMC = ImmGetContext(hwnd);
3146             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
3147
3148             if (n > 0) {
3149                 int i;
3150                 buff = snewn(n, char);
3151                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
3152                 /*
3153                  * Jaeyoun Chung reports that Korean character
3154                  * input doesn't work correctly if we do a single
3155                  * luni_send() covering the whole of buff. So
3156                  * instead we luni_send the characters one by one.
3157                  */
3158                 term_seen_key_event(term);
3159                 /* don't divide SURROGATE PAIR */
3160                 if (ldisc) {
3161                     for (i = 0; i < n; i += 2) {
3162                         WCHAR hs = *(unsigned short *)(buff+i);
3163                         if (IS_HIGH_SURROGATE(hs) && i+2 < n) {
3164                             WCHAR ls = *(unsigned short *)(buff+i+2);
3165                             if (IS_LOW_SURROGATE(ls)) {
3166                                 luni_send(ldisc, (unsigned short *)(buff+i), 2, 1);
3167                                 i += 2;
3168                                 continue;
3169                             }
3170                         }
3171                         luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
3172                     }
3173                 }
3174                 free(buff);
3175             }
3176             ImmReleaseContext(hwnd, hIMC);
3177             return 1;
3178         }
3179
3180       case WM_IME_CHAR:
3181         if (wParam & 0xFF00) {
3182             unsigned char buf[2];
3183
3184             buf[1] = wParam;
3185             buf[0] = wParam >> 8;
3186             term_seen_key_event(term);
3187             if (ldisc)
3188                 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
3189         } else {
3190             char c = (unsigned char) wParam;
3191             term_seen_key_event(term);
3192             if (ldisc)
3193                 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
3194         }
3195         return (0);
3196       case WM_CHAR:
3197       case WM_SYSCHAR:
3198         /*
3199          * Nevertheless, we are prepared to deal with WM_CHAR
3200          * messages, should they crop up. So if someone wants to
3201          * post the things to us as part of a macro manoeuvre,
3202          * we're ready to cope.
3203          */
3204         {
3205             static wchar_t pending_surrogate = 0;
3206             wchar_t c = wParam;
3207
3208             if (IS_HIGH_SURROGATE(c)) {
3209                 pending_surrogate = c;
3210             } else if (IS_SURROGATE_PAIR(pending_surrogate, c)) {
3211                 wchar_t pair[2];
3212                 pair[0] = pending_surrogate;
3213                 pair[1] = c;
3214                 term_seen_key_event(term);
3215                 luni_send(ldisc, pair, 2, 1);
3216             } else if (!IS_SURROGATE(c)) {
3217                 term_seen_key_event(term);
3218                 luni_send(ldisc, &c, 1, 1);
3219             }
3220         }
3221         return 0;
3222       case WM_SYSCOLORCHANGE:
3223         if (conf_get_int(conf, CONF_system_colour)) {
3224             /* Refresh palette from system colours. */
3225             /* XXX actually this zaps the entire palette. */
3226             systopalette();
3227             init_palette();
3228             /* Force a repaint of the terminal window. */
3229             term_invalidate(term);
3230         }
3231         break;
3232       case WM_AGENT_CALLBACK:
3233         {
3234             struct agent_callback *c = (struct agent_callback *)lParam;
3235             c->callback(c->callback_ctx, c->data, c->len);
3236             sfree(c);
3237         }
3238         return 0;
3239       case WM_GOT_CLIPDATA:
3240         if (process_clipdata((HGLOBAL)lParam, wParam))
3241             term_do_paste(term);
3242         return 0;
3243       default:
3244         if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
3245             int shift_pressed=0, control_pressed=0;
3246
3247             if (message == WM_MOUSEWHEEL) {
3248                 wheel_accumulator += (short)HIWORD(wParam);
3249                 shift_pressed=LOWORD(wParam) & MK_SHIFT;
3250                 control_pressed=LOWORD(wParam) & MK_CONTROL;
3251             } else {
3252                 BYTE keys[256];
3253                 wheel_accumulator += (int)wParam;
3254                 if (GetKeyboardState(keys)!=0) {
3255                     shift_pressed=keys[VK_SHIFT]&0x80;
3256                     control_pressed=keys[VK_CONTROL]&0x80;
3257                 }
3258             }
3259
3260             /* process events when the threshold is reached */
3261             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
3262                 int b;
3263
3264                 /* reduce amount for next time */
3265                 if (wheel_accumulator > 0) {
3266                     b = MBT_WHEEL_UP;
3267                     wheel_accumulator -= WHEEL_DELTA;
3268                 } else if (wheel_accumulator < 0) {
3269                     b = MBT_WHEEL_DOWN;
3270                     wheel_accumulator += WHEEL_DELTA;
3271                 } else
3272                     break;
3273
3274                 if (send_raw_mouse &&
3275                     !(conf_get_int(conf, CONF_mouse_override) &&
3276                       shift_pressed)) {
3277                     /* Mouse wheel position is in screen coordinates for
3278                      * some reason */
3279                     POINT p;
3280                     p.x = X_POS(lParam); p.y = Y_POS(lParam);
3281                     if (ScreenToClient(hwnd, &p)) {
3282                         /* send a mouse-down followed by a mouse up */
3283                         term_mouse(term, b, translate_button(b),
3284                                    MA_CLICK,
3285                                    TO_CHR_X(p.x),
3286                                    TO_CHR_Y(p.y), shift_pressed,
3287                                    control_pressed, is_alt_pressed());
3288                     } /* else: not sure when this can fail */
3289                 } else {
3290                     /* trigger a scroll */
3291                     term_scroll(term, 0,
3292                                 b == MBT_WHEEL_UP ?
3293                                 -term->rows / 2 : term->rows / 2);
3294                 }
3295             }
3296             return 0;
3297         }
3298     }
3299
3300     /*
3301      * Any messages we don't process completely above are passed through to
3302      * DefWindowProc() for default processing.
3303      */
3304     return DefWindowProcW(hwnd, message, wParam, lParam);
3305 }
3306
3307 /*
3308  * Move the system caret. (We maintain one, even though it's
3309  * invisible, for the benefit of blind people: apparently some
3310  * helper software tracks the system caret, so we should arrange to
3311  * have one.)
3312  */
3313 void sys_cursor(void *frontend, int x, int y)
3314 {
3315     int cx, cy;
3316
3317     if (!term->has_focus) return;
3318
3319     /*
3320      * Avoid gratuitously re-updating the cursor position and IMM
3321      * window if there's no actual change required.
3322      */
3323     cx = x * font_width + offset_width;
3324     cy = y * font_height + offset_height;
3325     if (cx == caret_x && cy == caret_y)
3326         return;
3327     caret_x = cx;
3328     caret_y = cy;
3329
3330     sys_cursor_update();
3331 }
3332
3333 static void sys_cursor_update(void)
3334 {
3335     COMPOSITIONFORM cf;
3336     HIMC hIMC;
3337
3338     if (!term->has_focus) return;
3339
3340     if (caret_x < 0 || caret_y < 0)
3341         return;
3342
3343     SetCaretPos(caret_x, caret_y);
3344
3345     /* IMM calls on Win98 and beyond only */
3346     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
3347     
3348     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
3349             osVersion.dwMinorVersion == 0) return; /* 95 */
3350
3351     /* we should have the IMM functions */
3352     hIMC = ImmGetContext(hwnd);
3353     cf.dwStyle = CFS_POINT;
3354     cf.ptCurrentPos.x = caret_x;
3355     cf.ptCurrentPos.y = caret_y;
3356     ImmSetCompositionWindow(hIMC, &cf);
3357
3358     ImmReleaseContext(hwnd, hIMC);
3359 }
3360
3361 /*
3362  * Draw a line of text in the window, at given character
3363  * coordinates, in given attributes.
3364  *
3365  * We are allowed to fiddle with the contents of `text'.
3366  */
3367 void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
3368                       unsigned long attr, int lattr)
3369 {
3370     COLORREF fg, bg, t;
3371     int nfg, nbg, nfont;
3372     HDC hdc = ctx;
3373     RECT line_box;
3374     int force_manual_underline = 0;
3375     int fnt_width, char_width;
3376     int text_adjust = 0;
3377     int xoffset = 0;
3378     int maxlen, remaining, opaque;
3379     int is_cursor = FALSE;
3380     static int *lpDx = NULL;
3381     static int lpDx_len = 0;
3382     int *lpDx_maybe;
3383     int len2; /* for SURROGATE PAIR */
3384
3385     lattr &= LATTR_MODE;
3386
3387     char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));
3388
3389     if (attr & ATTR_WIDE)
3390         char_width *= 2;
3391
3392     /* Only want the left half of double width lines */
3393     if (lattr != LATTR_NORM && x*2 >= term->cols)
3394         return;
3395
3396     x *= fnt_width;
3397     y *= font_height;
3398     x += offset_width;
3399     y += offset_height;
3400
3401     if ((attr & TATTR_ACTCURS) && (cursor_type == 0 || term->big_cursor)) {
3402         attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
3403         /* cursor fg and bg */
3404         attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);
3405         is_cursor = TRUE;
3406     }
3407
3408     nfont = 0;
3409     if (vtmode == VT_POORMAN && lattr != LATTR_NORM) {
3410         /* Assume a poorman font is borken in other ways too. */
3411         lattr = LATTR_WIDE;
3412     } else
3413         switch (lattr) {
3414           case LATTR_NORM:
3415             break;
3416           case LATTR_WIDE:
3417             nfont |= FONT_WIDE;
3418             break;
3419           default:
3420             nfont |= FONT_WIDE + FONT_HIGH;
3421             break;
3422         }
3423     if (attr & ATTR_NARROW)
3424         nfont |= FONT_NARROW;
3425
3426 #ifdef USES_VTLINE_HACK
3427     /* Special hack for the VT100 linedraw glyphs. */
3428     if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
3429         switch ((unsigned char) (text[0])) {
3430           case 0xBA:
3431             text_adjust = -2 * font_height / 5;
3432             break;
3433           case 0xBB:
3434             text_adjust = -1 * font_height / 5;
3435             break;
3436           case 0xBC:
3437             text_adjust = font_height / 5;
3438             break;
3439           case 0xBD:
3440             text_adjust = 2 * font_height / 5;
3441             break;
3442         }
3443         if (lattr == LATTR_TOP || lattr == LATTR_BOT)
3444             text_adjust *= 2;
3445         text[0] = ucsdata.unitab_xterm['q'];
3446         if (attr & ATTR_UNDER) {
3447             attr &= ~ATTR_UNDER;
3448             force_manual_underline = 1;
3449         }
3450     }
3451 #endif
3452
3453     /* Anything left as an original character set is unprintable. */
3454     if (DIRECT_CHAR(text[0]) &&
3455         (len < 2 || !IS_SURROGATE_PAIR(text[0], text[1]))) {
3456         int i;
3457         for (i = 0; i < len; i++)
3458             text[i] = 0xFFFD;
3459     }
3460
3461     /* OEM CP */
3462     if ((text[0] & CSET_MASK) == CSET_OEMCP)
3463         nfont |= FONT_OEM;
3464
3465     nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
3466     nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
3467     if (bold_font_mode == BOLD_FONT && (attr & ATTR_BOLD))
3468         nfont |= FONT_BOLD;
3469     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
3470         nfont |= FONT_UNDERLINE;
3471     another_font(nfont);
3472     if (!fonts[nfont]) {
3473         if (nfont & FONT_UNDERLINE)
3474             force_manual_underline = 1;
3475         /* Don't do the same for manual bold, it could be bad news. */
3476
3477         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
3478     }
3479     another_font(nfont);
3480     if (!fonts[nfont])
3481         nfont = FONT_NORMAL;
3482     if (attr & ATTR_REVERSE) {
3483         t = nfg;
3484         nfg = nbg;
3485         nbg = t;
3486     }
3487     if (bold_colours && (attr & ATTR_BOLD) && !is_cursor) {
3488         if (nfg < 16) nfg |= 8;
3489         else if (nfg >= 256) nfg |= 1;
3490     }
3491     if (bold_colours && (attr & ATTR_BLINK)) {
3492         if (nbg < 16) nbg |= 8;
3493         else if (nbg >= 256) nbg |= 1;
3494     }
3495     fg = colours[nfg];
3496     bg = colours[nbg];
3497     SelectObject(hdc, fonts[nfont]);
3498     SetTextColor(hdc, fg);
3499     SetBkColor(hdc, bg);
3500     if (attr & TATTR_COMBINING)
3501         SetBkMode(hdc, TRANSPARENT);
3502     else
3503         SetBkMode(hdc, OPAQUE);
3504     line_box.left = x;
3505     line_box.top = y;
3506     line_box.right = x + char_width * len;
3507     line_box.bottom = y + font_height;
3508     /* adjust line_box.right for SURROGATE PAIR & VARIATION SELECTOR */
3509     {
3510         int i;
3511         int rc_width = 0;
3512         for (i = 0; i < len ; i++) {
3513             if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) {
3514                 i++;
3515             } else if (i+1 < len && IS_SURROGATE_PAIR(text[i], text[i+1])) {
3516                 rc_width += char_width;
3517                 i++;
3518             } else if (IS_LOW_VARSEL(text[i])) {
3519                 /* do nothing */
3520             } else {
3521                 rc_width += char_width;
3522             }
3523         }
3524         line_box.right = line_box.left + rc_width;
3525     }
3526
3527     /* Only want the left half of double width lines */
3528     if (line_box.right > font_width*term->cols+offset_width)
3529         line_box.right = font_width*term->cols+offset_width;
3530
3531     if (font_varpitch) {
3532         /*
3533          * If we're using a variable-pitch font, we unconditionally
3534          * draw the glyphs one at a time and centre them in their
3535          * character cells (which means in particular that we must
3536          * disable the lpDx mechanism). This gives slightly odd but
3537          * generally reasonable results.
3538          */
3539         xoffset = char_width / 2;
3540         SetTextAlign(hdc, TA_TOP | TA_CENTER | TA_NOUPDATECP);
3541         lpDx_maybe = NULL;
3542         maxlen = 1;
3543     } else {
3544         /*
3545          * In a fixed-pitch font, we draw the whole string in one go
3546          * in the normal way.
3547          */
3548         xoffset = 0;
3549         SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3550         lpDx_maybe = lpDx;
3551         maxlen = len;
3552     }
3553
3554     opaque = TRUE;                     /* start by erasing the rectangle */
3555     for (remaining = len; remaining > 0;
3556          text += len, remaining -= len, x += char_width * len2) {
3557         len = (maxlen < remaining ? maxlen : remaining);
3558         /* don't divide SURROGATE PAIR and VARIATION SELECTOR */
3559         len2 = len;
3560         if (maxlen == 1) {
3561             if (remaining >= 1 && IS_SURROGATE_PAIR(text[0], text[1]))
3562                 len++;
3563             if (remaining-len >= 1 && IS_LOW_VARSEL(text[len]))
3564                 len++;
3565             else if (remaining-len >= 2 &&
3566                      IS_HIGH_VARSEL(text[len], text[len+1]))
3567                 len += 2;
3568         }
3569
3570         if (len > lpDx_len) {
3571             lpDx_len = len * 9 / 8 + 16;
3572             lpDx = sresize(lpDx, lpDx_len, int);
3573
3574             if (lpDx_maybe) lpDx_maybe = lpDx;
3575         }
3576
3577         {
3578             int i;
3579             /* only last char has dx width in SURROGATE PAIR and
3580              * VARIATION sequence */
3581             for (i = 0; i < len; i++) {
3582                 lpDx[i] = char_width;
3583                 if (i+1 < len && IS_HIGH_VARSEL(text[i], text[i+1])) {
3584                     if (i > 0) lpDx[i-1] = 0;
3585                     lpDx[i] = 0;
3586                     i++;
3587                     lpDx[i] = char_width;
3588                 } else if (i+1 < len && IS_SURROGATE_PAIR(text[i],text[i+1])) {
3589                     lpDx[i] = 0;
3590                     i++;
3591                     lpDx[i] = char_width;
3592                 } else if (IS_LOW_VARSEL(text[i])) {
3593                     if (i > 0) lpDx[i-1] = 0;
3594                     lpDx[i] = char_width;
3595                 }
3596             }
3597         }
3598
3599         /* We're using a private area for direct to font. (512 chars.) */
3600         if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {
3601             /* Ho Hum, dbcs fonts are a PITA! */
3602             /* To display on W9x I have to convert to UCS */
3603             static wchar_t *uni_buf = 0;
3604             static int uni_len = 0;
3605             int nlen, mptr;
3606             if (len > uni_len) {
3607                 sfree(uni_buf);
3608                 uni_len = len;
3609                 uni_buf = snewn(uni_len, wchar_t);
3610             }
3611
3612             for(nlen = mptr = 0; mptr<len; mptr++) {
3613                 uni_buf[nlen] = 0xFFFD;
3614                 if (IsDBCSLeadByteEx(ucsdata.font_codepage,
3615                                      (BYTE) text[mptr])) {
3616                     char dbcstext[2];
3617                     dbcstext[0] = text[mptr] & 0xFF;
3618                     dbcstext[1] = text[mptr+1] & 0xFF;
3619                     lpDx[nlen] += char_width;
3620                     MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
3621                                         dbcstext, 2, uni_buf+nlen, 1);
3622                     mptr++;
3623                 }
3624                 else
3625                 {
3626                     char dbcstext[1];
3627                     dbcstext[0] = text[mptr] & 0xFF;
3628                     MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
3629                                         dbcstext, 1, uni_buf+nlen, 1);
3630                 }
3631                 nlen++;
3632             }
3633             if (nlen <= 0)
3634                 return;                /* Eeek! */
3635
3636             ExtTextOutW(hdc, x + xoffset,
3637                         y - font_height * (lattr == LATTR_BOT) + text_adjust,
3638                         ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
3639                         &line_box, uni_buf, nlen,
3640                         lpDx_maybe);
3641             if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3642                 SetBkMode(hdc, TRANSPARENT);
3643                 ExtTextOutW(hdc, x + xoffset - 1,
3644                             y - font_height * (lattr ==
3645                                                LATTR_BOT) + text_adjust,
3646                             ETO_CLIPPED, &line_box, uni_buf, nlen, lpDx_maybe);
3647             }
3648
3649             lpDx[0] = -1;
3650         } else if (DIRECT_FONT(text[0])) {
3651             static char *directbuf = NULL;
3652             static int directlen = 0;
3653             int i;
3654             if (len > directlen) {
3655                 directlen = len;
3656                 directbuf = sresize(directbuf, directlen, char);
3657             }
3658
3659             for (i = 0; i < len; i++)
3660                 directbuf[i] = text[i] & 0xFF;
3661
3662             ExtTextOut(hdc, x + xoffset,
3663                        y - font_height * (lattr == LATTR_BOT) + text_adjust,
3664                        ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
3665                        &line_box, directbuf, len, lpDx_maybe);
3666             if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3667                 SetBkMode(hdc, TRANSPARENT);
3668
3669                 /* GRR: This draws the character outside its box and
3670                  * can leave 'droppings' even with the clip box! I
3671                  * suppose I could loop it one character at a time ...
3672                  * yuk.
3673                  * 
3674                  * Or ... I could do a test print with "W", and use +1
3675                  * or -1 for this shift depending on if the leftmost
3676                  * column is blank...
3677                  */
3678                 ExtTextOut(hdc, x + xoffset - 1,
3679                            y - font_height * (lattr ==
3680                                               LATTR_BOT) + text_adjust,
3681                            ETO_CLIPPED, &line_box, directbuf, len, lpDx_maybe);
3682             }
3683         } else {
3684             /* And 'normal' unicode characters */
3685             static WCHAR *wbuf = NULL;
3686             static int wlen = 0;
3687             int i;
3688
3689             if (wlen < len) {
3690                 sfree(wbuf);
3691                 wlen = len;
3692                 wbuf = snewn(wlen, WCHAR);
3693             }
3694
3695             for (i = 0; i < len; i++)
3696                 wbuf[i] = text[i];
3697
3698             /* print Glyphs as they are, without Windows' Shaping*/
3699             general_textout(hdc, x + xoffset,
3700                             y - font_height * (lattr==LATTR_BOT) + text_adjust,
3701                             &line_box, wbuf, len, lpDx,
3702                             opaque && !(attr & TATTR_COMBINING));
3703
3704             /* And the shadow bold hack. */
3705             if (bold_font_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3706                 SetBkMode(hdc, TRANSPARENT);
3707                 ExtTextOutW(hdc, x + xoffset - 1,
3708                             y - font_height * (lattr ==
3709                                                LATTR_BOT) + text_adjust,
3710                             ETO_CLIPPED, &line_box, wbuf, len, lpDx_maybe);
3711             }
3712         }
3713
3714         /*
3715          * If we're looping round again, stop erasing the background
3716          * rectangle.
3717          */
3718         SetBkMode(hdc, TRANSPARENT);
3719         opaque = FALSE;
3720     }
3721     if (lattr != LATTR_TOP && (force_manual_underline ||
3722                                (und_mode == UND_LINE
3723                                 && (attr & ATTR_UNDER)))) {
3724         HPEN oldpen;
3725         int dec = descent;
3726         if (lattr == LATTR_BOT)
3727             dec = dec * 2 - font_height;
3728
3729         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
3730         MoveToEx(hdc, line_box.left, line_box.top + dec, NULL);
3731         LineTo(hdc, line_box.right, line_box.top + dec);
3732         oldpen = SelectObject(hdc, oldpen);
3733         DeleteObject(oldpen);
3734     }
3735 }
3736
3737 /*
3738  * Wrapper that handles combining characters.
3739  */
3740 void do_text(Context ctx, int x, int y, wchar_t *text, int len,
3741              unsigned long attr, int lattr)
3742 {
3743     if (attr & TATTR_COMBINING) {
3744         unsigned long a = 0;
3745         int len0 = 1;
3746         /* don't divide SURROGATE PAIR and VARIATION SELECTOR */
3747         if (len >= 2 && IS_SURROGATE_PAIR(text[0], text[1]))
3748             len0 = 2;
3749         if (len-len0 >= 1 && IS_LOW_VARSEL(text[len0])) {
3750             attr &= ~TATTR_COMBINING;
3751             do_text_internal(ctx, x, y, text, len0+1, attr, lattr);
3752             text += len0+1;
3753             len -= len0+1;
3754             a = TATTR_COMBINING;
3755         } else if (len-len0 >= 2 && IS_HIGH_VARSEL(text[len0], text[len0+1])) {
3756             attr &= ~TATTR_COMBINING;
3757             do_text_internal(ctx, x, y, text, len0+2, attr, lattr);
3758             text += len0+2;
3759             len -= len0+2;
3760             a = TATTR_COMBINING;
3761         } else {
3762             attr &= ~TATTR_COMBINING;
3763         }
3764
3765         while (len--) {
3766             if (len >= 1 && IS_SURROGATE_PAIR(text[0], text[1])) {
3767                 do_text_internal(ctx, x, y, text, 2, attr | a, lattr);
3768                 len--;
3769                 text++;
3770             } else {
3771                 do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
3772             }
3773
3774             text++;
3775             a = TATTR_COMBINING;
3776         }
3777     } else
3778         do_text_internal(ctx, x, y, text, len, attr, lattr);
3779 }
3780
3781 void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
3782                unsigned long attr, int lattr)
3783 {
3784
3785     int fnt_width;
3786     int char_width;
3787     HDC hdc = ctx;
3788     int ctype = cursor_type;
3789
3790     lattr &= LATTR_MODE;
3791
3792     if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
3793         if (*text != UCSWIDE) {
3794             do_text(ctx, x, y, text, len, attr, lattr);
3795             return;
3796         }
3797         ctype = 2;
3798         attr |= TATTR_RIGHTCURS;
3799     }
3800
3801     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
3802     if (attr & ATTR_WIDE)
3803         char_width *= 2;
3804     x *= fnt_width;
3805     y *= font_height;
3806     x += offset_width;
3807     y += offset_height;
3808
3809     if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
3810         POINT pts[5];
3811         HPEN oldpen;
3812         pts[0].x = pts[1].x = pts[4].x = x;
3813         pts[2].x = pts[3].x = x + char_width - 1;
3814         pts[0].y = pts[3].y = pts[4].y = y;
3815         pts[1].y = pts[2].y = y + font_height - 1;
3816         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
3817         Polyline(hdc, pts, 5);
3818         oldpen = SelectObject(hdc, oldpen);
3819         DeleteObject(oldpen);
3820     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
3821         int startx, starty, dx, dy, length, i;
3822         if (ctype == 1) {
3823             startx = x;
3824             starty = y + descent;
3825             dx = 1;
3826             dy = 0;
3827             length = char_width;
3828         } else {
3829             int xadjust = 0;
3830             if (attr & TATTR_RIGHTCURS)
3831                 xadjust = char_width - 1;
3832             startx = x + xadjust;
3833             starty = y;
3834             dx = 0;
3835             dy = 1;
3836             length = font_height;
3837         }
3838         if (attr & TATTR_ACTCURS) {
3839             HPEN oldpen;
3840             oldpen =
3841                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
3842             MoveToEx(hdc, startx, starty, NULL);
3843             LineTo(hdc, startx + dx * length, starty + dy * length);
3844             oldpen = SelectObject(hdc, oldpen);
3845             DeleteObject(oldpen);
3846         } else {
3847             for (i = 0; i < length; i++) {
3848                 if (i % 2 == 0) {
3849                     SetPixel(hdc, startx, starty, colours[261]);
3850                 }
3851                 startx += dx;
3852                 starty += dy;
3853             }
3854         }
3855     }
3856 }
3857
3858 /* This function gets the actual width of a character in the normal font.
3859  */
3860 int char_width(Context ctx, int uc) {
3861     HDC hdc = ctx;
3862     int ibuf = 0;
3863
3864     /* If the font max is the same as the font ave width then this
3865      * function is a no-op.
3866      */
3867     if (!font_dualwidth) return 1;
3868
3869     switch (uc & CSET_MASK) {
3870       case CSET_ASCII:
3871         uc = ucsdata.unitab_line[uc & 0xFF];
3872         break;
3873       case CSET_LINEDRW:
3874         uc = ucsdata.unitab_xterm[uc & 0xFF];
3875         break;
3876       case CSET_SCOACS:
3877         uc = ucsdata.unitab_scoacs[uc & 0xFF];
3878         break;
3879     }
3880     if (DIRECT_FONT(uc)) {
3881         if (ucsdata.dbcs_screenfont) return 1;
3882
3883         /* Speedup, I know of no font where ascii is the wrong width */
3884         if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')
3885             return 1;
3886
3887         if ( (uc & CSET_MASK) == CSET_ACP ) {
3888             SelectObject(hdc, fonts[FONT_NORMAL]);
3889         } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {
3890             another_font(FONT_OEM);
3891             if (!fonts[FONT_OEM]) return 0;
3892
3893             SelectObject(hdc, fonts[FONT_OEM]);
3894         } else
3895             return 0;
3896
3897         if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&
3898              GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)
3899             return 0;
3900     } else {
3901         /* Speedup, I know of no font where ascii is the wrong width */
3902         if (uc >= ' ' && uc <= '~') return 1;
3903
3904         SelectObject(hdc, fonts[FONT_NORMAL]);
3905         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3906             /* Okay that one worked */ ;
3907         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3908             /* This should work on 9x too, but it's "less accurate" */ ;
3909         else
3910             return 0;
3911     }
3912
3913     ibuf += font_width / 2 -1;
3914     ibuf /= font_width;
3915
3916     return ibuf;
3917 }
3918
3919 DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
3920 DECL_WINDOWS_FUNCTION(static, BOOL, ToUnicodeEx,
3921                       (UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL));
3922
3923 static void init_winfuncs(void)
3924 {
3925     HMODULE user32_module = load_system32_dll("user32.dll");
3926     GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx);
3927     GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
3928 }
3929
3930 /*
3931  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3932  * codes. Returns number of bytes used, zero to drop the message,
3933  * -1 to forward the message to Windows, or another negative number
3934  * to indicate a NUL-terminated "special" string.
3935  */
3936 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3937                         unsigned char *output)
3938 {
3939     BYTE keystate[256];
3940     int scan, left_alt = 0, key_down, shift_state;
3941     int r, i, code;
3942     unsigned char *p = output;
3943     static int alt_sum = 0;
3944     int funky_type = conf_get_int(conf, CONF_funky_type);
3945     int no_applic_k = conf_get_int(conf, CONF_no_applic_k);
3946     int ctrlaltkeys = conf_get_int(conf, CONF_ctrlaltkeys);
3947     int nethack_keypad = conf_get_int(conf, CONF_nethack_keypad);
3948
3949     HKL kbd_layout = GetKeyboardLayout(0);
3950
3951     static wchar_t keys_unicode[3];
3952     static int compose_char = 0;
3953     static WPARAM compose_keycode = 0;
3954
3955     r = GetKeyboardState(keystate);
3956     if (!r)
3957         memset(keystate, 0, sizeof(keystate));
3958     else {
3959 #if 0
3960 #define SHOW_TOASCII_RESULT
3961         {                              /* Tell us all about key events */
3962             static BYTE oldstate[256];
3963             static int first = 1;
3964             static int scan;
3965             int ch;
3966             if (first)
3967                 memcpy(oldstate, keystate, sizeof(oldstate));
3968             first = 0;
3969
3970             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3971                 debug(("+"));
3972             } else if ((HIWORD(lParam) & KF_UP)
3973                        && scan == (HIWORD(lParam) & 0xFF)) {
3974                 debug((". U"));
3975             } else {
3976                 debug((".\n"));
3977                 if (wParam >= VK_F1 && wParam <= VK_F20)
3978                     debug(("K_F%d", wParam + 1 - VK_F1));
3979                 else
3980                     switch (wParam) {
3981                       case VK_SHIFT:
3982                         debug(("SHIFT"));
3983                         break;
3984                       case VK_CONTROL:
3985                         debug(("CTRL"));
3986                         break;
3987                       case VK_MENU:
3988                         debug(("ALT"));
3989                         break;
3990                       default:
3991                         debug(("VK_%02x", wParam));
3992                     }
3993                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3994                     debug(("*"));
3995                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3996
3997                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3998                 if (ch >= ' ' && ch <= '~')
3999                     debug((", '%c'", ch));
4000                 else if (ch)
4001                     debug((", $%02x", ch));
4002
4003                 if (keys_unicode[0])
4004                     debug((", KB0=%04x", keys_unicode[0]));
4005                 if (keys_unicode[1])
4006                     debug((", KB1=%04x", keys_unicode[1]));
4007                 if (keys_unicode[2])
4008                     debug((", KB2=%04x", keys_unicode[2]));
4009
4010                 if ((keystate[VK_SHIFT] & 0x80) != 0)
4011                     debug((", S"));
4012                 if ((keystate[VK_CONTROL] & 0x80) != 0)
4013                     debug((", C"));
4014                 if ((HIWORD(lParam) & KF_EXTENDED))
4015                     debug((", E"));
4016                 if ((HIWORD(lParam) & KF_UP))
4017                     debug((", U"));
4018             }
4019
4020             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
4021             else if ((HIWORD(lParam) & KF_UP))
4022                 oldstate[wParam & 0xFF] ^= 0x80;
4023             else
4024                 oldstate[wParam & 0xFF] ^= 0x81;
4025
4026             for (ch = 0; ch < 256; ch++)
4027                 if (oldstate[ch] != keystate[ch])
4028                     debug((", M%02x=%02x", ch, keystate[ch]));
4029
4030             memcpy(oldstate, keystate, sizeof(oldstate));
4031         }
4032 #endif
4033
4034         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
4035             keystate[VK_RMENU] = keystate[VK_MENU];
4036         }
4037
4038
4039         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
4040         if ((funky_type == FUNKY_VT400 ||
4041              (funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
4042               !no_applic_k))
4043             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
4044
4045             wParam = VK_EXECUTE;
4046
4047             /* UnToggle NUMLock */
4048             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
4049                 keystate[VK_NUMLOCK] ^= 1;
4050         }
4051
4052         /* And write back the 'adjusted' state */
4053         SetKeyboardState(keystate);
4054     }
4055
4056     /* Disable Auto repeat if required */
4057     if (term->repeat_off &&
4058         (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
4059         return 0;
4060
4061     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
4062         left_alt = 1;
4063
4064     key_down = ((HIWORD(lParam) & KF_UP) == 0);
4065
4066     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
4067     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
4068         if (ctrlaltkeys)
4069             keystate[VK_MENU] = 0;
4070         else {
4071             keystate[VK_RMENU] = 0x80;
4072             left_alt = 0;
4073         }
4074     }
4075
4076     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
4077     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
4078         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
4079
4080     /* Note if AltGr was pressed and if it was used as a compose key */
4081     if (!compose_state) {
4082         compose_keycode = 0x100;
4083         if (conf_get_int(conf, CONF_compose_key)) {
4084             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
4085                 compose_keycode = wParam;
4086         }
4087         if (wParam == VK_APPS)
4088             compose_keycode = wParam;
4089     }
4090
4091     if (wParam == compose_keycode) {
4092         if (compose_state == 0
4093             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
4094                 1;
4095         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
4096             compose_state = 2;
4097         else
4098             compose_state = 0;
4099     } else if (compose_state == 1 && wParam != VK_CONTROL)
4100         compose_state = 0;
4101
4102     if (compose_state > 1 && left_alt)
4103         compose_state = 0;
4104
4105     /* Sanitize the number pad if not using a PC NumPad */
4106     if (left_alt || (term->app_keypad_keys && !no_applic_k
4107                      && funky_type != FUNKY_XTERM)
4108         || funky_type == FUNKY_VT400 || nethack_keypad || compose_state) {
4109         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
4110             int nParam = 0;
4111             switch (wParam) {
4112               case VK_INSERT:
4113                 nParam = VK_NUMPAD0;
4114                 break;
4115               case VK_END:
4116                 nParam = VK_NUMPAD1;
4117                 break;
4118               case VK_DOWN:
4119                 nParam = VK_NUMPAD2;
4120                 break;
4121               case VK_NEXT:
4122                 nParam = VK_NUMPAD3;
4123                 break;
4124               case VK_LEFT:
4125                 nParam = VK_NUMPAD4;
4126                 break;
4127               case VK_CLEAR:
4128                 nParam = VK_NUMPAD5;
4129                 break;
4130               case VK_RIGHT:
4131                 nParam = VK_NUMPAD6;
4132                 break;
4133               case VK_HOME:
4134                 nParam = VK_NUMPAD7;
4135                 break;
4136               case VK_UP:
4137                 nParam = VK_NUMPAD8;
4138                 break;
4139               case VK_PRIOR:
4140                 nParam = VK_NUMPAD9;
4141                 break;
4142               case VK_DELETE:
4143                 nParam = VK_DECIMAL;
4144                 break;
4145             }
4146             if (nParam) {
4147                 if (keystate[VK_NUMLOCK] & 1)
4148                     shift_state |= 1;
4149                 wParam = nParam;
4150             }
4151         }
4152     }
4153
4154     /* If a key is pressed and AltGr is not active */
4155     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
4156         /* Okay, prepare for most alts then ... */
4157         if (left_alt)
4158             *p++ = '\033';
4159
4160         /* Lets see if it's a pattern we know all about ... */
4161         if (wParam == VK_PRIOR && shift_state == 1) {
4162             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
4163             return 0;
4164         }
4165         if (wParam == VK_PRIOR && shift_state == 2) {
4166             SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
4167             return 0;
4168         }
4169         if (wParam == VK_NEXT && shift_state == 1) {
4170             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
4171             return 0;
4172         }
4173         if (wParam == VK_NEXT && shift_state == 2) {
4174             SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
4175             return 0;
4176         }
4177         if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) {
4178             term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1));
4179             return 0;
4180         }
4181         if (wParam == VK_INSERT && shift_state == 1) {
4182             request_paste(NULL);
4183             return 0;
4184         }
4185         if (left_alt && wParam == VK_F4 && conf_get_int(conf, CONF_alt_f4)) {
4186             return -1;
4187         }
4188         if (left_alt && wParam == VK_SPACE && conf_get_int(conf,
4189                                                            CONF_alt_space)) {
4190             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
4191             return -1;
4192         }
4193         if (left_alt && wParam == VK_RETURN &&
4194             conf_get_int(conf, CONF_fullscreenonaltenter) &&
4195             (conf_get_int(conf, CONF_resize_action) != RESIZE_DISABLED)) {
4196             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
4197                 flip_full_screen();
4198             return -1;
4199         }
4200         /* Control-Numlock for app-keypad mode switch */
4201         if (wParam == VK_PAUSE && shift_state == 2) {
4202             term->app_keypad_keys ^= 1;
4203             return 0;
4204         }
4205
4206         /* Nethack keypad */
4207         if (nethack_keypad && !left_alt) {
4208             switch (wParam) {
4209               case VK_NUMPAD1:
4210                 *p++ = "bB\002\002"[shift_state & 3];
4211                 return p - output;
4212               case VK_NUMPAD2:
4213                 *p++ = "jJ\012\012"[shift_state & 3];
4214                 return p - output;
4215               case VK_NUMPAD3:
4216                 *p++ = "nN\016\016"[shift_state & 3];
4217                 return p - output;
4218               case VK_NUMPAD4:
4219                 *p++ = "hH\010\010"[shift_state & 3];
4220                 return p - output;
4221               case VK_NUMPAD5:
4222                 *p++ = shift_state ? '.' : '.';
4223                 return p - output;
4224               case VK_NUMPAD6:
4225                 *p++ = "lL\014\014"[shift_state & 3];
4226                 return p - output;
4227               case VK_NUMPAD7:
4228                 *p++ = "yY\031\031"[shift_state & 3];
4229                 return p - output;
4230               case VK_NUMPAD8:
4231                 *p++ = "kK\013\013"[shift_state & 3];
4232                 return p - output;
4233               case VK_NUMPAD9:
4234                 *p++ = "uU\025\025"[shift_state & 3];
4235                 return p - output;
4236             }
4237         }
4238
4239         /* Application Keypad */
4240         if (!left_alt) {
4241             int xkey = 0;
4242
4243             if (funky_type == FUNKY_VT400 ||
4244                 (funky_type <= FUNKY_LINUX &&
4245                  term->app_keypad_keys && !no_applic_k)) switch (wParam) {
4246                   case VK_EXECUTE:
4247                     xkey = 'P';
4248                     break;
4249                   case VK_DIVIDE:
4250                     xkey = 'Q';
4251                     break;
4252                   case VK_MULTIPLY:
4253                     xkey = 'R';
4254                     break;
4255                   case VK_SUBTRACT:
4256                     xkey = 'S';
4257                     break;
4258                 }
4259             if (term->app_keypad_keys && !no_applic_k)
4260                 switch (wParam) {
4261                   case VK_NUMPAD0:
4262                     xkey = 'p';
4263                     break;
4264                   case VK_NUMPAD1:
4265                     xkey = 'q';
4266                     break;
4267                   case VK_NUMPAD2:
4268                     xkey = 'r';
4269                     break;
4270                   case VK_NUMPAD3:
4271                     xkey = 's';
4272                     break;
4273                   case VK_NUMPAD4:
4274                     xkey = 't';
4275                     break;
4276                   case VK_NUMPAD5:
4277                     xkey = 'u';
4278                     break;
4279                   case VK_NUMPAD6:
4280                     xkey = 'v';
4281                     break;
4282                   case VK_NUMPAD7:
4283                     xkey = 'w';
4284                     break;
4285                   case VK_NUMPAD8:
4286                     xkey = 'x';
4287                     break;
4288                   case VK_NUMPAD9:
4289                     xkey = 'y';
4290                     break;
4291
4292                   case VK_DECIMAL:
4293                     xkey = 'n';
4294                     break;
4295                   case VK_ADD:
4296                     if (funky_type == FUNKY_XTERM) {
4297                         if (shift_state)
4298                             xkey = 'l';
4299                         else
4300                             xkey = 'k';
4301                     } else if (shift_state)
4302                         xkey = 'm';
4303                     else
4304                         xkey = 'l';
4305                     break;
4306
4307                   case VK_DIVIDE:
4308                     if (funky_type == FUNKY_XTERM)
4309                         xkey = 'o';
4310                     break;
4311                   case VK_MULTIPLY:
4312                     if (funky_type == FUNKY_XTERM)
4313                         xkey = 'j';
4314                     break;
4315                   case VK_SUBTRACT:
4316                     if (funky_type == FUNKY_XTERM)
4317                         xkey = 'm';
4318                     break;
4319
4320                   case VK_RETURN:
4321                     if (HIWORD(lParam) & KF_EXTENDED)
4322                         xkey = 'M';
4323                     break;
4324                 }
4325             if (xkey) {
4326                 if (term->vt52_mode) {
4327                     if (xkey >= 'P' && xkey <= 'S')
4328                         p += sprintf((char *) p, "\x1B%c", xkey);
4329                     else
4330                         p += sprintf((char *) p, "\x1B?%c", xkey);
4331                 } else
4332                     p += sprintf((char *) p, "\x1BO%c", xkey);
4333                 return p - output;
4334             }
4335         }
4336
4337         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
4338             *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x7F : 0x08);
4339             *p++ = 0;
4340             return -2;
4341         }
4342         if (wParam == VK_BACK && shift_state == 1) {    /* Shift Backspace */
4343             /* We do the opposite of what is configured */
4344             *p++ = (conf_get_int(conf, CONF_bksp_is_delete) ? 0x08 : 0x7F);
4345             *p++ = 0;
4346             return -2;
4347         }
4348         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
4349             *p++ = 0x1B;
4350             *p++ = '[';
4351             *p++ = 'Z';
4352             return p - output;
4353         }
4354         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
4355             *p++ = 0;
4356             return p - output;
4357         }
4358         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
4359             *p++ = 160;
4360             return p - output;
4361         }
4362         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
4363             if (back)
4364                 back->special(backhandle, TS_BRK);
4365             return 0;
4366         }
4367         if (wParam == VK_PAUSE) {      /* Break/Pause */
4368             *p++ = 26;
4369             *p++ = 0;
4370             return -2;
4371         }
4372         /* Control-2 to Control-8 are special */
4373         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
4374             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
4375             return p - output;
4376         }
4377         if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
4378             *p++ = 0x1F;
4379             return p - output;
4380         }
4381         if (shift_state == 2 && (wParam == 0xDF || wParam == 0xDC)) {
4382             *p++ = 0x1C;
4383             return p - output;
4384         }
4385         if (shift_state == 3 && wParam == 0xDE) {
4386             *p++ = 0x1E;               /* Ctrl-~ == Ctrl-^ in xterm at least */
4387             return p - output;
4388         }
4389         if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
4390             *p++ = '\r';
4391             *p++ = '\n';
4392             return p - output;
4393         }
4394
4395         /*
4396          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
4397          * for integer decimal nn.)
4398          *
4399          * We also deal with the weird ones here. Linux VCs replace F1
4400          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
4401          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
4402          * respectively.
4403          */
4404         code = 0;
4405         switch (wParam) {
4406           case VK_F1:
4407             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
4408             break;
4409           case VK_F2:
4410             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
4411             break;
4412           case VK_F3:
4413             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
4414             break;
4415           case VK_F4:
4416             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
4417             break;
4418           case VK_F5:
4419             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
4420             break;
4421           case VK_F6:
4422             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
4423             break;
4424           case VK_F7:
4425             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
4426             break;
4427           case VK_F8:
4428             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
4429             break;
4430           case VK_F9:
4431             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
4432             break;
4433           case VK_F10:
4434             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
4435             break;
4436           case VK_F11:
4437             code = 23;
4438             break;
4439           case VK_F12:
4440             code = 24;
4441             break;
4442           case VK_F13:
4443             code = 25;
4444             break;
4445           case VK_F14:
4446             code = 26;
4447             break;
4448           case VK_F15:
4449             code = 28;
4450             break;
4451           case VK_F16:
4452             code = 29;
4453             break;
4454           case VK_F17:
4455             code = 31;
4456             break;
4457           case VK_F18:
4458             code = 32;
4459             break;
4460           case VK_F19:
4461             code = 33;
4462             break;
4463           case VK_F20:
4464             code = 34;
4465             break;
4466         }
4467         if ((shift_state&2) == 0) switch (wParam) {
4468           case VK_HOME:
4469             code = 1;
4470             break;
4471           case VK_INSERT:
4472             code = 2;
4473             break;
4474           case VK_DELETE:
4475             code = 3;
4476             break;
4477           case VK_END:
4478             code = 4;
4479             break;
4480           case VK_PRIOR:
4481             code = 5;
4482             break;
4483           case VK_NEXT:
4484             code = 6;
4485             break;
4486         }
4487         /* Reorder edit keys to physical order */
4488         if (funky_type == FUNKY_VT400 && code <= 6)
4489             code = "\0\2\1\4\5\3\6"[code];
4490
4491         if (term->vt52_mode && code > 0 && code <= 6) {
4492             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
4493             return p - output;
4494         }
4495
4496         if (funky_type == FUNKY_SCO && code >= 11 && code <= 34) {
4497             /* SCO function keys */
4498             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
4499             int index = 0;
4500             switch (wParam) {
4501               case VK_F1: index = 0; break;
4502               case VK_F2: index = 1; break;
4503               case VK_F3: index = 2; break;
4504               case VK_F4: index = 3; break;
4505               case VK_F5: index = 4; break;
4506               case VK_F6: index = 5; break;
4507               case VK_F7: index = 6; break;
4508               case VK_F8: index = 7; break;
4509               case VK_F9: index = 8; break;
4510               case VK_F10: index = 9; break;
4511               case VK_F11: index = 10; break;
4512               case VK_F12: index = 11; break;
4513             }
4514             if (keystate[VK_SHIFT] & 0x80) index += 12;
4515             if (keystate[VK_CONTROL] & 0x80) index += 24;
4516             p += sprintf((char *) p, "\x1B[%c", codes[index]);
4517             return p - output;
4518         }
4519         if (funky_type == FUNKY_SCO &&     /* SCO small keypad */
4520             code >= 1 && code <= 6) {
4521             char codes[] = "HL.FIG";
4522             if (code == 3) {
4523                 *p++ = '\x7F';
4524             } else {
4525                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
4526             }
4527             return p - output;
4528         }
4529         if ((term->vt52_mode || funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
4530             int offt = 0;
4531             if (code > 15)
4532                 offt++;
4533             if (code > 21)
4534                 offt++;
4535             if (term->vt52_mode)
4536                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
4537             else
4538                 p +=
4539                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
4540             return p - output;
4541         }
4542         if (funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
4543             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
4544             return p - output;
4545         }
4546         if (funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
4547             if (term->vt52_mode)
4548                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
4549             else
4550                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
4551             return p - output;
4552         }
4553         if ((code == 1 || code == 4) &&
4554             conf_get_int(conf, CONF_rxvt_homeend)) {
4555             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
4556             return p - output;
4557         }
4558         if (code) {
4559             p += sprintf((char *) p, "\x1B[%d~", code);
4560             return p - output;
4561         }
4562
4563         /*
4564          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
4565          * some reason seems to send VK_CLEAR to Windows...).
4566          */
4567         {
4568             char xkey = 0;
4569             switch (wParam) {
4570               case VK_UP:
4571                 xkey = 'A';
4572                 break;
4573               case VK_DOWN:
4574                 xkey = 'B';
4575                 break;
4576               case VK_RIGHT:
4577                 xkey = 'C';
4578                 break;
4579               case VK_LEFT:
4580                 xkey = 'D';
4581                 break;
4582               case VK_CLEAR:
4583                 xkey = 'G';
4584                 break;
4585             }
4586             if (xkey) {
4587                 p += format_arrow_key(p, term, xkey, shift_state);
4588                 return p - output;
4589             }
4590         }
4591
4592         /*
4593          * Finally, deal with Return ourselves. (Win95 seems to
4594          * foul it up when Alt is pressed, for some reason.)
4595          */
4596         if (wParam == VK_RETURN) {     /* Return */
4597             *p++ = 0x0D;
4598             *p++ = 0;
4599             return -2;
4600         }
4601
4602         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
4603             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
4604         else
4605             alt_sum = 0;
4606     }
4607
4608     /* Okay we've done everything interesting; let windows deal with 
4609      * the boring stuff */
4610     {
4611         BOOL capsOn=0;
4612
4613         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
4614         if(keystate[VK_CAPITAL] != 0 &&
4615            conf_get_int(conf, CONF_xlat_capslockcyr)) {
4616             capsOn= !left_alt;
4617             keystate[VK_CAPITAL] = 0;
4618         }
4619
4620         /* XXX how do we know what the max size of the keys array should
4621          * be is? There's indication on MS' website of an Inquire/InquireEx
4622          * functioning returning a KBINFO structure which tells us. */
4623         if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT && p_ToUnicodeEx) {
4624             r = p_ToUnicodeEx(wParam, scan, keystate, keys_unicode,
4625                               lenof(keys_unicode), 0, kbd_layout);
4626         } else {
4627             /* XXX 'keys' parameter is declared in MSDN documentation as
4628              * 'LPWORD lpChar'.
4629              * The experience of a French user indicates that on
4630              * Win98, WORD[] should be passed in, but on Win2K, it should
4631              * be BYTE[]. German WinXP and my Win2K with "US International"
4632              * driver corroborate this.
4633              * Experimentally I've conditionalised the behaviour on the
4634              * Win9x/NT split, but I suspect it's worse than that.
4635              * See wishlist item `win-dead-keys' for more horrible detail
4636              * and speculations. */
4637             int i;
4638             static WORD keys[3];
4639             static BYTE keysb[3];
4640             r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4641             if (r > 0) {
4642                 for (i = 0; i < r; i++) {
4643                     keysb[i] = (BYTE)keys[i];
4644                 }
4645                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)keysb, r,
4646                                     keys_unicode, lenof(keys_unicode));
4647             }
4648         }
4649 #ifdef SHOW_TOASCII_RESULT
4650         if (r == 1 && !key_down) {
4651             if (alt_sum) {
4652                 if (in_utf(term) || ucsdata.dbcs_screenfont)
4653                     debug((", (U+%04x)", alt_sum));
4654                 else
4655                     debug((", LCH(%d)", alt_sum));
4656             } else {
4657                 debug((", ACH(%d)", keys_unicode[0]));
4658             }
4659         } else if (r > 0) {
4660             int r1;
4661             debug((", ASC("));
4662             for (r1 = 0; r1 < r; r1++) {
4663                 debug(("%s%d", r1 ? "," : "", keys_unicode[r1]));
4664             }
4665             debug((")"));
4666         }
4667 #endif
4668         if (r > 0) {
4669             WCHAR keybuf;
4670
4671             p = output;
4672             for (i = 0; i < r; i++) {
4673                 wchar_t wch = keys_unicode[i];
4674
4675                 if (compose_state == 2 && wch >= ' ' && wch < 0x80) {
4676                     compose_char = wch;
4677                     compose_state++;
4678                     continue;
4679                 }
4680                 if (compose_state == 3 && wch >= ' ' && wch < 0x80) {
4681                     int nc;
4682                     compose_state = 0;
4683
4684                     if ((nc = check_compose(compose_char, wch)) == -1) {
4685                         MessageBeep(MB_ICONHAND);
4686                         return 0;
4687                     }
4688                     keybuf = nc;
4689                     term_seen_key_event(term);
4690                     if (ldisc)
4691                         luni_send(ldisc, &keybuf, 1, 1);
4692                     continue;
4693                 }
4694
4695                 compose_state = 0;
4696
4697                 if (!key_down) {
4698                     if (alt_sum) {
4699                         if (in_utf(term) || ucsdata.dbcs_screenfont) {
4700                             keybuf = alt_sum;
4701                             term_seen_key_event(term);
4702                             if (ldisc)
4703                                 luni_send(ldisc, &keybuf, 1, 1);
4704                         } else {
4705                             char ch = (char) alt_sum;
4706                             /*
4707                              * We need not bother about stdin
4708                              * backlogs here, because in GUI PuTTY
4709                              * we can't do anything about it
4710                              * anyway; there's no means of asking
4711                              * Windows to hold off on KEYDOWN
4712                              * messages. We _have_ to buffer
4713                              * everything we're sent.
4714                              */
4715                             term_seen_key_event(term);
4716                             if (ldisc)
4717                                 ldisc_send(ldisc, &ch, 1, 1);
4718                         }
4719                         alt_sum = 0;
4720                     } else {
4721                         term_seen_key_event(term);
4722                         if (ldisc)
4723                             luni_send(ldisc, &wch, 1, 1);
4724                     }
4725                 } else {
4726                     if(capsOn && wch < 0x80) {
4727                         WCHAR cbuf[2];
4728                         cbuf[0] = 27;
4729                         cbuf[1] = xlat_uskbd2cyrllic(wch);
4730                         term_seen_key_event(term);
4731                         if (ldisc)
4732                             luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
4733                     } else {
4734                         WCHAR cbuf[2];
4735                         cbuf[0] = '\033';
4736                         cbuf[1] = wch;
4737                         term_seen_key_event(term);
4738                         if (ldisc)
4739                             luni_send(ldisc, cbuf +!left_alt, 1+!!left_alt, 1);
4740                     }
4741                 }
4742                 show_mouseptr(0);
4743             }
4744
4745             /* This is so the ALT-Numpad and dead keys work correctly. */
4746             keys_unicode[0] = 0;
4747
4748             return p - output;
4749         }
4750         /* If we're definitly not building up an ALT-54321 then clear it */
4751         if (!left_alt)
4752             keys_unicode[0] = 0;
4753         /* If we will be using alt_sum fix the 256s */
4754         else if (keys_unicode[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
4755             keys_unicode[0] = 10;
4756     }
4757
4758     /*
4759      * ALT alone may or may not want to bring up the System menu.
4760      * If it's not meant to, we return 0 on presses or releases of
4761      * ALT, to show that we've swallowed the keystroke. Otherwise
4762      * we return -1, which means Windows will give the keystroke
4763      * its default handling (i.e. bring up the System menu).
4764      */
4765     if (wParam == VK_MENU && !conf_get_int(conf, CONF_alt_only))
4766         return 0;
4767
4768     return -1;
4769 }
4770
4771 void set_title(void *frontend, char *title)
4772 {
4773     sfree(window_name);
4774     window_name = snewn(1 + strlen(title), char);
4775     strcpy(window_name, title);
4776     if (conf_get_int(conf, CONF_win_name_always) || !IsIconic(hwnd))
4777         SetWindowText(hwnd, title);
4778 }
4779
4780 void set_icon(void *frontend, char *title)
4781 {
4782     sfree(icon_name);
4783     icon_name = snewn(1 + strlen(title), char);
4784     strcpy(icon_name, title);
4785     if (!conf_get_int(conf, CONF_win_name_always) && IsIconic(hwnd))
4786         SetWindowText(hwnd, title);
4787 }
4788
4789 void set_sbar(void *frontend, int total, int start, int page)
4790 {
4791     SCROLLINFO si;
4792
4793     if (!conf_get_int(conf, is_full_screen() ?
4794                       CONF_scrollbar_in_fullscreen : CONF_scrollbar))
4795         return;
4796
4797     si.cbSize = sizeof(si);
4798     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
4799     si.nMin = 0;
4800     si.nMax = total - 1;
4801     si.nPage = page;
4802     si.nPos = start;
4803     if (hwnd)
4804         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
4805 }
4806
4807 Context get_ctx(void *frontend)
4808 {
4809     HDC hdc;
4810     if (hwnd) {
4811         hdc = GetDC(hwnd);
4812         if (hdc && pal)
4813             SelectPalette(hdc, pal, FALSE);
4814         return hdc;
4815     } else
4816         return NULL;
4817 }
4818
4819 void free_ctx(Context ctx)
4820 {
4821     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
4822     ReleaseDC(hwnd, ctx);
4823 }
4824
4825 static void real_palette_set(int n, int r, int g, int b)
4826 {
4827     if (pal) {
4828         logpal->palPalEntry[n].peRed = r;
4829         logpal->palPalEntry[n].peGreen = g;
4830         logpal->palPalEntry[n].peBlue = b;
4831         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
4832         colours[n] = PALETTERGB(r, g, b);
4833         SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
4834     } else
4835         colours[n] = RGB(r, g, b);
4836 }
4837
4838 void palette_set(void *frontend, int n, int r, int g, int b)
4839 {
4840     if (n >= 16)
4841         n += 256 - 16;
4842     if (n >= NALLCOLOURS)
4843         return;
4844     real_palette_set(n, r, g, b);
4845     if (pal) {
4846         HDC hdc = get_ctx(frontend);
4847         UnrealizeObject(pal);
4848         RealizePalette(hdc);
4849         free_ctx(hdc);
4850     } else {
4851         if (n == (ATTR_DEFBG>>ATTR_BGSHIFT))
4852             /* If Default Background changes, we need to ensure any
4853              * space between the text area and the window border is
4854              * redrawn. */
4855             InvalidateRect(hwnd, NULL, TRUE);
4856     }
4857 }
4858
4859 void palette_reset(void *frontend)
4860 {
4861     int i;
4862
4863     /* And this */
4864     for (i = 0; i < NALLCOLOURS; i++) {
4865         if (pal) {
4866             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4867             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4868             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4869             logpal->palPalEntry[i].peFlags = 0;
4870             colours[i] = PALETTERGB(defpal[i].rgbtRed,
4871                                     defpal[i].rgbtGreen,
4872                                     defpal[i].rgbtBlue);
4873         } else
4874             colours[i] = RGB(defpal[i].rgbtRed,
4875                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
4876     }
4877
4878     if (pal) {
4879         HDC hdc;
4880         SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
4881         hdc = get_ctx(frontend);
4882         RealizePalette(hdc);
4883         free_ctx(hdc);
4884     } else {
4885         /* Default Background may have changed. Ensure any space between
4886          * text area and window border is redrawn. */
4887         InvalidateRect(hwnd, NULL, TRUE);
4888     }
4889 }
4890
4891 void write_aclip(void *frontend, char *data, int len, int must_deselect)
4892 {
4893     HGLOBAL clipdata;
4894     void *lock;
4895
4896     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
4897     if (!clipdata)
4898         return;
4899     lock = GlobalLock(clipdata);
4900     if (!lock)
4901         return;
4902     memcpy(lock, data, len);
4903     ((unsigned char *) lock)[len] = 0;
4904     GlobalUnlock(clipdata);
4905
4906     if (!must_deselect)
4907         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4908
4909     if (OpenClipboard(hwnd)) {
4910         EmptyClipboard();
4911         SetClipboardData(CF_TEXT, clipdata);
4912         CloseClipboard();
4913     } else
4914         GlobalFree(clipdata);
4915
4916     if (!must_deselect)
4917         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4918 }
4919
4920 /*
4921  * Note: unlike write_aclip() this will not append a nul.
4922  */
4923 void write_clip(void *frontend, wchar_t * data, int *attr, int len, int must_deselect)
4924 {
4925     HGLOBAL clipdata, clipdata2, clipdata3;
4926     int len2;
4927     void *lock, *lock2, *lock3;
4928
4929     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4930
4931     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4932                            len * sizeof(wchar_t));
4933     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4934
4935     if (!clipdata || !clipdata2) {
4936         if (clipdata)
4937             GlobalFree(clipdata);
4938         if (clipdata2)
4939             GlobalFree(clipdata2);
4940         return;
4941     }
4942     if (!(lock = GlobalLock(clipdata))) {
4943         GlobalFree(clipdata);
4944         GlobalFree(clipdata2);
4945         return;
4946     }
4947     if (!(lock2 = GlobalLock(clipdata2))) {
4948         GlobalUnlock(clipdata);
4949         GlobalFree(clipdata);
4950         GlobalFree(clipdata2);
4951         return;
4952     }
4953
4954     memcpy(lock, data, len * sizeof(wchar_t));
4955     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4956
4957     if (conf_get_int(conf, CONF_rtf_paste)) {
4958         wchar_t unitab[256];
4959         char *rtf = NULL;
4960         unsigned char *tdata = (unsigned char *)lock2;
4961         wchar_t *udata = (wchar_t *)lock;
4962         int rtflen = 0, uindex = 0, tindex = 0;
4963         int rtfsize = 0;
4964         int multilen, blen, alen, totallen, i;
4965         char before[16], after[4];
4966         int fgcolour,  lastfgcolour  = 0;
4967         int bgcolour,  lastbgcolour  = 0;
4968         int attrBold,  lastAttrBold  = 0;
4969         int attrUnder, lastAttrUnder = 0;
4970         int palette[NALLCOLOURS];
4971         int numcolours;
4972         FontSpec *font = conf_get_fontspec(conf, CONF_font);
4973
4974         get_unitab(CP_ACP, unitab, 0);
4975
4976         rtfsize = 100 + strlen(font->name);
4977         rtf = snewn(rtfsize, char);
4978         rtflen = sprintf(rtf, "{\\rtf1\\ansi\\deff0{\\fonttbl\\f0\\fmodern %s;}\\f0\\fs%d",
4979                          font->name, font->height*2);
4980
4981         /*
4982          * Add colour palette
4983          * {\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}
4984          */
4985
4986         /*
4987          * First - Determine all colours in use
4988          *    o  Foregound and background colours share the same palette
4989          */
4990         if (attr) {
4991             memset(palette, 0, sizeof(palette));
4992             for (i = 0; i < (len-1); i++) {
4993                 fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT);
4994                 bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT);
4995
4996                 if (attr[i] & ATTR_REVERSE) {
4997                     int tmpcolour = fgcolour;   /* Swap foreground and background */
4998                     fgcolour = bgcolour;
4999                     bgcolour = tmpcolour;
5000                 }
5001
5002                 if (bold_colours && (attr[i] & ATTR_BOLD)) {
5003                     if (fgcolour  <   8)        /* ANSI colours */
5004                         fgcolour +=   8;
5005                     else if (fgcolour >= 256)   /* Default colours */
5006                         fgcolour ++;
5007                 }
5008
5009                 if (attr[i] & ATTR_BLINK) {
5010                     if (bgcolour  <   8)        /* ANSI colours */
5011                         bgcolour +=   8;
5012                     else if (bgcolour >= 256)   /* Default colours */
5013                         bgcolour ++;
5014                 }
5015
5016                 palette[fgcolour]++;
5017                 palette[bgcolour]++;
5018             }
5019
5020             /*
5021              * Next - Create a reduced palette
5022              */
5023             numcolours = 0;
5024             for (i = 0; i < NALLCOLOURS; i++) {
5025                 if (palette[i] != 0)
5026                     palette[i]  = ++numcolours;
5027             }
5028
5029             /*
5030              * Finally - Write the colour table
5031              */
5032             rtf = sresize(rtf, rtfsize + (numcolours * 25), char);
5033             strcat(rtf, "{\\colortbl ;");
5034             rtflen = strlen(rtf);
5035
5036             for (i = 0; i < NALLCOLOURS; i++) {
5037                 if (palette[i] != 0) {
5038                     rtflen += sprintf(&rtf[rtflen], "\\red%d\\green%d\\blue%d;", defpal[i].rgbtRed, defpal[i].rgbtGreen, defpal[i].rgbtBlue);
5039                 }
5040             }
5041             strcpy(&rtf[rtflen], "}");
5042             rtflen ++;
5043         }
5044
5045         /*
5046          * We want to construct a piece of RTF that specifies the
5047          * same Unicode text. To do this we will read back in
5048          * parallel from the Unicode data in `udata' and the
5049          * non-Unicode data in `tdata'. For each character in
5050          * `tdata' which becomes the right thing in `udata' when
5051          * looked up in `unitab', we just copy straight over from
5052          * tdata. For each one that doesn't, we must WCToMB it
5053          * individually and produce a \u escape sequence.
5054          * 
5055          * It would probably be more robust to just bite the bullet
5056          * and WCToMB each individual Unicode character one by one,
5057          * then MBToWC each one back to see if it was an accurate
5058          * translation; but that strikes me as a horrifying number
5059          * of Windows API calls so I want to see if this faster way
5060          * will work. If it screws up badly we can always revert to
5061          * the simple and slow way.
5062          */
5063         while (tindex < len2 && uindex < len &&
5064                tdata[tindex] && udata[uindex]) {
5065             if (tindex + 1 < len2 &&
5066                 tdata[tindex] == '\r' &&
5067                 tdata[tindex+1] == '\n') {
5068                 tindex++;
5069                 uindex++;
5070             }
5071
5072             /*
5073              * Set text attributes
5074              */
5075             if (attr) {
5076                 if (rtfsize < rtflen + 64) {
5077                     rtfsize = rtflen + 512;
5078                     rtf = sresize(rtf, rtfsize, char);
5079                 }
5080
5081                 /*
5082                  * Determine foreground and background colours
5083                  */
5084                 fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT);
5085                 bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT);
5086
5087                 if (attr[tindex] & ATTR_REVERSE) {
5088                     int tmpcolour = fgcolour;       /* Swap foreground and background */
5089                     fgcolour = bgcolour;
5090                     bgcolour = tmpcolour;
5091                 }
5092
5093                 if (bold_colours && (attr[tindex] & ATTR_BOLD)) {
5094                     if (fgcolour  <   8)            /* ANSI colours */
5095                         fgcolour +=   8;
5096                     else if (fgcolour >= 256)       /* Default colours */
5097                         fgcolour ++;
5098                 }
5099
5100                 if (attr[tindex] & ATTR_BLINK) {
5101                     if (bgcolour  <   8)            /* ANSI colours */
5102                         bgcolour +=   8;
5103                     else if (bgcolour >= 256)       /* Default colours */
5104                         bgcolour ++;
5105                 }
5106
5107                 /*
5108                  * Collect other attributes
5109                  */
5110                 if (bold_font_mode != BOLD_NONE)
5111                     attrBold  = attr[tindex] & ATTR_BOLD;
5112                 else
5113                     attrBold  = 0;
5114                 
5115                 attrUnder = attr[tindex] & ATTR_UNDER;
5116
5117                 /*
5118                  * Reverse video
5119                  *   o  If video isn't reversed, ignore colour attributes for default foregound
5120                  *      or background.
5121                  *   o  Special case where bolded text is displayed using the default foregound
5122                  *      and background colours - force to bolded RTF.
5123                  */
5124                 if (!(attr[tindex] & ATTR_REVERSE)) {
5125                     if (bgcolour >= 256)            /* Default color */
5126                         bgcolour  = -1;             /* No coloring */
5127
5128                     if (fgcolour >= 256) {          /* Default colour */
5129                         if (bold_colours && (fgcolour & 1) && bgcolour == -1)
5130                             attrBold = ATTR_BOLD;   /* Emphasize text with bold attribute */
5131
5132                         fgcolour  = -1;             /* No coloring */
5133                     }
5134                 }
5135
5136                 /*
5137                  * Write RTF text attributes
5138                  */
5139                 if (lastfgcolour != fgcolour) {
5140                     lastfgcolour  = fgcolour;
5141                     rtflen       += sprintf(&rtf[rtflen], "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0);
5142                 }
5143
5144                 if (lastbgcolour != bgcolour) {
5145                     lastbgcolour  = bgcolour;
5146                     rtflen       += sprintf(&rtf[rtflen], "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0);
5147                 }
5148
5149                 if (lastAttrBold != attrBold) {
5150                     lastAttrBold  = attrBold;
5151                     rtflen       += sprintf(&rtf[rtflen], "%s", attrBold ? "\\b " : "\\b0 ");
5152                 }
5153
5154                 if (lastAttrUnder != attrUnder) {
5155                     lastAttrUnder  = attrUnder;
5156                     rtflen        += sprintf(&rtf[rtflen], "%s", attrUnder ? "\\ul " : "\\ulnone ");
5157                 }
5158             }
5159
5160             if (unitab[tdata[tindex]] == udata[uindex]) {
5161                 multilen = 1;
5162                 before[0] = '\0';
5163                 after[0] = '\0';
5164                 blen = alen = 0;
5165             } else {
5166                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
5167                                                NULL, 0, NULL, NULL);
5168                 if (multilen != 1) {
5169                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
5170                                    udata[uindex]);
5171                     alen = 1; strcpy(after, "}");
5172                 } else {
5173                     blen = sprintf(before, "\\u%d", udata[uindex]);
5174                     alen = 0; after[0] = '\0';
5175                 }
5176             }
5177             assert(tindex + multilen <= len2);
5178             totallen = blen + alen;
5179             for (i = 0; i < multilen; i++) {
5180                 if (tdata[tindex+i] == '\\' ||
5181                     tdata[tindex+i] == '{' ||
5182                     tdata[tindex+i] == '}')
5183                     totallen += 2;
5184                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
5185                     totallen += 6;     /* \par\r\n */
5186                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
5187                     totallen += 4;
5188                 else
5189                     totallen++;
5190             }
5191
5192             if (rtfsize < rtflen + totallen + 3) {
5193                 rtfsize = rtflen + totallen + 512;
5194                 rtf = sresize(rtf, rtfsize, char);
5195             }
5196
5197             strcpy(rtf + rtflen, before); rtflen += blen;
5198             for (i = 0; i < multilen; i++) {
5199                 if (tdata[tindex+i] == '\\' ||
5200                     tdata[tindex+i] == '{' ||
5201                     tdata[tindex+i] == '}') {
5202                     rtf[rtflen++] = '\\';
5203                     rtf[rtflen++] = tdata[tindex+i];
5204                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
5205                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
5206                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
5207                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
5208                 } else {
5209                     rtf[rtflen++] = tdata[tindex+i];
5210                 }
5211             }
5212             strcpy(rtf + rtflen, after); rtflen += alen;
5213
5214             tindex += multilen;
5215             uindex++;
5216         }
5217
5218         rtf[rtflen++] = '}';           /* Terminate RTF stream */
5219         rtf[rtflen++] = '\0';
5220         rtf[rtflen++] = '\0';
5221
5222         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
5223         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
5224             memcpy(lock3, rtf, rtflen);
5225             GlobalUnlock(clipdata3);
5226         }
5227         sfree(rtf);
5228     } else
5229         clipdata3 = NULL;
5230
5231     GlobalUnlock(clipdata);
5232     GlobalUnlock(clipdata2);
5233
5234     if (!must_deselect)
5235         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
5236
5237     if (OpenClipboard(hwnd)) {
5238         EmptyClipboard();
5239         SetClipboardData(CF_UNICODETEXT, clipdata);
5240         SetClipboardData(CF_TEXT, clipdata2);
5241         if (clipdata3)
5242             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
5243         CloseClipboard();
5244     } else {
5245         GlobalFree(clipdata);
5246         GlobalFree(clipdata2);
5247     }
5248
5249     if (!must_deselect)
5250         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
5251 }
5252
5253 static DWORD WINAPI clipboard_read_threadfunc(void *param)
5254 {
5255     HWND hwnd = (HWND)param;
5256     HGLOBAL clipdata;
5257
5258     if (OpenClipboard(NULL)) {
5259         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
5260             SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)1, (LPARAM)clipdata);
5261         } else if ((clipdata = GetClipboardData(CF_TEXT))) {
5262             SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)0, (LPARAM)clipdata);
5263         }
5264         CloseClipboard();
5265     }
5266
5267     return 0;
5268 }
5269
5270 static int process_clipdata(HGLOBAL clipdata, int unicode)
5271 {
5272     sfree(clipboard_contents);
5273     clipboard_contents = NULL;
5274     clipboard_length = 0;
5275
5276     if (unicode) {
5277         wchar_t *p = GlobalLock(clipdata);
5278         wchar_t *p2;
5279
5280         if (p) {
5281             /* Unwilling to rely on Windows having wcslen() */
5282             for (p2 = p; *p2; p2++);
5283             clipboard_length = p2 - p;
5284             clipboard_contents = snewn(clipboard_length + 1, wchar_t);
5285             memcpy(clipboard_contents, p, clipboard_length * sizeof(wchar_t));
5286             clipboard_contents[clipboard_length] = L'\0';
5287             return TRUE;
5288         }
5289     } else {
5290         char *s = GlobalLock(clipdata);
5291         int i;
5292
5293         if (s) {
5294             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
5295             clipboard_contents = snewn(i, wchar_t);
5296             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1,
5297                                 clipboard_contents, i);
5298             clipboard_length = i - 1;
5299             clipboard_contents[clipboard_length] = L'\0';
5300             return TRUE;
5301         }
5302     }
5303
5304     return FALSE;
5305 }
5306
5307 void request_paste(void *frontend)
5308 {
5309     /*
5310      * I always thought pasting was synchronous in Windows; the
5311      * clipboard access functions certainly _look_ synchronous,
5312      * unlike the X ones. But in fact it seems that in some
5313      * situations the contents of the clipboard might not be
5314      * immediately available, and the clipboard-reading functions
5315      * may block. This leads to trouble if the application
5316      * delivering the clipboard data has to get hold of it by -
5317      * for example - talking over a network connection which is
5318      * forwarded through this very PuTTY.
5319      *
5320      * Hence, we spawn a subthread to read the clipboard, and do
5321      * our paste when it's finished. The thread will send a
5322      * message back to our main window when it terminates, and
5323      * that tells us it's OK to paste.
5324      */
5325     DWORD in_threadid; /* required for Win9x */
5326     CreateThread(NULL, 0, clipboard_read_threadfunc,
5327                  hwnd, 0, &in_threadid);
5328 }
5329
5330 void get_clip(void *frontend, wchar_t **p, int *len)
5331 {
5332     if (p) {
5333         *p = clipboard_contents;
5334         *len = clipboard_length;
5335     }
5336 }
5337
5338 #if 0
5339 /*
5340  * Move `lines' lines from position `from' to position `to' in the
5341  * window.
5342  */
5343 void optimised_move(void *frontend, int to, int from, int lines)
5344 {
5345     RECT r;
5346     int min, max;
5347
5348     min = (to < from ? to : from);
5349     max = to + from - min;
5350
5351     r.left = offset_width;
5352     r.right = offset_width + term->cols * font_width;
5353     r.top = offset_height + min * font_height;
5354     r.bottom = offset_height + (max + lines) * font_height;
5355     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
5356 }
5357 #endif
5358
5359 /*
5360  * Print a message box and perform a fatal exit.
5361  */
5362 void fatalbox(char *fmt, ...)
5363 {
5364     va_list ap;
5365     char *stuff, morestuff[100];
5366
5367     va_start(ap, fmt);
5368     stuff = dupvprintf(fmt, ap);
5369     va_end(ap);
5370     sprintf(morestuff, "%.70s Fatal Error", appname);
5371     MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
5372     sfree(stuff);
5373     cleanup_exit(1);
5374 }
5375
5376 /*
5377  * Print a modal (Really Bad) message box and perform a fatal exit.
5378  */
5379 void modalfatalbox(char *fmt, ...)
5380 {
5381     va_list ap;
5382     char *stuff, morestuff[100];
5383
5384     va_start(ap, fmt);
5385     stuff = dupvprintf(fmt, ap);
5386     va_end(ap);
5387     sprintf(morestuff, "%.70s Fatal Error", appname);
5388     MessageBox(hwnd, stuff, morestuff,
5389                MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
5390     sfree(stuff);
5391     cleanup_exit(1);
5392 }
5393
5394 /*
5395  * Print a message box and don't close the connection.
5396  */
5397 void nonfatal(char *fmt, ...)
5398 {
5399     va_list ap;
5400     char *stuff, morestuff[100];
5401
5402     va_start(ap, fmt);
5403     stuff = dupvprintf(fmt, ap);
5404     va_end(ap);
5405     sprintf(morestuff, "%.70s Error", appname);
5406     MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
5407     sfree(stuff);
5408 }
5409
5410 static BOOL flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout)
5411 {
5412     if (p_FlashWindowEx) {
5413         FLASHWINFO fi;
5414         fi.cbSize = sizeof(fi);
5415         fi.hwnd = hwnd;
5416         fi.dwFlags = dwFlags;
5417         fi.uCount = uCount;
5418         fi.dwTimeout = dwTimeout;
5419         return (*p_FlashWindowEx)(&fi);
5420     }
5421     else
5422         return FALSE; /* shrug */
5423 }
5424
5425 static void flash_window(int mode);
5426 static long next_flash;
5427 static int flashing = 0;
5428
5429 /*
5430  * Timer for platforms where we must maintain window flashing manually
5431  * (e.g., Win95).
5432  */
5433 static void flash_window_timer(void *ctx, unsigned long now)
5434 {
5435     if (flashing && now == next_flash) {
5436         flash_window(1);
5437     }
5438 }
5439
5440 /*
5441  * Manage window caption / taskbar flashing, if enabled.
5442  * 0 = stop, 1 = maintain, 2 = start
5443  */
5444 static void flash_window(int mode)
5445 {
5446     int beep_ind = conf_get_int(conf, CONF_beep_ind);
5447     if ((mode == 0) || (beep_ind == B_IND_DISABLED)) {
5448         /* stop */
5449         if (flashing) {
5450             flashing = 0;
5451             if (p_FlashWindowEx)
5452                 flash_window_ex(FLASHW_STOP, 0, 0);
5453             else
5454                 FlashWindow(hwnd, FALSE);
5455         }
5456
5457     } else if (mode == 2) {
5458         /* start */
5459         if (!flashing) {
5460             flashing = 1;
5461             if (p_FlashWindowEx) {
5462                 /* For so-called "steady" mode, we use uCount=2, which
5463                  * seems to be the traditional number of flashes used
5464                  * by user notifications (e.g., by Explorer).
5465                  * uCount=0 appears to enable continuous flashing, per
5466                  * "flashing" mode, although I haven't seen this
5467                  * documented. */
5468                 flash_window_ex(FLASHW_ALL | FLASHW_TIMER,
5469                                 (beep_ind == B_IND_FLASH ? 0 : 2),
5470                                 0 /* system cursor blink rate */);
5471                 /* No need to schedule timer */
5472             } else {
5473                 FlashWindow(hwnd, TRUE);
5474                 next_flash = schedule_timer(450, flash_window_timer, hwnd);
5475             }
5476         }
5477
5478     } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) {
5479         /* maintain */
5480         if (flashing && !p_FlashWindowEx) {
5481             FlashWindow(hwnd, TRUE);    /* toggle */
5482             next_flash = schedule_timer(450, flash_window_timer, hwnd);
5483         }
5484     }
5485 }
5486
5487 /*
5488  * Beep.
5489  */
5490 void do_beep(void *frontend, int mode)
5491 {
5492     if (mode == BELL_DEFAULT) {
5493         /*
5494          * For MessageBeep style bells, we want to be careful of
5495          * timing, because they don't have the nice property of
5496          * PlaySound bells that each one cancels the previous
5497          * active one. So we limit the rate to one per 50ms or so.
5498          */
5499         static long lastbeep = 0;
5500         long beepdiff;
5501
5502         beepdiff = GetTickCount() - lastbeep;
5503         if (beepdiff >= 0 && beepdiff < 50)
5504             return;
5505         MessageBeep(MB_OK);
5506         /*
5507          * The above MessageBeep call takes time, so we record the
5508          * time _after_ it finishes rather than before it starts.
5509          */
5510         lastbeep = GetTickCount();
5511     } else if (mode == BELL_WAVEFILE) {
5512         Filename *bell_wavefile = conf_get_filename(conf, CONF_bell_wavefile);
5513         if (!PlaySound(bell_wavefile->path, NULL,
5514                        SND_ASYNC | SND_FILENAME)) {
5515             char buf[sizeof(bell_wavefile->path) + 80];
5516             char otherbuf[100];
5517             sprintf(buf, "Unable to play sound file\n%s\n"
5518                     "Using default sound instead", bell_wavefile->path);
5519             sprintf(otherbuf, "%.70s Sound Error", appname);
5520             MessageBox(hwnd, buf, otherbuf,
5521                        MB_OK | MB_ICONEXCLAMATION);
5522             conf_set_int(conf, CONF_beep, BELL_DEFAULT);
5523         }
5524     } else if (mode == BELL_PCSPEAKER) {
5525         static long lastbeep = 0;
5526         long beepdiff;
5527
5528         beepdiff = GetTickCount() - lastbeep;
5529         if (beepdiff >= 0 && beepdiff < 50)
5530             return;
5531
5532         /*
5533          * We must beep in different ways depending on whether this
5534          * is a 95-series or NT-series OS.
5535          */
5536         if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
5537             Beep(800, 100);
5538         else
5539             MessageBeep(-1);
5540         lastbeep = GetTickCount();
5541     }
5542     /* Otherwise, either visual bell or disabled; do nothing here */
5543     if (!term->has_focus) {
5544         flash_window(2);               /* start */
5545     }
5546 }
5547
5548 /*
5549  * Minimise or restore the window in response to a server-side
5550  * request.
5551  */
5552 void set_iconic(void *frontend, int iconic)
5553 {
5554     if (IsIconic(hwnd)) {
5555         if (!iconic)
5556             ShowWindow(hwnd, SW_RESTORE);
5557     } else {
5558         if (iconic)
5559             ShowWindow(hwnd, SW_MINIMIZE);
5560     }
5561 }
5562
5563 /*
5564  * Move the window in response to a server-side request.
5565  */
5566 void move_window(void *frontend, int x, int y)
5567 {
5568     int resize_action = conf_get_int(conf, CONF_resize_action);
5569     if (resize_action == RESIZE_DISABLED || 
5570         resize_action == RESIZE_FONT ||
5571         IsZoomed(hwnd))
5572        return;
5573
5574     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
5575 }
5576
5577 /*
5578  * Move the window to the top or bottom of the z-order in response
5579  * to a server-side request.
5580  */
5581 void set_zorder(void *frontend, int top)
5582 {
5583     if (conf_get_int(conf, CONF_alwaysontop))
5584         return;                        /* ignore */
5585     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
5586                  SWP_NOMOVE | SWP_NOSIZE);
5587 }
5588
5589 /*
5590  * Refresh the window in response to a server-side request.
5591  */
5592 void refresh_window(void *frontend)
5593 {
5594     InvalidateRect(hwnd, NULL, TRUE);
5595 }
5596
5597 /*
5598  * Maximise or restore the window in response to a server-side
5599  * request.
5600  */
5601 void set_zoomed(void *frontend, int zoomed)
5602 {
5603     if (IsZoomed(hwnd)) {
5604         if (!zoomed)
5605             ShowWindow(hwnd, SW_RESTORE);
5606     } else {
5607         if (zoomed)
5608             ShowWindow(hwnd, SW_MAXIMIZE);
5609     }
5610 }
5611
5612 /*
5613  * Report whether the window is iconic, for terminal reports.
5614  */
5615 int is_iconic(void *frontend)
5616 {
5617     return IsIconic(hwnd);
5618 }
5619
5620 /*
5621  * Report the window's position, for terminal reports.
5622  */
5623 void get_window_pos(void *frontend, int *x, int *y)
5624 {
5625     RECT r;
5626     GetWindowRect(hwnd, &r);
5627     *x = r.left;
5628     *y = r.top;
5629 }
5630
5631 /*
5632  * Report the window's pixel size, for terminal reports.
5633  */
5634 void get_window_pixels(void *frontend, int *x, int *y)
5635 {
5636     RECT r;
5637     GetWindowRect(hwnd, &r);
5638     *x = r.right - r.left;
5639     *y = r.bottom - r.top;
5640 }
5641
5642 /*
5643  * Return the window or icon title.
5644  */
5645 char *get_window_title(void *frontend, int icon)
5646 {
5647     return icon ? icon_name : window_name;
5648 }
5649
5650 /*
5651  * See if we're in full-screen mode.
5652  */
5653 static int is_full_screen()
5654 {
5655     if (!IsZoomed(hwnd))
5656         return FALSE;
5657     if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CAPTION)
5658         return FALSE;
5659     return TRUE;
5660 }
5661
5662 /* Get the rect/size of a full screen window using the nearest available
5663  * monitor in multimon systems; default to something sensible if only
5664  * one monitor is present. */
5665 static int get_fullscreen_rect(RECT * ss)
5666 {
5667 #if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
5668         HMONITOR mon;
5669         MONITORINFO mi;
5670         mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
5671         mi.cbSize = sizeof(mi);
5672         GetMonitorInfo(mon, &mi);
5673
5674         /* structure copy */
5675         *ss = mi.rcMonitor;
5676         return TRUE;
5677 #else
5678 /* could also use code like this:
5679         ss->left = ss->top = 0;
5680         ss->right = GetSystemMetrics(SM_CXSCREEN);
5681         ss->bottom = GetSystemMetrics(SM_CYSCREEN);
5682 */ 
5683         return GetClientRect(GetDesktopWindow(), ss);
5684 #endif
5685 }
5686
5687
5688 /*
5689  * Go full-screen. This should only be called when we are already
5690  * maximised.
5691  */
5692 static void make_full_screen()
5693 {
5694     DWORD style;
5695         RECT ss;
5696
5697     assert(IsZoomed(hwnd));
5698
5699         if (is_full_screen())
5700                 return;
5701         
5702     /* Remove the window furniture. */
5703     style = GetWindowLongPtr(hwnd, GWL_STYLE);
5704     style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
5705     if (conf_get_int(conf, CONF_scrollbar_in_fullscreen))
5706         style |= WS_VSCROLL;
5707     else
5708         style &= ~WS_VSCROLL;
5709     SetWindowLongPtr(hwnd, GWL_STYLE, style);
5710
5711     /* Resize ourselves to exactly cover the nearest monitor. */
5712         get_fullscreen_rect(&ss);
5713     SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
5714                         ss.right - ss.left,
5715                         ss.bottom - ss.top,
5716                         SWP_FRAMECHANGED);
5717
5718     /* We may have changed size as a result */
5719
5720     reset_window(0);
5721
5722     /* Tick the menu item in the System and context menus. */
5723     {
5724         int i;
5725         for (i = 0; i < lenof(popup_menus); i++)
5726             CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED);
5727     }
5728 }
5729
5730 /*
5731  * Clear the full-screen attributes.
5732  */
5733 static void clear_full_screen()
5734 {
5735     DWORD oldstyle, style;
5736
5737     /* Reinstate the window furniture. */
5738     style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);
5739     style |= WS_CAPTION | WS_BORDER;
5740     if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
5741         style &= ~WS_THICKFRAME;
5742     else
5743         style |= WS_THICKFRAME;
5744     if (conf_get_int(conf, CONF_scrollbar))
5745         style |= WS_VSCROLL;
5746     else
5747         style &= ~WS_VSCROLL;
5748     if (style != oldstyle) {
5749         SetWindowLongPtr(hwnd, GWL_STYLE, style);
5750         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
5751                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
5752                      SWP_FRAMECHANGED);
5753     }
5754
5755     /* Untick the menu item in the System and context menus. */
5756     {
5757         int i;
5758         for (i = 0; i < lenof(popup_menus); i++)
5759             CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED);
5760     }
5761 }
5762
5763 /*
5764  * Toggle full-screen mode.
5765  */
5766 static void flip_full_screen()
5767 {
5768     if (is_full_screen()) {
5769         ShowWindow(hwnd, SW_RESTORE);
5770     } else if (IsZoomed(hwnd)) {
5771         make_full_screen();
5772     } else {
5773         SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
5774         ShowWindow(hwnd, SW_MAXIMIZE);
5775     }
5776 }
5777
5778 void frontend_keypress(void *handle)
5779 {
5780     /*
5781      * Keypress termination in non-Close-On-Exit mode is not
5782      * currently supported in PuTTY proper, because the window
5783      * always has a perfectly good Close button anyway. So we do
5784      * nothing here.
5785      */
5786     return;
5787 }
5788
5789 int from_backend(void *frontend, int is_stderr, const char *data, int len)
5790 {
5791     return term_data(term, is_stderr, data, len);
5792 }
5793
5794 int from_backend_untrusted(void *frontend, const char *data, int len)
5795 {
5796     return term_data_untrusted(term, data, len);
5797 }
5798
5799 int from_backend_eof(void *frontend)
5800 {
5801     return TRUE;   /* do respond to incoming EOF with outgoing */
5802 }
5803
5804 int get_userpass_input(prompts_t *p, unsigned char *in, int inlen)
5805 {
5806     int ret;
5807     ret = cmdline_get_passwd_input(p, in, inlen);
5808     if (ret == -1)
5809         ret = term_get_userpass_input(term, p, in, inlen);
5810     return ret;
5811 }
5812
5813 void agent_schedule_callback(void (*callback)(void *, void *, int),
5814                              void *callback_ctx, void *data, int len)
5815 {
5816     struct agent_callback *c = snew(struct agent_callback);
5817     c->callback = callback;
5818     c->callback_ctx = callback_ctx;
5819     c->data = data;
5820     c->len = len;
5821     PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
5822 }