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