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