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