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