]> asedeno.scripts.mit.edu Git - PuTTY_svn.git/blob - window.c
15f23870cdb753e5fe26a5d6e76bad7bf34358f5
[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                     if ((lParam - IDM_SAVED_MIN) / 16 < nsessions) {
1565                         char *session =
1566                             sessions[(lParam - IDM_SAVED_MIN) / 16];
1567                         cl = smalloc(16 + strlen(session));
1568                                        /* 8, but play safe */
1569                         if (!cl)
1570                             cl = NULL;    
1571                                        /* not a very important failure mode */
1572                         else {
1573                             sprintf(cl, "putty @%s", session);
1574                             freecl = TRUE;
1575                         }
1576                     } else
1577                         break;
1578                 } else
1579                     cl = NULL;
1580
1581                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1582                 si.cb = sizeof(si);
1583                 si.lpReserved = NULL;
1584                 si.lpDesktop = NULL;
1585                 si.lpTitle = NULL;
1586                 si.dwFlags = 0;
1587                 si.cbReserved2 = 0;
1588                 si.lpReserved2 = NULL;
1589                 CreateProcess(b, cl, NULL, NULL, TRUE,
1590                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1591
1592                 if (filemap)
1593                     CloseHandle(filemap);
1594                 if (freecl)
1595                     sfree(cl);
1596             }
1597             break;
1598           case IDM_RECONF:
1599             {
1600                 Config prev_cfg;
1601                 int init_lvl = 1;
1602
1603                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1604                 prev_cfg = cfg;
1605
1606                 if (!do_reconfig(hwnd))
1607                     break;
1608
1609                 {
1610                     /* Disable full-screen if resizing forbidden */
1611                     HMENU m = GetSystemMenu (hwnd, FALSE);
1612                     EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND | 
1613                                    (cfg.resize_action == RESIZE_DISABLED)
1614                                    ? MF_GRAYED : MF_ENABLED);
1615                     /* Gracefully unzoom if necessary */
1616                     if (full_screen &&
1617                         (cfg.resize_action == RESIZE_DISABLED)) {
1618                         flip_full_screen();
1619                     }
1620                 }
1621
1622                 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1623                     prev_cfg.logtype != cfg.logtype) {
1624                     logfclose();       /* reset logging */
1625                     logfopen();
1626                 }
1627
1628                 sfree(logpal);
1629                 /*
1630                  * Flush the line discipline's edit buffer in the
1631                  * case where local editing has just been disabled.
1632                  */
1633                 ldisc_send(NULL, 0, 0);
1634                 if (pal)
1635                     DeleteObject(pal);
1636                 logpal = NULL;
1637                 pal = NULL;
1638                 cfgtopalette();
1639                 init_palette();
1640
1641                 /* Screen size changed ? */
1642                 if (cfg.height != prev_cfg.height ||
1643                     cfg.width != prev_cfg.width ||
1644                     cfg.savelines != prev_cfg.savelines ||
1645                     cfg.resize_action == RESIZE_FONT ||
1646                     cfg.resize_action == RESIZE_DISABLED)
1647                     term_size(cfg.height, cfg.width, cfg.savelines);
1648
1649                 /* Enable or disable the scroll bar, etc */
1650                 {
1651                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1652                     LONG nexflag, exflag =
1653                         GetWindowLong(hwnd, GWL_EXSTYLE);
1654
1655                     nexflag = exflag;
1656                     if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1657                         if (cfg.alwaysontop) {
1658                             nexflag |= WS_EX_TOPMOST;
1659                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1660                                          SWP_NOMOVE | SWP_NOSIZE);
1661                         } else {
1662                             nexflag &= ~(WS_EX_TOPMOST);
1663                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1664                                          SWP_NOMOVE | SWP_NOSIZE);
1665                         }
1666                     }
1667                     if (cfg.sunken_edge)
1668                         nexflag |= WS_EX_CLIENTEDGE;
1669                     else
1670                         nexflag &= ~(WS_EX_CLIENTEDGE);
1671
1672                     nflg = flag;
1673                     if (full_screen ?
1674                         cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1675                         nflg |= WS_VSCROLL;
1676                     else
1677                         nflg &= ~WS_VSCROLL;
1678                     if (cfg.resize_action == RESIZE_DISABLED)
1679                         nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1680                     else
1681                         nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1682
1683                     if (nflg != flag || nexflag != exflag) {
1684                         if (nflg != flag)
1685                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1686                         if (nexflag != exflag)
1687                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1688
1689                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1690                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1691                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1692                                      SWP_FRAMECHANGED);
1693
1694                         init_lvl = 2;
1695                     }
1696                 }
1697
1698                 /* Oops */
1699                 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1700                     force_normal(hwnd);
1701                     init_lvl = 2;
1702                 }
1703
1704                 set_title(cfg.wintitle);
1705                 if (IsIconic(hwnd)) {
1706                     SetWindowText(hwnd,
1707                                   cfg.win_name_always ? window_name :
1708                                   icon_name);
1709                 }
1710
1711                 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1712                     strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1713                     cfg.fontisbold != prev_cfg.fontisbold ||
1714                     cfg.fontheight != prev_cfg.fontheight ||
1715                     cfg.fontcharset != prev_cfg.fontcharset ||
1716                     cfg.vtmode != prev_cfg.vtmode ||
1717                     cfg.bold_colour != prev_cfg.bold_colour ||
1718                     cfg.resize_action == RESIZE_DISABLED ||
1719                     cfg.resize_action == RESIZE_EITHER ||
1720                     (cfg.resize_action != prev_cfg.resize_action))
1721                     init_lvl = 2;
1722
1723                 InvalidateRect(hwnd, NULL, TRUE);
1724                 reset_window(init_lvl);
1725                 net_pending_errors();
1726             }
1727             break;
1728           case IDM_COPYALL:
1729             term_copyall();
1730             break;
1731           case IDM_CLRSB:
1732             term_clrsb();
1733             break;
1734           case IDM_RESET:
1735             term_pwron();
1736             break;
1737           case IDM_TEL_AYT:
1738             back->special(TS_AYT);
1739             net_pending_errors();
1740             break;
1741           case IDM_TEL_BRK:
1742             back->special(TS_BRK);
1743             net_pending_errors();
1744             break;
1745           case IDM_TEL_SYNCH:
1746             back->special(TS_SYNCH);
1747             net_pending_errors();
1748             break;
1749           case IDM_TEL_EC:
1750             back->special(TS_EC);
1751             net_pending_errors();
1752             break;
1753           case IDM_TEL_EL:
1754             back->special(TS_EL);
1755             net_pending_errors();
1756             break;
1757           case IDM_TEL_GA:
1758             back->special(TS_GA);
1759             net_pending_errors();
1760             break;
1761           case IDM_TEL_NOP:
1762             back->special(TS_NOP);
1763             net_pending_errors();
1764             break;
1765           case IDM_TEL_ABORT:
1766             back->special(TS_ABORT);
1767             net_pending_errors();
1768             break;
1769           case IDM_TEL_AO:
1770             back->special(TS_AO);
1771             net_pending_errors();
1772             break;
1773           case IDM_TEL_IP:
1774             back->special(TS_IP);
1775             net_pending_errors();
1776             break;
1777           case IDM_TEL_SUSP:
1778             back->special(TS_SUSP);
1779             net_pending_errors();
1780             break;
1781           case IDM_TEL_EOR:
1782             back->special(TS_EOR);
1783             net_pending_errors();
1784             break;
1785           case IDM_TEL_EOF:
1786             back->special(TS_EOF);
1787             net_pending_errors();
1788             break;
1789           case IDM_ABOUT:
1790             showabout(hwnd);
1791             break;
1792           case IDM_HELP:
1793             WinHelp(hwnd, help_path,
1794                     help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1795             break;
1796           case SC_MOUSEMENU:
1797             /*
1798              * We get this if the System menu has been activated
1799              * using the mouse.
1800              */
1801             show_mouseptr(1);
1802             break;
1803           case SC_KEYMENU:
1804             /*
1805              * We get this if the System menu has been activated
1806              * using the keyboard. This might happen from within
1807              * TranslateKey, in which case it really wants to be
1808              * followed by a `space' character to actually _bring
1809              * the menu up_ rather than just sitting there in
1810              * `ready to appear' state.
1811              */
1812             show_mouseptr(1);          /* make sure pointer is visible */
1813             if( lParam == 0 )
1814                 PostMessage(hwnd, WM_CHAR, ' ', 0);
1815             break;
1816           case IDM_FULLSCREEN:
1817                 flip_full_screen();     
1818                 break;
1819           default:
1820             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1821                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1822             }
1823         }
1824         break;
1825
1826 #define X_POS(l) ((int)(short)LOWORD(l))
1827 #define Y_POS(l) ((int)(short)HIWORD(l))
1828
1829 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1830 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1831 #define WHEEL_DELTA 120
1832       case WM_MOUSEWHEEL:
1833         {
1834             wheel_accumulator += (short) HIWORD(wParam);
1835             wParam = LOWORD(wParam);
1836
1837             /* process events when the threshold is reached */
1838             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1839                 int b;
1840
1841                 /* reduce amount for next time */
1842                 if (wheel_accumulator > 0) {
1843                     b = MBT_WHEEL_UP;
1844                     wheel_accumulator -= WHEEL_DELTA;
1845                 } else if (wheel_accumulator < 0) {
1846                     b = MBT_WHEEL_DOWN;
1847                     wheel_accumulator += WHEEL_DELTA;
1848                 } else
1849                     break;
1850
1851                 if (send_raw_mouse) {
1852                     /* send a mouse-down followed by a mouse up */
1853                     
1854                     term_mouse(b,
1855                                MA_CLICK,
1856                                TO_CHR_X(X_POS(lParam)),
1857                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1858                                wParam & MK_CONTROL, is_alt_pressed());
1859                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1860                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1861                                wParam & MK_CONTROL, is_alt_pressed());
1862                 } else {
1863                     /* trigger a scroll */
1864                     term_scroll(0,
1865                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1866                 }
1867             }
1868             return 0;
1869         }
1870       case WM_LBUTTONDOWN:
1871       case WM_MBUTTONDOWN:
1872       case WM_RBUTTONDOWN:
1873       case WM_LBUTTONUP:
1874       case WM_MBUTTONUP:
1875       case WM_RBUTTONUP:
1876         {
1877             int button, press;
1878
1879             switch (message) {
1880               case WM_LBUTTONDOWN:
1881                 button = MBT_LEFT;
1882                 press = 1;
1883                 break;
1884               case WM_MBUTTONDOWN:
1885                 button = MBT_MIDDLE;
1886                 press = 1;
1887                 break;
1888               case WM_RBUTTONDOWN:
1889                 button = MBT_RIGHT;
1890                 press = 1;
1891                 break;
1892               case WM_LBUTTONUP:
1893                 button = MBT_LEFT;
1894                 press = 0;
1895                 break;
1896               case WM_MBUTTONUP:
1897                 button = MBT_MIDDLE;
1898                 press = 0;
1899                 break;
1900               case WM_RBUTTONUP:
1901                 button = MBT_RIGHT;
1902                 press = 0;
1903                 break;
1904               default:
1905                 button = press = 0;    /* shouldn't happen */
1906             }
1907             show_mouseptr(1);
1908             /*
1909              * Special case: in full-screen mode, if the left
1910              * button is clicked in the very top left corner of the
1911              * window, we put up the System menu instead of doing
1912              * selection.
1913              */
1914             if (full_screen && press && button == MBT_LEFT &&
1915                 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
1916                 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
1917                 return 0;
1918             }
1919             if (press) {
1920                 click(button,
1921                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1922                       wParam & MK_SHIFT, wParam & MK_CONTROL,
1923                       is_alt_pressed());
1924                 SetCapture(hwnd);
1925             } else {
1926                 term_mouse(button, MA_RELEASE,
1927                            TO_CHR_X(X_POS(lParam)),
1928                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1929                            wParam & MK_CONTROL, is_alt_pressed());
1930                 ReleaseCapture();
1931             }
1932         }
1933         return 0;
1934       case WM_MOUSEMOVE:
1935         show_mouseptr(1);
1936         /*
1937          * Add the mouse position and message time to the random
1938          * number noise.
1939          */
1940         noise_ultralight(lParam);
1941
1942         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1943             Mouse_Button b;
1944             if (wParam & MK_LBUTTON)
1945                 b = MBT_LEFT;
1946             else if (wParam & MK_MBUTTON)
1947                 b = MBT_MIDDLE;
1948             else
1949                 b = MBT_RIGHT;
1950             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1951                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1952                        wParam & MK_CONTROL, is_alt_pressed());
1953         }
1954         return 0;
1955       case WM_NCMOUSEMOVE:
1956         show_mouseptr(1);
1957         noise_ultralight(lParam);
1958         return 0;
1959       case WM_IGNORE_CLIP:
1960         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1961         break;
1962       case WM_DESTROYCLIPBOARD:
1963         if (!ignore_clip)
1964             term_deselect();
1965         ignore_clip = FALSE;
1966         return 0;
1967       case WM_PAINT:
1968         {
1969             PAINTSTRUCT p;
1970             HideCaret(hwnd);
1971             hdc = BeginPaint(hwnd, &p);
1972             if (pal) {
1973                 SelectPalette(hdc, pal, TRUE);
1974                 RealizePalette(hdc);
1975             }
1976             term_paint(hdc, 
1977                        (p.rcPaint.left-offset_width)/font_width,
1978                        (p.rcPaint.top-offset_height)/font_height,
1979                        (p.rcPaint.right-offset_width-1)/font_width,
1980                        (p.rcPaint.bottom-offset_height-1)/font_height);
1981
1982             if (p.fErase ||
1983                 p.rcPaint.left  < offset_width  ||
1984                 p.rcPaint.top   < offset_height ||
1985                 p.rcPaint.right >= offset_width + font_width*cols ||
1986                 p.rcPaint.bottom>= offset_height + font_height*rows)
1987             {
1988                 HBRUSH fillcolour, oldbrush;
1989                 HPEN   edge, oldpen;
1990                 fillcolour = CreateSolidBrush (
1991                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1992                 oldbrush = SelectObject(hdc, fillcolour);
1993                 edge = CreatePen(PS_SOLID, 0, 
1994                                     colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1995                 oldpen = SelectObject(hdc, edge);
1996
1997                 ExcludeClipRect(hdc, 
1998                         offset_width, offset_height,
1999                         offset_width+font_width*cols,
2000                         offset_height+font_height*rows);
2001
2002                 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, 
2003                           p.rcPaint.right, p.rcPaint.bottom);
2004
2005                 // SelectClipRgn(hdc, NULL);
2006
2007                 SelectObject(hdc, oldbrush);
2008                 DeleteObject(fillcolour);
2009                 SelectObject(hdc, oldpen);
2010                 DeleteObject(edge);
2011             }
2012             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2013             SelectObject(hdc, GetStockObject(WHITE_PEN));
2014             EndPaint(hwnd, &p);
2015             ShowCaret(hwnd);
2016         }
2017         return 0;
2018       case WM_NETEVENT:
2019         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2020          * but the only one that's likely to try to overload us is FD_READ.
2021          * This means buffering just one is fine.
2022          */
2023         if (pending_netevent)
2024             enact_pending_netevent();
2025
2026         pending_netevent = TRUE;
2027         pend_netevent_wParam = wParam;
2028         pend_netevent_lParam = lParam;
2029         if (WSAGETSELECTEVENT(lParam) != FD_READ)
2030             enact_pending_netevent();
2031
2032         time(&last_movement);
2033         return 0;
2034       case WM_SETFOCUS:
2035         has_focus = TRUE;
2036         CreateCaret(hwnd, caretbm, font_width, font_height);
2037         ShowCaret(hwnd);
2038         flash_window(0);               /* stop */
2039         compose_state = 0;
2040         term_out();
2041         term_update();
2042         break;
2043       case WM_KILLFOCUS:
2044         if (full_screen) flip_full_screen();
2045         show_mouseptr(1);
2046         has_focus = FALSE;
2047         DestroyCaret();
2048         term_out();
2049         term_update();
2050         break;
2051       case WM_ENTERSIZEMOVE:
2052 #ifdef RDB_DEBUG_PATCH
2053         debug((27, "WM_ENTERSIZEMOVE"));
2054 #endif
2055         EnableSizeTip(1);
2056         resizing = TRUE;
2057         need_backend_resize = FALSE;
2058         break;
2059       case WM_EXITSIZEMOVE:
2060         EnableSizeTip(0);
2061         resizing = FALSE;
2062 #ifdef RDB_DEBUG_PATCH
2063         debug((27, "WM_EXITSIZEMOVE"));
2064 #endif
2065         if (need_backend_resize) {
2066             term_size(cfg.height, cfg.width, cfg.savelines);
2067             InvalidateRect(hwnd, NULL, TRUE);
2068         }
2069         break;
2070       case WM_SIZING:
2071         /*
2072          * This does two jobs:
2073          * 1) Keep the sizetip uptodate
2074          * 2) Make sure the window size is _stepped_ in units of the font size.
2075          */
2076         if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2077             int width, height, w, h, ew, eh;
2078             LPRECT r = (LPRECT) lParam;
2079
2080             if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2081                     (cfg.height != rows || cfg.width != cols )) {
2082                 /* 
2083                  * Great! It seems that both the terminal size and the
2084                  * font size have been changed and the user is now dragging.
2085                  * 
2086                  * It will now be difficult to get back to the configured
2087                  * font size!
2088                  *
2089                  * This would be easier but it seems to be too confusing.
2090
2091                 term_size(cfg.height, cfg.width, cfg.savelines);
2092                 reset_window(2);
2093                  */
2094                 cfg.height=rows; cfg.width=cols;
2095
2096                 InvalidateRect(hwnd, NULL, TRUE);
2097                 need_backend_resize = TRUE;
2098             }
2099
2100             width = r->right - r->left - extra_width;
2101             height = r->bottom - r->top - extra_height;
2102             w = (width + font_width / 2) / font_width;
2103             if (w < 1)
2104                 w = 1;
2105             h = (height + font_height / 2) / font_height;
2106             if (h < 1)
2107                 h = 1;
2108             UpdateSizeTip(hwnd, w, h);
2109             ew = width - w * font_width;
2110             eh = height - h * font_height;
2111             if (ew != 0) {
2112                 if (wParam == WMSZ_LEFT ||
2113                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2114                     r->left += ew;
2115                 else
2116                     r->right -= ew;
2117             }
2118             if (eh != 0) {
2119                 if (wParam == WMSZ_TOP ||
2120                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2121                     r->top += eh;
2122                 else
2123                     r->bottom -= eh;
2124             }
2125             if (ew || eh)
2126                 return 1;
2127             else
2128                 return 0;
2129         } else {
2130             int width, height, w, h, rv = 0;
2131             int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2132             int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2133             LPRECT r = (LPRECT) lParam;
2134
2135             width = r->right - r->left - ex_width;
2136             height = r->bottom - r->top - ex_height;
2137
2138             w = (width + cols/2)/cols;
2139             h = (height + rows/2)/rows;
2140             if ( r->right != r->left + w*cols + ex_width) 
2141                 rv = 1;
2142
2143             if (wParam == WMSZ_LEFT ||
2144                 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2145                 r->left = r->right - w*cols - ex_width;
2146             else
2147                 r->right = r->left + w*cols + ex_width;
2148
2149             if (r->bottom != r->top + h*rows + ex_height)
2150                 rv = 1;
2151
2152             if (wParam == WMSZ_TOP ||
2153                 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2154                 r->top = r->bottom - h*rows - ex_height;
2155             else
2156                 r->bottom = r->top + h*rows + ex_height;
2157
2158             return rv;
2159         }
2160         /* break;  (never reached) */
2161       case WM_SIZE:
2162 #ifdef RDB_DEBUG_PATCH
2163         debug((27, "WM_SIZE %s (%d,%d)",
2164                 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2165                 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2166                 (wParam == SIZE_RESTORED && resizing) ? "to":
2167                 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2168                 "...",
2169             LOWORD(lParam), HIWORD(lParam)));
2170 #endif
2171         if (wParam == SIZE_MINIMIZED)
2172             SetWindowText(hwnd,
2173                           cfg.win_name_always ? window_name : icon_name);
2174         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2175             SetWindowText(hwnd, window_name);
2176
2177         if (cfg.resize_action == RESIZE_DISABLED) {
2178             /* A resize, well it better be a minimize. */
2179             reset_window(-1);
2180         } else {
2181
2182             int width, height, w, h;
2183
2184             width = LOWORD(lParam);
2185             height = HIWORD(lParam);
2186
2187             if (!resizing) {
2188                 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2189                     was_zoomed = 1;
2190                     prev_rows = rows;
2191                     prev_cols = cols;
2192                     if (cfg.resize_action == RESIZE_TERM) {
2193                         w = width / font_width;
2194                         if (w < 1) w = 1;
2195                         h = height / font_height;
2196                         if (h < 1) h = 1;
2197
2198                         term_size(h, w, cfg.savelines);
2199                     }
2200                     reset_window(0);
2201                 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2202                     was_zoomed = 0;
2203                     if (cfg.resize_action == RESIZE_TERM)
2204                         term_size(prev_rows, prev_cols, cfg.savelines);
2205                     if (cfg.resize_action != RESIZE_FONT)
2206                         reset_window(2);
2207                     else
2208                         reset_window(0);
2209                 }
2210                 /* This is an unexpected resize, these will normally happen
2211                  * if the window is too large. Probably either the user
2212                  * selected a huge font or the screen size has changed.
2213                  *
2214                  * This is also called with minimize.
2215                  */
2216                 else reset_window(-1);
2217             }
2218
2219             /*
2220              * Don't call back->size in mid-resize. (To prevent
2221              * massive numbers of resize events getting sent
2222              * down the connection during an NT opaque drag.)
2223              */
2224             if (resizing) {
2225                 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2226                     need_backend_resize = TRUE;
2227                     w = (width-cfg.window_border*2) / font_width;
2228                     if (w < 1) w = 1;
2229                     h = (height-cfg.window_border*2) / font_height;
2230                     if (h < 1) h = 1;
2231
2232                     cfg.height = h;
2233                     cfg.width = w;
2234                 } else 
2235                     reset_window(0);
2236             }
2237         }
2238         return 0;
2239       case WM_VSCROLL:
2240         switch (LOWORD(wParam)) {
2241           case SB_BOTTOM:
2242             term_scroll(-1, 0);
2243             break;
2244           case SB_TOP:
2245             term_scroll(+1, 0);
2246             break;
2247           case SB_LINEDOWN:
2248             term_scroll(0, +1);
2249             break;
2250           case SB_LINEUP:
2251             term_scroll(0, -1);
2252             break;
2253           case SB_PAGEDOWN:
2254             term_scroll(0, +rows / 2);
2255             break;
2256           case SB_PAGEUP:
2257             term_scroll(0, -rows / 2);
2258             break;
2259           case SB_THUMBPOSITION:
2260           case SB_THUMBTRACK:
2261             term_scroll(1, HIWORD(wParam));
2262             break;
2263         }
2264         break;
2265       case WM_PALETTECHANGED:
2266         if ((HWND) wParam != hwnd && pal != NULL) {
2267             HDC hdc = get_ctx();
2268             if (hdc) {
2269                 if (RealizePalette(hdc) > 0)
2270                     UpdateColors(hdc);
2271                 free_ctx(hdc);
2272             }
2273         }
2274         break;
2275       case WM_QUERYNEWPALETTE:
2276         if (pal != NULL) {
2277             HDC hdc = get_ctx();
2278             if (hdc) {
2279                 if (RealizePalette(hdc) > 0)
2280                     UpdateColors(hdc);
2281                 free_ctx(hdc);
2282                 return TRUE;
2283             }
2284         }
2285         return FALSE;
2286       case WM_KEYDOWN:
2287       case WM_SYSKEYDOWN:
2288       case WM_KEYUP:
2289       case WM_SYSKEYUP:
2290         /*
2291          * Add the scan code and keypress timing to the random
2292          * number noise.
2293          */
2294         noise_ultralight(lParam);
2295
2296         /*
2297          * We don't do TranslateMessage since it disassociates the
2298          * resulting CHAR message from the KEYDOWN that sparked it,
2299          * which we occasionally don't want. Instead, we process
2300          * KEYDOWN, and call the Win32 translator functions so that
2301          * we get the translations under _our_ control.
2302          */
2303         {
2304             unsigned char buf[20];
2305             int len;
2306
2307             if (wParam == VK_PROCESSKEY) {
2308                 MSG m;
2309                 m.hwnd = hwnd;
2310                 m.message = WM_KEYDOWN;
2311                 m.wParam = wParam;
2312                 m.lParam = lParam & 0xdfff;
2313                 TranslateMessage(&m);
2314             } else {
2315                 len = TranslateKey(message, wParam, lParam, buf);
2316                 if (len == -1)
2317                     return DefWindowProc(hwnd, message, wParam, lParam);
2318
2319                 if (len != 0) {
2320                     /*
2321                      * Interrupt an ongoing paste. I'm not sure
2322                      * this is sensible, but for the moment it's
2323                      * preferable to having to faff about buffering
2324                      * things.
2325                      */
2326                     term_nopaste();
2327
2328                     /*
2329                      * We need not bother about stdin backlogs
2330                      * here, because in GUI PuTTY we can't do
2331                      * anything about it anyway; there's no means
2332                      * of asking Windows to hold off on KEYDOWN
2333                      * messages. We _have_ to buffer everything
2334                      * we're sent.
2335                      */
2336                     ldisc_send(buf, len, 1);
2337                     show_mouseptr(0);
2338                 }
2339             }
2340         }
2341         net_pending_errors();
2342         return 0;
2343       case WM_INPUTLANGCHANGE:
2344         /* wParam == Font number */
2345         /* lParam == Locale */
2346         set_input_locale((HKL)lParam);
2347         break;
2348       case WM_IME_NOTIFY:
2349         if(wParam == IMN_SETOPENSTATUS) {
2350             HIMC hImc = ImmGetContext(hwnd);
2351             ImmSetCompositionFont(hImc, &lfont);
2352             ImmReleaseContext(hwnd, hImc);
2353             return 0;
2354         }
2355         break;
2356       case WM_IME_COMPOSITION:
2357         {
2358             HIMC hIMC;
2359             int n;
2360             char *buff;
2361    
2362             if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS || 
2363                 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2364
2365             if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2366                 break; /* fall back to DefWindowProc */
2367
2368             hIMC = ImmGetContext(hwnd);
2369             n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2370
2371             if (n > 0) {
2372                 buff = (char*) smalloc(n);
2373                 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2374                 luni_send((unsigned short *)buff, n / 2, 1);
2375                 free(buff);
2376             }
2377             ImmReleaseContext(hwnd, hIMC);
2378             return 1;
2379         }
2380
2381       case WM_IME_CHAR:
2382         if (wParam & 0xFF00) {
2383             unsigned char buf[2];
2384
2385             buf[1] = wParam;
2386             buf[0] = wParam >> 8;
2387             lpage_send(kbd_codepage, buf, 2, 1);
2388         } else {
2389             char c = (unsigned char) wParam;
2390             lpage_send(kbd_codepage, &c, 1, 1);
2391         }
2392         return (0);
2393       case WM_CHAR:
2394       case WM_SYSCHAR:
2395         /*
2396          * Nevertheless, we are prepared to deal with WM_CHAR
2397          * messages, should they crop up. So if someone wants to
2398          * post the things to us as part of a macro manoeuvre,
2399          * we're ready to cope.
2400          */
2401         {
2402             char c = (unsigned char)wParam;
2403             lpage_send(CP_ACP, &c, 1, 1);
2404         }
2405         return 0;
2406       case WM_SETCURSOR:
2407         if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2408             SetCursor(LoadCursor(NULL, IDC_ARROW));
2409             return TRUE;
2410         }
2411     }
2412
2413     return DefWindowProc(hwnd, message, wParam, lParam);
2414 }
2415
2416 /*
2417  * Move the system caret. (We maintain one, even though it's
2418  * invisible, for the benefit of blind people: apparently some
2419  * helper software tracks the system caret, so we should arrange to
2420  * have one.)
2421  */
2422 void sys_cursor(int x, int y)
2423 {
2424     COMPOSITIONFORM cf;
2425     HIMC hIMC;
2426
2427     if (!has_focus) return;
2428     
2429     SetCaretPos(x * font_width + offset_width,
2430                 y * font_height + offset_height);
2431
2432     /* IMM calls on Win98 and beyond only */
2433     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2434     
2435     if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2436             osVersion.dwMinorVersion == 0) return; /* 95 */
2437
2438     /* we should have the IMM functions */
2439     hIMC = ImmGetContext(hwnd);
2440     cf.dwStyle = CFS_POINT;
2441     cf.ptCurrentPos.x = x * font_width + offset_width;
2442     cf.ptCurrentPos.y = y * font_height + offset_height;
2443     ImmSetCompositionWindow(hIMC, &cf);
2444
2445     ImmReleaseContext(hwnd, hIMC);
2446 }
2447
2448 /*
2449  * Draw a line of text in the window, at given character
2450  * coordinates, in given attributes.
2451  *
2452  * We are allowed to fiddle with the contents of `text'.
2453  */
2454 void do_text(Context ctx, int x, int y, char *text, int len,
2455              unsigned long attr, int lattr)
2456 {
2457     COLORREF fg, bg, t;
2458     int nfg, nbg, nfont;
2459     HDC hdc = ctx;
2460     RECT line_box;
2461     int force_manual_underline = 0;
2462     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2463     int char_width = fnt_width;
2464     int text_adjust = 0;
2465     static int *IpDx = 0, IpDxLEN = 0;
2466
2467     if (attr & ATTR_WIDE)
2468         char_width *= 2;
2469
2470     if (len > IpDxLEN || IpDx[0] != char_width) {
2471         int i;
2472         if (len > IpDxLEN) {
2473             sfree(IpDx);
2474             IpDx = smalloc((len + 16) * sizeof(int));
2475             IpDxLEN = (len + 16);
2476         }
2477         for (i = 0; i < IpDxLEN; i++)
2478             IpDx[i] = char_width;
2479     }
2480
2481     /* Only want the left half of double width lines */
2482     if (lattr != LATTR_NORM && x*2 >= cols)
2483         return;
2484
2485     x *= fnt_width;
2486     y *= font_height;
2487     x += offset_width;
2488     y += offset_height;
2489
2490     if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2491         attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2492         attr ^= ATTR_CUR_XOR;
2493     }
2494
2495     nfont = 0;
2496     if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2497         /* Assume a poorman font is borken in other ways too. */
2498         lattr = LATTR_WIDE;
2499     } else
2500         switch (lattr) {
2501           case LATTR_NORM:
2502             break;
2503           case LATTR_WIDE:
2504             nfont |= FONT_WIDE;
2505             break;
2506           default:
2507             nfont |= FONT_WIDE + FONT_HIGH;
2508             break;
2509         }
2510     if (attr & ATTR_NARROW)
2511         nfont |= FONT_NARROW;
2512
2513     /* Special hack for the VT100 linedraw glyphs. */
2514     if ((attr & CSET_MASK) == 0x2300) {
2515         if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2516             switch ((unsigned char) (text[0])) {
2517               case 0xBA:
2518                 text_adjust = -2 * font_height / 5;
2519                 break;
2520               case 0xBB:
2521                 text_adjust = -1 * font_height / 5;
2522                 break;
2523               case 0xBC:
2524                 text_adjust = font_height / 5;
2525                 break;
2526               case 0xBD:
2527                 text_adjust = 2 * font_height / 5;
2528                 break;
2529             }
2530             if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2531                 text_adjust *= 2;
2532             attr &= ~CSET_MASK;
2533             text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2534             attr |= (unitab_xterm['q'] & CSET_MASK);
2535             if (attr & ATTR_UNDER) {
2536                 attr &= ~ATTR_UNDER;
2537                 force_manual_underline = 1;
2538             }
2539         }
2540     }
2541
2542     /* Anything left as an original character set is unprintable. */
2543     if (DIRECT_CHAR(attr)) {
2544         attr &= ~CSET_MASK;
2545         attr |= 0xFF00;
2546         memset(text, 0xFD, len);
2547     }
2548
2549     /* OEM CP */
2550     if ((attr & CSET_MASK) == ATTR_OEMCP)
2551         nfont |= FONT_OEM;
2552
2553     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2554     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2555     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2556         nfont |= FONT_BOLD;
2557     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2558         nfont |= FONT_UNDERLINE;
2559     another_font(nfont);
2560     if (!fonts[nfont]) {
2561         if (nfont & FONT_UNDERLINE)
2562             force_manual_underline = 1;
2563         /* Don't do the same for manual bold, it could be bad news. */
2564
2565         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2566     }
2567     another_font(nfont);
2568     if (!fonts[nfont])
2569         nfont = FONT_NORMAL;
2570     if (attr & ATTR_REVERSE) {
2571         t = nfg;
2572         nfg = nbg;
2573         nbg = t;
2574     }
2575     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2576         nfg++;
2577     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2578         nbg++;
2579     fg = colours[nfg];
2580     bg = colours[nbg];
2581     SelectObject(hdc, fonts[nfont]);
2582     SetTextColor(hdc, fg);
2583     SetBkColor(hdc, bg);
2584     SetBkMode(hdc, OPAQUE);
2585     line_box.left = x;
2586     line_box.top = y;
2587     line_box.right = x + char_width * len;
2588     line_box.bottom = y + font_height;
2589
2590     /* Only want the left half of double width lines */
2591     if (line_box.right > font_width*cols+offset_width)
2592         line_box.right = font_width*cols+offset_width;
2593
2594     /* We're using a private area for direct to font. (512 chars.) */
2595     if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2596         /* Ho Hum, dbcs fonts are a PITA! */
2597         /* To display on W9x I have to convert to UCS */
2598         static wchar_t *uni_buf = 0;
2599         static int uni_len = 0;
2600         int nlen, mptr;
2601         if (len > uni_len) {
2602             sfree(uni_buf);
2603             uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2604         }
2605
2606         for(nlen = mptr = 0; mptr<len; mptr++) {
2607             uni_buf[nlen] = 0xFFFD;
2608             if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2609                 IpDx[nlen] += char_width;
2610                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2611                                    text+mptr, 2, uni_buf+nlen, 1);
2612                 mptr++;
2613             }
2614             else
2615             {
2616                 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2617                                    text+mptr, 1, uni_buf+nlen, 1);
2618             }
2619             nlen++;
2620         }
2621         if (nlen <= 0)
2622             return;                    /* Eeek! */
2623
2624         ExtTextOutW(hdc, x,
2625                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2626                     ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2627         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2628             SetBkMode(hdc, TRANSPARENT);
2629             ExtTextOutW(hdc, x - 1,
2630                         y - font_height * (lattr ==
2631                                            LATTR_BOT) + text_adjust,
2632                         ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2633         }
2634
2635         IpDx[0] = -1;
2636     } else if (DIRECT_FONT(attr)) {
2637         ExtTextOut(hdc, x,
2638                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
2639                    ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2640         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2641             SetBkMode(hdc, TRANSPARENT);
2642
2643             /* GRR: This draws the character outside it's box and can leave
2644              * 'droppings' even with the clip box! I suppose I could loop it
2645              * one character at a time ... yuk. 
2646              * 
2647              * Or ... I could do a test print with "W", and use +1 or -1 for this
2648              * shift depending on if the leftmost column is blank...
2649              */
2650             ExtTextOut(hdc, x - 1,
2651                        y - font_height * (lattr ==
2652                                           LATTR_BOT) + text_adjust,
2653                        ETO_CLIPPED, &line_box, text, len, IpDx);
2654         }
2655     } else {
2656         /* And 'normal' unicode characters */
2657         static WCHAR *wbuf = NULL;
2658         static int wlen = 0;
2659         int i;
2660         if (wlen < len) {
2661             sfree(wbuf);
2662             wlen = len;
2663             wbuf = smalloc(wlen * sizeof(WCHAR));
2664         }
2665         for (i = 0; i < len; i++)
2666             wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2667
2668         ExtTextOutW(hdc, x,
2669                     y - font_height * (lattr == LATTR_BOT) + text_adjust,
2670                     ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2671
2672         /* And the shadow bold hack. */
2673         if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2674             SetBkMode(hdc, TRANSPARENT);
2675             ExtTextOutW(hdc, x - 1,
2676                         y - font_height * (lattr ==
2677                                            LATTR_BOT) + text_adjust,
2678                         ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2679         }
2680     }
2681     if (lattr != LATTR_TOP && (force_manual_underline ||
2682                                (und_mode == UND_LINE
2683                                 && (attr & ATTR_UNDER)))) {
2684         HPEN oldpen;
2685         int dec = descent;
2686         if (lattr == LATTR_BOT)
2687             dec = dec * 2 - font_height;
2688
2689         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2690         MoveToEx(hdc, x, y + dec, NULL);
2691         LineTo(hdc, x + len * char_width, y + dec);
2692         oldpen = SelectObject(hdc, oldpen);
2693         DeleteObject(oldpen);
2694     }
2695 }
2696
2697 void do_cursor(Context ctx, int x, int y, char *text, int len,
2698                unsigned long attr, int lattr)
2699 {
2700
2701     int fnt_width;
2702     int char_width;
2703     HDC hdc = ctx;
2704     int ctype = cfg.cursor_type;
2705
2706     if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2707         if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2708             do_text(ctx, x, y, text, len, attr, lattr);
2709             return;
2710         }
2711         ctype = 2;
2712         attr |= TATTR_RIGHTCURS;
2713     }
2714
2715     fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2716     if (attr & ATTR_WIDE)
2717         char_width *= 2;
2718     x *= fnt_width;
2719     y *= font_height;
2720     x += offset_width;
2721     y += offset_height;
2722
2723     if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2724         POINT pts[5];
2725         HPEN oldpen;
2726         pts[0].x = pts[1].x = pts[4].x = x;
2727         pts[2].x = pts[3].x = x + char_width - 1;
2728         pts[0].y = pts[3].y = pts[4].y = y;
2729         pts[1].y = pts[2].y = y + font_height - 1;
2730         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2731         Polyline(hdc, pts, 5);
2732         oldpen = SelectObject(hdc, oldpen);
2733         DeleteObject(oldpen);
2734     } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2735         int startx, starty, dx, dy, length, i;
2736         if (ctype == 1) {
2737             startx = x;
2738             starty = y + descent;
2739             dx = 1;
2740             dy = 0;
2741             length = char_width;
2742         } else {
2743             int xadjust = 0;
2744             if (attr & TATTR_RIGHTCURS)
2745                 xadjust = char_width - 1;
2746             startx = x + xadjust;
2747             starty = y;
2748             dx = 0;
2749             dy = 1;
2750             length = font_height;
2751         }
2752         if (attr & TATTR_ACTCURS) {
2753             HPEN oldpen;
2754             oldpen =
2755                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2756             MoveToEx(hdc, startx, starty, NULL);
2757             LineTo(hdc, startx + dx * length, starty + dy * length);
2758             oldpen = SelectObject(hdc, oldpen);
2759             DeleteObject(oldpen);
2760         } else {
2761             for (i = 0; i < length; i++) {
2762                 if (i % 2 == 0) {
2763                     SetPixel(hdc, startx, starty, colours[23]);
2764                 }
2765                 startx += dx;
2766                 starty += dy;
2767             }
2768         }
2769     }
2770 }
2771
2772 /* This function gets the actual width of a character in the normal font.
2773  */
2774 int CharWidth(Context ctx, int uc) {
2775     HDC hdc = ctx;
2776     int ibuf = 0;
2777
2778     /* If the font max is the same as the font ave width then this
2779      * function is a no-op.
2780      */
2781     if (!font_dualwidth) return 1;
2782
2783     switch (uc & CSET_MASK) {
2784       case ATTR_ASCII:
2785         uc = unitab_line[uc & 0xFF];
2786         break;
2787       case ATTR_LINEDRW:
2788         uc = unitab_xterm[uc & 0xFF];
2789         break;
2790       case ATTR_SCOACS:
2791         uc = unitab_scoacs[uc & 0xFF];
2792         break;
2793     }
2794     if (DIRECT_FONT(uc)) {
2795         if (dbcs_screenfont) return 1;
2796
2797         /* Speedup, I know of no font where ascii is the wrong width */
2798         if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~') 
2799             return 1;
2800
2801         if ( (uc & CSET_MASK) == ATTR_ACP ) {
2802             SelectObject(hdc, fonts[FONT_NORMAL]);
2803         } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2804             another_font(FONT_OEM);
2805             if (!fonts[FONT_OEM]) return 0;
2806
2807             SelectObject(hdc, fonts[FONT_OEM]);
2808         } else
2809             return 0;
2810
2811         if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 && 
2812              GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2813             return 0;
2814     } else {
2815         /* Speedup, I know of no font where ascii is the wrong width */
2816         if (uc >= ' ' && uc <= '~') return 1;
2817
2818         SelectObject(hdc, fonts[FONT_NORMAL]);
2819         if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2820             /* Okay that one worked */ ;
2821         else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2822             /* This should work on 9x too, but it's "less accurate" */ ;
2823         else
2824             return 0;
2825     }
2826
2827     ibuf += font_width / 2 -1;
2828     ibuf /= font_width;
2829
2830     return ibuf;
2831 }
2832
2833 /*
2834  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2835  * codes. Returns number of bytes used or zero to drop the message
2836  * or -1 to forward the message to windows.
2837  */
2838 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2839                         unsigned char *output)
2840 {
2841     BYTE keystate[256];
2842     int scan, left_alt = 0, key_down, shift_state;
2843     int r, i, code;
2844     unsigned char *p = output;
2845     static int alt_sum = 0;
2846
2847     HKL kbd_layout = GetKeyboardLayout(0);
2848
2849     static WORD keys[3];
2850     static int compose_char = 0;
2851     static WPARAM compose_key = 0;
2852
2853     r = GetKeyboardState(keystate);
2854     if (!r)
2855         memset(keystate, 0, sizeof(keystate));
2856     else {
2857 #if 0
2858 #define SHOW_TOASCII_RESULT
2859         {                              /* Tell us all about key events */
2860             static BYTE oldstate[256];
2861             static int first = 1;
2862             static int scan;
2863             int ch;
2864             if (first)
2865                 memcpy(oldstate, keystate, sizeof(oldstate));
2866             first = 0;
2867
2868             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2869                 debug(("+"));
2870             } else if ((HIWORD(lParam) & KF_UP)
2871                        && scan == (HIWORD(lParam) & 0xFF)) {
2872                 debug((". U"));
2873             } else {
2874                 debug((".\n"));
2875                 if (wParam >= VK_F1 && wParam <= VK_F20)
2876                     debug(("K_F%d", wParam + 1 - VK_F1));
2877                 else
2878                     switch (wParam) {
2879                       case VK_SHIFT:
2880                         debug(("SHIFT"));
2881                         break;
2882                       case VK_CONTROL:
2883                         debug(("CTRL"));
2884                         break;
2885                       case VK_MENU:
2886                         debug(("ALT"));
2887                         break;
2888                       default:
2889                         debug(("VK_%02x", wParam));
2890                     }
2891                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2892                     debug(("*"));
2893                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2894
2895                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2896                 if (ch >= ' ' && ch <= '~')
2897                     debug((", '%c'", ch));
2898                 else if (ch)
2899                     debug((", $%02x", ch));
2900
2901                 if (keys[0])
2902                     debug((", KB0=%02x", keys[0]));
2903                 if (keys[1])
2904                     debug((", KB1=%02x", keys[1]));
2905                 if (keys[2])
2906                     debug((", KB2=%02x", keys[2]));
2907
2908                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2909                     debug((", S"));
2910                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2911                     debug((", C"));
2912                 if ((HIWORD(lParam) & KF_EXTENDED))
2913                     debug((", E"));
2914                 if ((HIWORD(lParam) & KF_UP))
2915                     debug((", U"));
2916             }
2917
2918             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2919             else if ((HIWORD(lParam) & KF_UP))
2920                 oldstate[wParam & 0xFF] ^= 0x80;
2921             else
2922                 oldstate[wParam & 0xFF] ^= 0x81;
2923
2924             for (ch = 0; ch < 256; ch++)
2925                 if (oldstate[ch] != keystate[ch])
2926                     debug((", M%02x=%02x", ch, keystate[ch]));
2927
2928             memcpy(oldstate, keystate, sizeof(oldstate));
2929         }
2930 #endif
2931
2932         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2933             keystate[VK_RMENU] = keystate[VK_MENU];
2934         }
2935
2936
2937         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2938         if ((cfg.funky_type == 3 ||
2939              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2940             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2941
2942             wParam = VK_EXECUTE;
2943
2944             /* UnToggle NUMLock */
2945             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2946                 keystate[VK_NUMLOCK] ^= 1;
2947         }
2948
2949         /* And write back the 'adjusted' state */
2950         SetKeyboardState(keystate);
2951     }
2952
2953     /* Disable Auto repeat if required */
2954     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2955         return 0;
2956
2957     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2958         left_alt = 1;
2959
2960     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2961
2962     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2963     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2964         if (cfg.ctrlaltkeys)
2965             keystate[VK_MENU] = 0;
2966         else {
2967             keystate[VK_RMENU] = 0x80;
2968             left_alt = 0;
2969         }
2970     }
2971
2972     alt_pressed = (left_alt && key_down);
2973
2974     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2975     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2976         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2977
2978     /* Note if AltGr was pressed and if it was used as a compose key */
2979     if (!compose_state) {
2980         compose_key = 0x100;
2981         if (cfg.compose_key) {
2982             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2983                 compose_key = wParam;
2984         }
2985         if (wParam == VK_APPS)
2986             compose_key = wParam;
2987     }
2988
2989     if (wParam == compose_key) {
2990         if (compose_state == 0
2991             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2992                 1;
2993         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2994             compose_state = 2;
2995         else
2996             compose_state = 0;
2997     } else if (compose_state == 1 && wParam != VK_CONTROL)
2998         compose_state = 0;
2999
3000     /* 
3001      * Record that we pressed key so the scroll window can be reset, but
3002      * be careful to avoid Shift-UP/Down
3003      */
3004     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3005         wParam != VK_MENU && wParam != VK_CONTROL) {
3006         seen_key_event = 1;
3007     }
3008
3009     if (compose_state > 1 && left_alt)
3010         compose_state = 0;
3011
3012     /* Sanitize the number pad if not using a PC NumPad */
3013     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3014                      && cfg.funky_type != 2)
3015         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3016         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3017             int nParam = 0;
3018             switch (wParam) {
3019               case VK_INSERT:
3020                 nParam = VK_NUMPAD0;
3021                 break;
3022               case VK_END:
3023                 nParam = VK_NUMPAD1;
3024                 break;
3025               case VK_DOWN:
3026                 nParam = VK_NUMPAD2;
3027                 break;
3028               case VK_NEXT:
3029                 nParam = VK_NUMPAD3;
3030                 break;
3031               case VK_LEFT:
3032                 nParam = VK_NUMPAD4;
3033                 break;
3034               case VK_CLEAR:
3035                 nParam = VK_NUMPAD5;
3036                 break;
3037               case VK_RIGHT:
3038                 nParam = VK_NUMPAD6;
3039                 break;
3040               case VK_HOME:
3041                 nParam = VK_NUMPAD7;
3042                 break;
3043               case VK_UP:
3044                 nParam = VK_NUMPAD8;
3045                 break;
3046               case VK_PRIOR:
3047                 nParam = VK_NUMPAD9;
3048                 break;
3049               case VK_DELETE:
3050                 nParam = VK_DECIMAL;
3051                 break;
3052             }
3053             if (nParam) {
3054                 if (keystate[VK_NUMLOCK] & 1)
3055                     shift_state |= 1;
3056                 wParam = nParam;
3057             }
3058         }
3059     }
3060
3061     /* If a key is pressed and AltGr is not active */
3062     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3063         /* Okay, prepare for most alts then ... */
3064         if (left_alt)
3065             *p++ = '\033';
3066
3067         /* Lets see if it's a pattern we know all about ... */
3068         if (wParam == VK_PRIOR && shift_state == 1) {
3069             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3070             return 0;
3071         }
3072         if (wParam == VK_NEXT && shift_state == 1) {
3073             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3074             return 0;
3075         }
3076         if (wParam == VK_INSERT && shift_state == 1) {
3077             term_do_paste();
3078             return 0;
3079         }
3080         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3081             return -1;
3082         }
3083         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3084             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3085             return -1;
3086         }
3087         if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3088             (cfg.resize_action != RESIZE_DISABLED)) {
3089             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3090                 flip_full_screen();
3091             return -1;
3092         }
3093         /* Control-Numlock for app-keypad mode switch */
3094         if (wParam == VK_PAUSE && shift_state == 2) {
3095             app_keypad_keys ^= 1;
3096             return 0;
3097         }
3098
3099         /* Nethack keypad */
3100         if (cfg.nethack_keypad && !left_alt) {
3101             switch (wParam) {
3102               case VK_NUMPAD1:
3103                 *p++ = shift_state ? 'B' : 'b';
3104                 return p - output;
3105               case VK_NUMPAD2:
3106                 *p++ = shift_state ? 'J' : 'j';
3107                 return p - output;
3108               case VK_NUMPAD3:
3109                 *p++ = shift_state ? 'N' : 'n';
3110                 return p - output;
3111               case VK_NUMPAD4:
3112                 *p++ = shift_state ? 'H' : 'h';
3113                 return p - output;
3114               case VK_NUMPAD5:
3115                 *p++ = shift_state ? '.' : '.';
3116                 return p - output;
3117               case VK_NUMPAD6:
3118                 *p++ = shift_state ? 'L' : 'l';
3119                 return p - output;
3120               case VK_NUMPAD7:
3121                 *p++ = shift_state ? 'Y' : 'y';
3122                 return p - output;
3123               case VK_NUMPAD8:
3124                 *p++ = shift_state ? 'K' : 'k';
3125                 return p - output;
3126               case VK_NUMPAD9:
3127                 *p++ = shift_state ? 'U' : 'u';
3128                 return p - output;
3129             }
3130         }
3131
3132         /* Application Keypad */
3133         if (!left_alt) {
3134             int xkey = 0;
3135
3136             if (cfg.funky_type == 3 ||
3137                 (cfg.funky_type <= 1 &&
3138                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3139                   case VK_EXECUTE:
3140                     xkey = 'P';
3141                     break;
3142                   case VK_DIVIDE:
3143                     xkey = 'Q';
3144                     break;
3145                   case VK_MULTIPLY:
3146                     xkey = 'R';
3147                     break;
3148                   case VK_SUBTRACT:
3149                     xkey = 'S';
3150                     break;
3151                 }
3152             if (app_keypad_keys && !cfg.no_applic_k)
3153                 switch (wParam) {
3154                   case VK_NUMPAD0:
3155                     xkey = 'p';
3156                     break;
3157                   case VK_NUMPAD1:
3158                     xkey = 'q';
3159                     break;
3160                   case VK_NUMPAD2:
3161                     xkey = 'r';
3162                     break;
3163                   case VK_NUMPAD3:
3164                     xkey = 's';
3165                     break;
3166                   case VK_NUMPAD4:
3167                     xkey = 't';
3168                     break;
3169                   case VK_NUMPAD5:
3170                     xkey = 'u';
3171                     break;
3172                   case VK_NUMPAD6:
3173                     xkey = 'v';
3174                     break;
3175                   case VK_NUMPAD7:
3176                     xkey = 'w';
3177                     break;
3178                   case VK_NUMPAD8:
3179                     xkey = 'x';
3180                     break;
3181                   case VK_NUMPAD9:
3182                     xkey = 'y';
3183                     break;
3184
3185                   case VK_DECIMAL:
3186                     xkey = 'n';
3187                     break;
3188                   case VK_ADD:
3189                     if (cfg.funky_type == 2) {
3190                         if (shift_state)
3191                             xkey = 'l';
3192                         else
3193                             xkey = 'k';
3194                     } else if (shift_state)
3195                         xkey = 'm';
3196                     else
3197                         xkey = 'l';
3198                     break;
3199
3200                   case VK_DIVIDE:
3201                     if (cfg.funky_type == 2)
3202                         xkey = 'o';
3203                     break;
3204                   case VK_MULTIPLY:
3205                     if (cfg.funky_type == 2)
3206                         xkey = 'j';
3207                     break;
3208                   case VK_SUBTRACT:
3209                     if (cfg.funky_type == 2)
3210                         xkey = 'm';
3211                     break;
3212
3213                   case VK_RETURN:
3214                     if (HIWORD(lParam) & KF_EXTENDED)
3215                         xkey = 'M';
3216                     break;
3217                 }
3218             if (xkey) {
3219                 if (vt52_mode) {
3220                     if (xkey >= 'P' && xkey <= 'S')
3221                         p += sprintf((char *) p, "\x1B%c", xkey);
3222                     else
3223                         p += sprintf((char *) p, "\x1B?%c", xkey);
3224                 } else
3225                     p += sprintf((char *) p, "\x1BO%c", xkey);
3226                 return p - output;
3227             }
3228         }
3229
3230         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
3231             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3232             *p++ = 0;
3233             return -2;
3234         }
3235         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
3236             *p++ = 0x1B;
3237             *p++ = '[';
3238             *p++ = 'Z';
3239             return p - output;
3240         }
3241         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
3242             *p++ = 0;
3243             return p - output;
3244         }
3245         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
3246             *p++ = 160;
3247             return p - output;
3248         }
3249         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
3250             *p++ = 3;
3251             *p++ = 0;
3252             return -2;
3253         }
3254         if (wParam == VK_PAUSE) {      /* Break/Pause */
3255             *p++ = 26;
3256             *p++ = 0;
3257             return -2;
3258         }
3259         /* Control-2 to Control-8 are special */
3260         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3261             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3262             return p - output;
3263         }
3264         if (shift_state == 2 && wParam == 0xBD) {
3265             *p++ = 0x1F;
3266             return p - output;
3267         }
3268         if (shift_state == 2 && wParam == 0xDF) {
3269             *p++ = 0x1C;
3270             return p - output;
3271         }
3272         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3273             *p++ = '\r';
3274             *p++ = '\n';
3275             return p - output;
3276         }
3277
3278         /*
3279          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3280          * for integer decimal nn.)
3281          *
3282          * We also deal with the weird ones here. Linux VCs replace F1
3283          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3284          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3285          * respectively.
3286          */
3287         code = 0;
3288         switch (wParam) {
3289           case VK_F1:
3290             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3291             break;
3292           case VK_F2:
3293             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3294             break;
3295           case VK_F3:
3296             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3297             break;
3298           case VK_F4:
3299             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3300             break;
3301           case VK_F5:
3302             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3303             break;
3304           case VK_F6:
3305             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3306             break;
3307           case VK_F7:
3308             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3309             break;
3310           case VK_F8:
3311             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3312             break;
3313           case VK_F9:
3314             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3315             break;
3316           case VK_F10:
3317             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3318             break;
3319           case VK_F11:
3320             code = 23;
3321             break;
3322           case VK_F12:
3323             code = 24;
3324             break;
3325           case VK_F13:
3326             code = 25;
3327             break;
3328           case VK_F14:
3329             code = 26;
3330             break;
3331           case VK_F15:
3332             code = 28;
3333             break;
3334           case VK_F16:
3335             code = 29;
3336             break;
3337           case VK_F17:
3338             code = 31;
3339             break;
3340           case VK_F18:
3341             code = 32;
3342             break;
3343           case VK_F19:
3344             code = 33;
3345             break;
3346           case VK_F20:
3347             code = 34;
3348             break;
3349         }
3350         if ((shift_state&2) == 0) switch (wParam) {
3351           case VK_HOME:
3352             code = 1;
3353             break;
3354           case VK_INSERT:
3355             code = 2;
3356             break;
3357           case VK_DELETE:
3358             code = 3;
3359             break;
3360           case VK_END:
3361             code = 4;
3362             break;
3363           case VK_PRIOR:
3364             code = 5;
3365             break;
3366           case VK_NEXT:
3367             code = 6;
3368             break;
3369         }
3370         /* Reorder edit keys to physical order */
3371         if (cfg.funky_type == 3 && code <= 6)
3372             code = "\0\2\1\4\5\3\6"[code];
3373
3374         if (vt52_mode && code > 0 && code <= 6) {
3375             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3376             return p - output;
3377         }
3378
3379         if (cfg.funky_type == 5 &&     /* SCO function keys */
3380             code >= 11 && code <= 34) {
3381             char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3382             int index = 0;
3383             switch (wParam) {
3384               case VK_F1: index = 0; break;
3385               case VK_F2: index = 1; break;
3386               case VK_F3: index = 2; break;
3387               case VK_F4: index = 3; break;
3388               case VK_F5: index = 4; break;
3389               case VK_F6: index = 5; break;
3390               case VK_F7: index = 6; break;
3391               case VK_F8: index = 7; break;
3392               case VK_F9: index = 8; break;
3393               case VK_F10: index = 9; break;
3394               case VK_F11: index = 10; break;
3395               case VK_F12: index = 11; break;
3396             }
3397             if (keystate[VK_SHIFT] & 0x80) index += 12;
3398             if (keystate[VK_CONTROL] & 0x80) index += 24;
3399             p += sprintf((char *) p, "\x1B[%c", codes[index]);
3400             return p - output;
3401         }
3402         if (cfg.funky_type == 5 &&     /* SCO small keypad */
3403             code >= 1 && code <= 6) {
3404             char codes[] = "HL.FIG";
3405             if (code == 3) {
3406                 *p++ = '\x7F';
3407             } else {
3408                 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3409             }
3410             return p - output;
3411         }
3412         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3413             int offt = 0;
3414             if (code > 15)
3415                 offt++;
3416             if (code > 21)
3417                 offt++;
3418             if (vt52_mode)
3419                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3420             else
3421                 p +=
3422                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3423             return p - output;
3424         }
3425         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3426             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3427             return p - output;
3428         }
3429         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3430             if (vt52_mode)
3431                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3432             else
3433                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3434             return p - output;
3435         }
3436         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3437             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3438             return p - output;
3439         }
3440         if (code) {
3441             p += sprintf((char *) p, "\x1B[%d~", code);
3442             return p - output;
3443         }
3444
3445         /*
3446          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3447          * some reason seems to send VK_CLEAR to Windows...).
3448          */
3449         {
3450             char xkey = 0;
3451             switch (wParam) {
3452               case VK_UP:
3453                 xkey = 'A';
3454                 break;
3455               case VK_DOWN:
3456                 xkey = 'B';
3457                 break;
3458               case VK_RIGHT:
3459                 xkey = 'C';
3460                 break;
3461               case VK_LEFT:
3462                 xkey = 'D';
3463                 break;
3464               case VK_CLEAR:
3465                 xkey = 'G';
3466                 break;
3467             }
3468             if (xkey) {
3469                 if (vt52_mode)
3470                     p += sprintf((char *) p, "\x1B%c", xkey);
3471                 else {
3472                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3473                     /* VT100 & VT102 manuals both state the app cursor keys
3474                      * only work if the app keypad is on.
3475                      */
3476                     if (!app_keypad_keys)
3477                         app_flg = 0;
3478                     /* Useful mapping of Ctrl-arrows */
3479                     if (shift_state == 2)
3480                         app_flg = !app_flg;
3481
3482                     if (app_flg)
3483                         p += sprintf((char *) p, "\x1BO%c", xkey);
3484                     else
3485                         p += sprintf((char *) p, "\x1B[%c", xkey);
3486                 }
3487                 return p - output;
3488             }
3489         }
3490
3491         /*
3492          * Finally, deal with Return ourselves. (Win95 seems to
3493          * foul it up when Alt is pressed, for some reason.)
3494          */
3495         if (wParam == VK_RETURN) {     /* Return */
3496             *p++ = 0x0D;
3497             *p++ = 0;
3498             return -2;
3499         }
3500
3501         if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3502             alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3503         else
3504             alt_sum = 0;
3505     }
3506
3507     /* Okay we've done everything interesting; let windows deal with 
3508      * the boring stuff */
3509     {
3510         BOOL capsOn=0;
3511
3512         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3513         if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3514             capsOn= !left_alt;
3515             keystate[VK_CAPITAL] = 0;
3516         }
3517
3518         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3519 #ifdef SHOW_TOASCII_RESULT
3520         if (r == 1 && !key_down) {
3521             if (alt_sum) {
3522                 if (in_utf || dbcs_screenfont)
3523                     debug((", (U+%04x)", alt_sum));
3524                 else
3525                     debug((", LCH(%d)", alt_sum));
3526             } else {
3527                 debug((", ACH(%d)", keys[0]));
3528             }
3529         } else if (r > 0) {
3530             int r1;
3531             debug((", ASC("));
3532             for (r1 = 0; r1 < r; r1++) {
3533                 debug(("%s%d", r1 ? "," : "", keys[r1]));
3534             }
3535             debug((")"));
3536         }
3537 #endif
3538         if (r > 0) {
3539             WCHAR keybuf;
3540
3541             /*
3542              * Interrupt an ongoing paste. I'm not sure this is
3543              * sensible, but for the moment it's preferable to
3544              * having to faff about buffering things.
3545              */
3546             term_nopaste();
3547
3548             p = output;
3549             for (i = 0; i < r; i++) {
3550                 unsigned char ch = (unsigned char) keys[i];
3551
3552                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3553                     compose_char = ch;
3554                     compose_state++;
3555                     continue;
3556                 }
3557                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3558                     int nc;
3559                     compose_state = 0;
3560
3561                     if ((nc = check_compose(compose_char, ch)) == -1) {
3562                         MessageBeep(MB_ICONHAND);
3563                         return 0;
3564                     }
3565                     keybuf = nc;
3566                     luni_send(&keybuf, 1, 1);
3567                     continue;
3568                 }
3569
3570                 compose_state = 0;
3571
3572                 if (!key_down) {
3573                     if (alt_sum) {
3574                         if (in_utf || dbcs_screenfont) {
3575                             keybuf = alt_sum;
3576                             luni_send(&keybuf, 1, 1);
3577                         } else {
3578                             ch = (char) alt_sum;
3579                             /*
3580                              * We need not bother about stdin
3581                              * backlogs here, because in GUI PuTTY
3582                              * we can't do anything about it
3583                              * anyway; there's no means of asking
3584                              * Windows to hold off on KEYDOWN
3585                              * messages. We _have_ to buffer
3586                              * everything we're sent.
3587                              */
3588                             ldisc_send(&ch, 1, 1);
3589                         }
3590                         alt_sum = 0;
3591                     } else
3592                         lpage_send(kbd_codepage, &ch, 1, 1);
3593                 } else {
3594                     if(capsOn && ch < 0x80) {
3595                         WCHAR cbuf[2];
3596                         cbuf[0] = 27;
3597                         cbuf[1] = xlat_uskbd2cyrllic(ch);
3598                         luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3599                     } else {
3600                         char cbuf[2];
3601                         cbuf[0] = '\033';
3602                         cbuf[1] = ch;
3603                         lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3604                     }
3605                 }
3606                 show_mouseptr(0);
3607             }
3608
3609             /* This is so the ALT-Numpad and dead keys work correctly. */
3610             keys[0] = 0;
3611
3612             return p - output;
3613         }
3614         /* If we're definitly not building up an ALT-54321 then clear it */
3615         if (!left_alt)
3616             keys[0] = 0;
3617         /* If we will be using alt_sum fix the 256s */
3618         else if (keys[0] && (in_utf || dbcs_screenfont))
3619             keys[0] = 10;
3620     }
3621
3622     /*
3623      * ALT alone may or may not want to bring up the System menu.
3624      * If it's not meant to, we return 0 on presses or releases of
3625      * ALT, to show that we've swallowed the keystroke. Otherwise
3626      * we return -1, which means Windows will give the keystroke
3627      * its default handling (i.e. bring up the System menu).
3628      */
3629     if (wParam == VK_MENU && !cfg.alt_only)
3630         return 0;
3631
3632     return -1;
3633 }
3634
3635 void set_title(char *title)
3636 {
3637     sfree(window_name);
3638     window_name = smalloc(1 + strlen(title));
3639     strcpy(window_name, title);
3640     if (cfg.win_name_always || !IsIconic(hwnd))
3641         SetWindowText(hwnd, title);
3642 }
3643
3644 void set_icon(char *title)
3645 {
3646     sfree(icon_name);
3647     icon_name = smalloc(1 + strlen(title));
3648     strcpy(icon_name, title);
3649     if (!cfg.win_name_always && IsIconic(hwnd))
3650         SetWindowText(hwnd, title);
3651 }
3652
3653 void set_sbar(int total, int start, int page)
3654 {
3655     SCROLLINFO si;
3656
3657     if ((full_screen && !cfg.scrollbar_in_fullscreen) ||
3658         (!full_screen && !cfg.scrollbar))
3659         return;
3660
3661     si.cbSize = sizeof(si);
3662     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3663     si.nMin = 0;
3664     si.nMax = total - 1;
3665     si.nPage = page;
3666     si.nPos = start;
3667     if (hwnd)
3668         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3669 }
3670
3671 Context get_ctx(void)
3672 {
3673     HDC hdc;
3674     if (hwnd) {
3675         hdc = GetDC(hwnd);
3676         if (hdc && pal)
3677             SelectPalette(hdc, pal, FALSE);
3678         return hdc;
3679     } else
3680         return NULL;
3681 }
3682
3683 void free_ctx(Context ctx)
3684 {
3685     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3686     ReleaseDC(hwnd, ctx);
3687 }
3688
3689 static void real_palette_set(int n, int r, int g, int b)
3690 {
3691     if (pal) {
3692         logpal->palPalEntry[n].peRed = r;
3693         logpal->palPalEntry[n].peGreen = g;
3694         logpal->palPalEntry[n].peBlue = b;
3695         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3696         colours[n] = PALETTERGB(r, g, b);
3697         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3698     } else
3699         colours[n] = RGB(r, g, b);
3700 }
3701
3702 void palette_set(int n, int r, int g, int b)
3703 {
3704     static const int first[21] = {
3705         0, 2, 4, 6, 8, 10, 12, 14,
3706         1, 3, 5, 7, 9, 11, 13, 15,
3707         16, 17, 18, 20, 22
3708     };
3709     real_palette_set(first[n], r, g, b);
3710     if (first[n] >= 18)
3711         real_palette_set(first[n] + 1, r, g, b);
3712     if (pal) {
3713         HDC hdc = get_ctx();
3714         UnrealizeObject(pal);
3715         RealizePalette(hdc);
3716         free_ctx(hdc);
3717     }
3718 }
3719
3720 void palette_reset(void)
3721 {
3722     int i;
3723
3724     for (i = 0; i < NCOLOURS; i++) {
3725         if (pal) {
3726             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3727             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3728             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3729             logpal->palPalEntry[i].peFlags = 0;
3730             colours[i] = PALETTERGB(defpal[i].rgbtRed,
3731                                     defpal[i].rgbtGreen,
3732                                     defpal[i].rgbtBlue);
3733         } else
3734             colours[i] = RGB(defpal[i].rgbtRed,
3735                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3736     }
3737
3738     if (pal) {
3739         HDC hdc;
3740         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3741         hdc = get_ctx();
3742         RealizePalette(hdc);
3743         free_ctx(hdc);
3744     }
3745 }
3746
3747 void write_aclip(char *data, int len, int must_deselect)
3748 {
3749     HGLOBAL clipdata;
3750     void *lock;
3751
3752     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3753     if (!clipdata)
3754         return;
3755     lock = GlobalLock(clipdata);
3756     if (!lock)
3757         return;
3758     memcpy(lock, data, len);
3759     ((unsigned char *) lock)[len] = 0;
3760     GlobalUnlock(clipdata);
3761
3762     if (!must_deselect)
3763         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3764
3765     if (OpenClipboard(hwnd)) {
3766         EmptyClipboard();
3767         SetClipboardData(CF_TEXT, clipdata);
3768         CloseClipboard();
3769     } else
3770         GlobalFree(clipdata);
3771
3772     if (!must_deselect)
3773         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3774 }
3775
3776 /*
3777  * Note: unlike write_aclip() this will not append a nul.
3778  */
3779 void write_clip(wchar_t * data, int len, int must_deselect)
3780 {
3781     HGLOBAL clipdata, clipdata2, clipdata3;
3782     int len2;
3783     void *lock, *lock2, *lock3;
3784
3785     len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3786
3787     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3788                            len * sizeof(wchar_t));
3789     clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3790
3791     if (!clipdata || !clipdata2 || !clipdata3) {
3792         if (clipdata)
3793             GlobalFree(clipdata);
3794         if (clipdata2)
3795             GlobalFree(clipdata2);
3796         if (clipdata3)
3797             GlobalFree(clipdata3);
3798         return;
3799     }
3800     if (!(lock = GlobalLock(clipdata)))
3801         return;
3802     if (!(lock2 = GlobalLock(clipdata2)))
3803         return;
3804
3805     memcpy(lock, data, len * sizeof(wchar_t));
3806     WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3807
3808     if (cfg.rtf_paste) {
3809         wchar_t unitab[256];
3810         char *rtf = NULL;
3811         unsigned char *tdata = (unsigned char *)lock2;
3812         wchar_t *udata = (wchar_t *)lock;
3813         int rtflen = 0, uindex = 0, tindex = 0;
3814         int rtfsize = 0;
3815         int multilen, blen, alen, totallen, i;
3816         char before[16], after[4];
3817
3818         get_unitab(CP_ACP, unitab, 0);
3819
3820         rtfsize = 100 + strlen(cfg.font);
3821         rtf = smalloc(rtfsize);
3822         sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3823                 GetACP(), cfg.font);
3824         rtflen = strlen(rtf);
3825
3826         /*
3827          * We want to construct a piece of RTF that specifies the
3828          * same Unicode text. To do this we will read back in
3829          * parallel from the Unicode data in `udata' and the
3830          * non-Unicode data in `tdata'. For each character in
3831          * `tdata' which becomes the right thing in `udata' when
3832          * looked up in `unitab', we just copy straight over from
3833          * tdata. For each one that doesn't, we must WCToMB it
3834          * individually and produce a \u escape sequence.
3835          * 
3836          * It would probably be more robust to just bite the bullet
3837          * and WCToMB each individual Unicode character one by one,
3838          * then MBToWC each one back to see if it was an accurate
3839          * translation; but that strikes me as a horrifying number
3840          * of Windows API calls so I want to see if this faster way
3841          * will work. If it screws up badly we can always revert to
3842          * the simple and slow way.
3843          */
3844         while (tindex < len2 && uindex < len &&
3845                tdata[tindex] && udata[uindex]) {
3846             if (tindex + 1 < len2 &&
3847                 tdata[tindex] == '\r' &&
3848                 tdata[tindex+1] == '\n') {
3849                 tindex++;
3850                 uindex++;
3851             }
3852             if (unitab[tdata[tindex]] == udata[uindex]) {
3853                 multilen = 1;
3854                 before[0] = '\0';
3855                 after[0] = '\0';
3856                 blen = alen = 0;
3857             } else {
3858                 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3859                                                NULL, 0, NULL, NULL);
3860                 if (multilen != 1) {
3861                     blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3862                                    udata[uindex]);
3863                     alen = 1; strcpy(after, "}");
3864                 } else {
3865                     blen = sprintf(before, "\\u%d", udata[uindex]);
3866                     alen = 0; after[0] = '\0';
3867                 }
3868             }
3869             assert(tindex + multilen <= len2);
3870             totallen = blen + alen;
3871             for (i = 0; i < multilen; i++) {
3872                 if (tdata[tindex+i] == '\\' ||
3873                     tdata[tindex+i] == '{' ||
3874                     tdata[tindex+i] == '}')
3875                     totallen += 2;
3876                 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3877                     totallen += 6;     /* \par\r\n */
3878                 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3879                     totallen += 4;
3880                 else
3881                     totallen++;
3882             }
3883
3884             if (rtfsize < rtflen + totallen + 3) {
3885                 rtfsize = rtflen + totallen + 512;
3886                 rtf = srealloc(rtf, rtfsize);
3887             }
3888
3889             strcpy(rtf + rtflen, before); rtflen += blen;
3890             for (i = 0; i < multilen; i++) {
3891                 if (tdata[tindex+i] == '\\' ||
3892                     tdata[tindex+i] == '{' ||
3893                     tdata[tindex+i] == '}') {
3894                     rtf[rtflen++] = '\\';
3895                     rtf[rtflen++] = tdata[tindex+i];
3896                 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
3897                     rtflen += sprintf(rtf+rtflen, "\\par\r\n");
3898                 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
3899                     rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
3900                 } else {
3901                     rtf[rtflen++] = tdata[tindex+i];
3902                 }
3903             }
3904             strcpy(rtf + rtflen, after); rtflen += alen;
3905
3906             tindex += multilen;
3907             uindex++;
3908         }
3909
3910         strcpy(rtf + rtflen, "}");
3911         rtflen += 2;
3912
3913         clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
3914         if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
3915             strcpy(lock3, rtf);
3916             GlobalUnlock(clipdata3);
3917         }
3918         sfree(rtf);
3919     } else
3920         clipdata3 = NULL;
3921
3922     GlobalUnlock(clipdata);
3923     GlobalUnlock(clipdata2);
3924
3925     if (!must_deselect)
3926         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3927
3928     if (OpenClipboard(hwnd)) {
3929         EmptyClipboard();
3930         SetClipboardData(CF_UNICODETEXT, clipdata);
3931         SetClipboardData(CF_TEXT, clipdata2);
3932         if (clipdata3)
3933             SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
3934         CloseClipboard();
3935     } else {
3936         GlobalFree(clipdata);
3937         GlobalFree(clipdata2);
3938     }
3939
3940     if (!must_deselect)
3941         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3942 }
3943
3944 void get_clip(wchar_t ** p, int *len)
3945 {
3946     static HGLOBAL clipdata = NULL;
3947     static wchar_t *converted = 0;
3948     wchar_t *p2;
3949
3950     if (converted) {
3951         sfree(converted);
3952         converted = 0;
3953     }
3954     if (!p) {
3955         if (clipdata)
3956             GlobalUnlock(clipdata);
3957         clipdata = NULL;
3958         return;
3959     } else if (OpenClipboard(NULL)) {
3960         if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3961             CloseClipboard();
3962             *p = GlobalLock(clipdata);
3963             if (*p) {
3964                 for (p2 = *p; *p2; p2++);
3965                 *len = p2 - *p;
3966                 return;
3967             }
3968         } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3969             char *s;
3970             int i;
3971             CloseClipboard();
3972             s = GlobalLock(clipdata);
3973             i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3974             *p = converted = smalloc(i * sizeof(wchar_t));
3975             MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3976             *len = i - 1;
3977             return;
3978         } else
3979             CloseClipboard();
3980     }
3981
3982     *p = NULL;
3983     *len = 0;
3984 }
3985
3986 #if 0
3987 /*
3988  * Move `lines' lines from position `from' to position `to' in the
3989  * window.
3990  */
3991 void optimised_move(int to, int from, int lines)
3992 {
3993     RECT r;
3994     int min, max;
3995
3996     min = (to < from ? to : from);
3997     max = to + from - min;
3998
3999     r.left = offset_width;
4000     r.right = offset_width + cols * font_width;
4001     r.top = offset_height + min * font_height;
4002     r.bottom = offset_height + (max + lines) * font_height;
4003     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4004 }
4005 #endif
4006
4007 /*
4008  * Print a message box and perform a fatal exit.
4009  */
4010 void fatalbox(char *fmt, ...)
4011 {
4012     va_list ap;
4013     char stuff[200];
4014
4015     va_start(ap, fmt);
4016     vsprintf(stuff, fmt, ap);
4017     va_end(ap);
4018     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4019     exit(1);
4020 }
4021
4022 /*
4023  * Manage window caption / taskbar flashing, if enabled.
4024  * 0 = stop, 1 = maintain, 2 = start
4025  */
4026 static void flash_window(int mode)
4027 {
4028     static long last_flash = 0;
4029     static int flashing = 0;
4030     if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4031         /* stop */
4032         if (flashing) {
4033             FlashWindow(hwnd, FALSE);
4034             flashing = 0;
4035         }
4036
4037     } else if (mode == 2) {
4038         /* start */
4039         if (!flashing) {
4040             last_flash = GetTickCount();
4041             flashing = 1;
4042             FlashWindow(hwnd, TRUE);
4043         }
4044
4045     } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4046         /* maintain */
4047         if (flashing) {
4048             long now = GetTickCount();
4049             long fdiff = now - last_flash;
4050             if (fdiff < 0 || fdiff > 450) {
4051                 last_flash = now;
4052                 FlashWindow(hwnd, TRUE);        /* toggle */
4053             }
4054         }
4055     }
4056 }
4057
4058 /*
4059  * Beep.
4060  */
4061 void beep(int mode)
4062 {
4063     if (mode == BELL_DEFAULT) {
4064         /*
4065          * For MessageBeep style bells, we want to be careful of
4066          * timing, because they don't have the nice property of
4067          * PlaySound bells that each one cancels the previous
4068          * active one. So we limit the rate to one per 50ms or so.
4069          */
4070         static long lastbeep = 0;
4071         long beepdiff;
4072
4073         beepdiff = GetTickCount() - lastbeep;
4074         if (beepdiff >= 0 && beepdiff < 50)
4075             return;
4076         MessageBeep(MB_OK);
4077         /*
4078          * The above MessageBeep call takes time, so we record the
4079          * time _after_ it finishes rather than before it starts.
4080          */
4081         lastbeep = GetTickCount();
4082     } else if (mode == BELL_WAVEFILE) {
4083         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4084             char buf[sizeof(cfg.bell_wavefile) + 80];
4085             sprintf(buf, "Unable to play sound file\n%s\n"
4086                     "Using default sound instead", cfg.bell_wavefile);
4087             MessageBox(hwnd, buf, "PuTTY Sound Error",
4088                        MB_OK | MB_ICONEXCLAMATION);
4089             cfg.beep = BELL_DEFAULT;
4090         }
4091     }
4092     /* Otherwise, either visual bell or disabled; do nothing here */
4093     if (!has_focus) {
4094         flash_window(2);               /* start */
4095     }
4096 }
4097
4098 /*
4099  * Toggle full screen mode. Thanks to cwis@nerim.fr for the
4100  * implementation.
4101  * Revised by <wez@thebrainroom.com>
4102  */
4103 static void flip_full_screen(void)
4104 {
4105     WINDOWPLACEMENT wp;
4106     LONG style;
4107
4108     wp.length = sizeof(wp);
4109     GetWindowPlacement(hwnd, &wp);
4110
4111     full_screen = !full_screen;
4112
4113     if (full_screen) {
4114         if (wp.showCmd == SW_SHOWMAXIMIZED) {
4115             /* Ooops it was already 'zoomed' we have to unzoom it before
4116              * everything will work right.
4117              */
4118             wp.showCmd = SW_SHOWNORMAL;
4119             SetWindowPlacement(hwnd, &wp);
4120         }
4121
4122         style = GetWindowLong(hwnd, GWL_STYLE) & ~(WS_CAPTION|WS_THICKFRAME);
4123         style &= ~WS_VSCROLL;
4124         if (cfg.scrollbar_in_fullscreen)
4125             style |= WS_VSCROLL;
4126         SetWindowLong(hwnd, GWL_STYLE, style);
4127
4128         /* Some versions of explorer get confused and don't take
4129          * notice of us going fullscreen, so go topmost too.
4130          */
4131         SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
4132                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
4133                      SWP_NOMOVE | SWP_NOSIZE |
4134                      SWP_FRAMECHANGED);
4135
4136         wp.showCmd = SW_SHOWMAXIMIZED;
4137         SetWindowPlacement(hwnd, &wp);
4138     } else {
4139         style = GetWindowLong(hwnd, GWL_STYLE) | WS_CAPTION;
4140         if (cfg.resize_action != RESIZE_DISABLED)
4141             style |= WS_THICKFRAME;
4142         style &= ~WS_VSCROLL;
4143         if (cfg.scrollbar)
4144             style |= WS_VSCROLL;
4145         SetWindowLong(hwnd, GWL_STYLE, style);
4146
4147         SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
4148                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
4149                      SWP_NOMOVE | SWP_NOSIZE |
4150                      SWP_FRAMECHANGED);
4151
4152         wp.showCmd = SW_SHOWNORMAL;
4153         SetWindowPlacement(hwnd, &wp);
4154     }
4155
4156     CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4157                   MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
4158 }
4159
4160 /*
4161  * Minimise or restore the window in response to a server-side
4162  * request.
4163  */
4164 void set_iconic(int iconic)
4165 {
4166     if (IsIconic(hwnd)) {
4167         if (!iconic)
4168             ShowWindow(hwnd, SW_RESTORE);
4169     } else {
4170         if (iconic)
4171             ShowWindow(hwnd, SW_MINIMIZE);
4172     }
4173 }
4174
4175 /*
4176  * Move the window in response to a server-side request.
4177  */
4178 void move_window(int x, int y)
4179 {
4180     SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4181 }
4182
4183 /*
4184  * Move the window to the top or bottom of the z-order in response
4185  * to a server-side request.
4186  */
4187 void set_zorder(int top)
4188 {
4189     if (cfg.alwaysontop || full_screen)
4190         return;                        /* ignore */
4191     SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4192                  SWP_NOMOVE | SWP_NOSIZE);
4193 }
4194
4195 /*
4196  * Refresh the window in response to a server-side request.
4197  */
4198 void refresh_window(void)
4199 {
4200     InvalidateRect(hwnd, NULL, TRUE);
4201 }
4202
4203 /*
4204  * Maximise or restore the window in response to a server-side
4205  * request.
4206  */
4207 void set_zoomed(int zoomed)
4208 {
4209     if (IsZoomed(hwnd) || full_screen) {
4210         if (!zoomed) {
4211             if (full_screen)
4212                 flip_full_screen();
4213             else
4214                 ShowWindow(hwnd, SW_RESTORE);
4215         }
4216     } else {
4217         if (zoomed)
4218             ShowWindow(hwnd, SW_MAXIMIZE);
4219     }
4220 }
4221
4222 /*
4223  * Report whether the window is iconic, for terminal reports.
4224  */
4225 int is_iconic(void)
4226 {
4227     return IsIconic(hwnd);
4228 }
4229
4230 /*
4231  * Report the window's position, for terminal reports.
4232  */
4233 void get_window_pos(int *x, int *y)
4234 {
4235     RECT r;
4236     GetWindowRect(hwnd, &r);
4237     *x = r.left;
4238     *y = r.top;
4239 }
4240
4241 /*
4242  * Report the window's pixel size, for terminal reports.
4243  */
4244 void get_window_pixels(int *x, int *y)
4245 {
4246     RECT r;
4247     GetWindowRect(hwnd, &r);
4248     *x = r.right - r.left;
4249     *y = r.bottom - r.top;
4250 }
4251
4252 /*
4253  * Return the window or icon title.
4254  */
4255 char *get_window_title(int icon)
4256 {
4257     return icon ? icon_name : window_name;
4258 }