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