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