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