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