]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - window.c
From RDB: a patch to allow special keys (^C, ^Z, Delete, Return) to
[PuTTY.git] / window.c
1 #include <windows.h>
2 #include <imm.h>
3 #include <commctrl.h>
4 #include <mmsystem.h>
5 #ifndef AUTO_WINSOCK
6 #ifdef WINSOCK_TWO
7 #include <winsock2.h>
8 #else
9 #include <winsock.h>
10 #endif
11 #endif
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 #include <time.h>
16
17 #define PUTTY_DO_GLOBALS               /* actually _define_ globals */
18 #include "putty.h"
19 #include "winstuff.h"
20 #include "storage.h"
21 #include "win_res.h"
22
23 #define IDM_SHOWLOG   0x0010
24 #define IDM_NEWSESS   0x0020
25 #define IDM_DUPSESS   0x0030
26 #define IDM_RECONF    0x0040
27 #define IDM_CLRSB     0x0050
28 #define IDM_RESET     0x0060
29 #define IDM_TEL_AYT   0x0070
30 #define IDM_TEL_BRK   0x0080
31 #define IDM_TEL_SYNCH 0x0090
32 #define IDM_TEL_EC    0x00a0
33 #define IDM_TEL_EL    0x00b0
34 #define IDM_TEL_GA    0x00c0
35 #define IDM_TEL_NOP   0x00d0
36 #define IDM_TEL_ABORT 0x00e0
37 #define IDM_TEL_AO    0x00f0
38 #define IDM_TEL_IP    0x0100
39 #define IDM_TEL_SUSP  0x0110
40 #define IDM_TEL_EOR   0x0120
41 #define IDM_TEL_EOF   0x0130
42 #define IDM_ABOUT     0x0140
43 #define IDM_SAVEDSESS 0x0150
44 #define IDM_COPYALL   0x0160
45
46 #define IDM_SESSLGP   0x0250           /* log type printable */
47 #define IDM_SESSLGA   0x0260           /* log type all chars */
48 #define IDM_SESSLGE   0x0270           /* log end */
49 #define IDM_SAVED_MIN 0x1000
50 #define IDM_SAVED_MAX 0x2000
51
52 #define WM_IGNORE_SIZE (WM_XUSER + 1)
53 #define WM_IGNORE_CLIP (WM_XUSER + 2)
54
55 /* Needed for Chinese support and apparently not always defined. */
56 #ifndef VK_PROCESSKEY
57 #define VK_PROCESSKEY 0xE5
58 #endif
59
60 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
61 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
62                         unsigned char *output);
63 static void cfgtopalette(void);
64 static void init_palette(void);
65 static void init_fonts(int);
66
67 static int extra_width, extra_height;
68
69 static int pending_netevent = 0;
70 static WPARAM pend_netevent_wParam = 0;
71 static LPARAM pend_netevent_lParam = 0;
72 static void enact_pending_netevent(void);
73
74 static time_t last_movement = 0;
75
76 #define FONT_NORMAL 0
77 #define FONT_BOLD 1
78 #define FONT_UNDERLINE 2
79 #define FONT_BOLDUND 3
80 #define FONT_OEM 4
81 #define FONT_OEMBOLD 5
82 #define FONT_OEMBOLDUND 6
83 #define FONT_OEMUND 7
84 static HFONT fonts[8];
85 static int font_needs_hand_underlining;
86 static enum {
87     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
88 } bold_mode;
89 static enum {
90     UND_LINE, UND_FONT
91 } und_mode;
92 static int descent;
93
94 #define NCOLOURS 24
95 static COLORREF colours[NCOLOURS];
96 static HPALETTE pal;
97 static LPLOGPALETTE logpal;
98 static RGBTRIPLE defpal[NCOLOURS];
99
100 static HWND hwnd;
101
102 static HBITMAP caretbm;
103
104 static int dbltime, lasttime, lastact;
105 static Mouse_Button lastbtn;
106
107 /* this allows xterm-style mouse handling. */
108 static int send_raw_mouse = 0;
109 static int wheel_accumulator = 0;
110
111 static char *window_name, *icon_name;
112
113 static int compose_state = 0;
114
115 /* Dummy routine, only required in plink. */
116 void ldisc_update(int echo, int edit)
117 {
118 }
119
120 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
121 {
122     static char appname[] = "PuTTY";
123     WORD winsock_ver;
124     WSADATA wsadata;
125     WNDCLASS wndclass;
126     MSG msg;
127     int guess_width, guess_height;
128
129     hinst = inst;
130     flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
131
132     winsock_ver = MAKEWORD(1, 1);
133     if (WSAStartup(winsock_ver, &wsadata)) {
134         MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
135                    MB_OK | MB_ICONEXCLAMATION);
136         return 1;
137     }
138     if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
139         MessageBox(NULL, "WinSock version is incompatible with 1.1",
140                    "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
141         WSACleanup();
142         return 1;
143     }
144     /* WISHLIST: maybe allow config tweaking even if winsock not present? */
145     sk_init();
146
147     InitCommonControls();
148
149     /* Ensure a Maximize setting in Explorer doesn't maximise the
150      * config box. */
151     defuse_showwindow();
152
153     /*
154      * Process the command line.
155      */
156     {
157         char *p;
158
159         default_protocol = DEFAULT_PROTOCOL;
160         default_port = DEFAULT_PORT;
161         cfg.logtype = LGTYP_NONE;
162
163         do_defaults(NULL, &cfg);
164
165         p = cmdline;
166         while (*p && isspace(*p))
167             p++;
168
169         /*
170          * Process command line options first. Yes, this can be
171          * done better, and it will be as soon as I have the
172          * energy...
173          */
174         while (*p == '-') {
175             char *q = p + strcspn(p, " \t");
176             p++;
177             if (q == p + 3 &&
178                 tolower(p[0]) == 's' &&
179                 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
180                 default_protocol = cfg.protocol = PROT_SSH;
181                 default_port = cfg.port = 22;
182             } else if (q == p + 7 &&
183                        tolower(p[0]) == 'c' &&
184                        tolower(p[1]) == 'l' &&
185                        tolower(p[2]) == 'e' &&
186                        tolower(p[3]) == 'a' &&
187                        tolower(p[4]) == 'n' &&
188                        tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
189                 /*
190                  * `putty -cleanup'. Remove all registry entries
191                  * associated with PuTTY, and also find and delete
192                  * the random seed file.
193                  */
194                 if (MessageBox(NULL,
195                                "This procedure will remove ALL Registry\n"
196                                "entries associated with PuTTY, and will\n"
197                                "also remove the PuTTY random seed file.\n"
198                                "\n"
199                                "THIS PROCESS WILL DESTROY YOUR SAVED\n"
200                                "SESSIONS. Are you really sure you want\n"
201                                "to continue?",
202                                "PuTTY Warning",
203                                MB_YESNO | MB_ICONWARNING) == IDYES) {
204                     cleanup_all();
205                 }
206                 exit(0);
207             }
208             p = q + strspn(q, " \t");
209         }
210
211         /*
212          * An initial @ means to activate a saved session.
213          */
214         if (*p == '@') {
215             int i = strlen(p);
216             while (i > 1 && isspace(p[i - 1]))
217                 i--;
218             p[i] = '\0';
219             do_defaults(p + 1, &cfg);
220             if (!*cfg.host && !do_config()) {
221                 WSACleanup();
222                 return 0;
223             }
224         } else if (*p == '&') {
225             /*
226              * An initial & means we've been given a command line
227              * containing the hex value of a HANDLE for a file
228              * mapping object, which we must then extract as a
229              * config.
230              */
231             HANDLE filemap;
232             Config *cp;
233             if (sscanf(p + 1, "%p", &filemap) == 1 &&
234                 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
235                                     0, 0, sizeof(Config))) != NULL) {
236                 cfg = *cp;
237                 UnmapViewOfFile(cp);
238                 CloseHandle(filemap);
239             } else if (!do_config()) {
240                 WSACleanup();
241                 return 0;
242             }
243         } else if (*p) {
244             char *q = p;
245             /*
246              * If the hostname starts with "telnet:", set the
247              * protocol to Telnet and process the string as a
248              * Telnet URL.
249              */
250             if (!strncmp(q, "telnet:", 7)) {
251                 char c;
252
253                 q += 7;
254                 if (q[0] == '/' && q[1] == '/')
255                     q += 2;
256                 cfg.protocol = PROT_TELNET;
257                 p = q;
258                 while (*p && *p != ':' && *p != '/')
259                     p++;
260                 c = *p;
261                 if (*p)
262                     *p++ = '\0';
263                 if (c == ':')
264                     cfg.port = atoi(p);
265                 else
266                     cfg.port = -1;
267                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
268                 cfg.host[sizeof(cfg.host) - 1] = '\0';
269             } else {
270                 while (*p && !isspace(*p))
271                     p++;
272                 if (*p)
273                     *p++ = '\0';
274                 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
275                 cfg.host[sizeof(cfg.host) - 1] = '\0';
276                 while (*p && isspace(*p))
277                     p++;
278                 if (*p)
279                     cfg.port = atoi(p);
280                 else
281                     cfg.port = -1;
282             }
283         } else {
284             if (!do_config()) {
285                 WSACleanup();
286                 return 0;
287             }
288         }
289
290         /* See if host is of the form user@host */
291         if (cfg.host[0] != '\0') {
292             char *atsign = strchr(cfg.host, '@');
293             /* Make sure we're not overflowing the user field */
294             if (atsign) {
295                 if (atsign - cfg.host < sizeof cfg.username) {
296                     strncpy(cfg.username, cfg.host, atsign - cfg.host);
297                     cfg.username[atsign - cfg.host] = '\0';
298                 }
299                 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
300             }
301         }
302
303         /*
304          * Trim a colon suffix off the hostname if it's there.
305          */
306         cfg.host[strcspn(cfg.host, ":")] = '\0';
307     }
308
309     /*
310      * Select protocol. This is farmed out into a table in a
311      * separate file to enable an ssh-free variant.
312      */
313     {
314         int i;
315         back = NULL;
316         for (i = 0; backends[i].backend != NULL; i++)
317             if (backends[i].protocol == cfg.protocol) {
318                 back = backends[i].backend;
319                 break;
320             }
321         if (back == NULL) {
322             MessageBox(NULL, "Unsupported protocol number found",
323                        "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
324             WSACleanup();
325             return 1;
326         }
327     }
328
329     /* Check for invalid Port number (i.e. zero) */
330     if (cfg.port == 0) {
331         MessageBox(NULL, "Invalid Port Number",
332                    "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
333         WSACleanup();
334         return 1;
335     }
336
337     if (!prev) {
338         wndclass.style = 0;
339         wndclass.lpfnWndProc = WndProc;
340         wndclass.cbClsExtra = 0;
341         wndclass.cbWndExtra = 0;
342         wndclass.hInstance = inst;
343         wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
344         wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
345         wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
346         wndclass.lpszMenuName = NULL;
347         wndclass.lpszClassName = appname;
348
349         RegisterClass(&wndclass);
350     }
351
352     hwnd = NULL;
353
354     savelines = cfg.savelines;
355     term_init();
356
357     cfgtopalette();
358
359     /*
360      * Guess some defaults for the window size. This all gets
361      * updated later, so we don't really care too much. However, we
362      * do want the font width/height guesses to correspond to a
363      * large font rather than a small one...
364      */
365
366     font_width = 10;
367     font_height = 20;
368     extra_width = 25;
369     extra_height = 28;
370     term_size(cfg.height, cfg.width, cfg.savelines);
371     guess_width = extra_width + font_width * cols;
372     guess_height = extra_height + font_height * rows;
373     {
374         RECT r;
375         HWND w = GetDesktopWindow();
376         GetWindowRect(w, &r);
377         if (guess_width > r.right - r.left)
378             guess_width = r.right - r.left;
379         if (guess_height > r.bottom - r.top)
380             guess_height = r.bottom - r.top;
381     }
382
383     {
384         int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
385         int exwinmode = 0;
386         if (!cfg.scrollbar)
387             winmode &= ~(WS_VSCROLL);
388         if (cfg.locksize)
389             winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
390         if (cfg.alwaysontop)
391             exwinmode |= WS_EX_TOPMOST;
392         if (cfg.sunken_edge)
393             exwinmode |= WS_EX_CLIENTEDGE;
394         hwnd = CreateWindowEx(exwinmode, appname, appname,
395                               winmode, CW_USEDEFAULT, CW_USEDEFAULT,
396                               guess_width, guess_height,
397                               NULL, NULL, inst, NULL);
398     }
399
400     /*
401      * Initialise the fonts, simultaneously correcting the guesses
402      * for font_{width,height}.
403      */
404     bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
405     und_mode = UND_FONT;
406     init_fonts(0);
407
408     /*
409      * Correct the guesses for extra_{width,height}.
410      */
411     {
412         RECT cr, wr;
413         GetWindowRect(hwnd, &wr);
414         GetClientRect(hwnd, &cr);
415         extra_width = wr.right - wr.left - cr.right + cr.left;
416         extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
417     }
418
419     /*
420      * Resize the window, now we know what size we _really_ want it
421      * to be.
422      */
423     guess_width = extra_width + font_width * cols;
424     guess_height = extra_height + font_height * rows;
425     SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
426     SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
427                  SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
428
429     /*
430      * Set up a caret bitmap, with no content.
431      */
432     {
433         char *bits;
434         int size = (font_width + 15) / 16 * 2 * font_height;
435         bits = smalloc(size);
436         memset(bits, 0, size);
437         caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
438         sfree(bits);
439     }
440     CreateCaret(hwnd, caretbm, font_width, font_height);
441
442     /*
443      * Initialise the scroll bar.
444      */
445     {
446         SCROLLINFO si;
447
448         si.cbSize = sizeof(si);
449         si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
450         si.nMin = 0;
451         si.nMax = rows - 1;
452         si.nPage = rows;
453         si.nPos = 0;
454         SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
455     }
456
457     /*
458      * Start up the telnet connection.
459      */
460     {
461         char *error;
462         char msg[1024], *title;
463         char *realhost;
464
465         error = back->init(cfg.host, cfg.port, &realhost);
466         if (error) {
467             sprintf(msg, "Unable to open connection to\n"
468                     "%.800s\n" "%s", cfg.host, error);
469             MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
470             return 0;
471         }
472         window_name = icon_name = NULL;
473         if (*cfg.wintitle) {
474             title = cfg.wintitle;
475         } else {
476             sprintf(msg, "%s - PuTTY", realhost);
477             title = msg;
478         }
479         sfree(realhost);
480         set_title(title);
481         set_icon(title);
482     }
483
484     session_closed = FALSE;
485
486     /*
487      * Set up the input and output buffers.
488      */
489     inbuf_head = 0;
490     outbuf_reap = outbuf_head = 0;
491
492     /*
493      * Prepare the mouse handler.
494      */
495     lastact = MA_NOTHING;
496     lastbtn = MBT_NOTHING;
497     dbltime = GetDoubleClickTime();
498
499     /*
500      * Set up the session-control options on the system menu.
501      */
502     {
503         HMENU m = GetSystemMenu(hwnd, FALSE);
504         HMENU p, s;
505         int i;
506
507         AppendMenu(m, MF_SEPARATOR, 0, 0);
508         if (cfg.protocol == PROT_TELNET) {
509             p = CreateMenu();
510             AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
511             AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
512             AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
513             AppendMenu(p, MF_SEPARATOR, 0, 0);
514             AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
515             AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
516             AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
517             AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
518             AppendMenu(p, MF_SEPARATOR, 0, 0);
519             AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
520             AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
521             AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
522             AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
523             AppendMenu(p, MF_SEPARATOR, 0, 0);
524             AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
525             AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
526             AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
527                        "Telnet Command");
528             AppendMenu(m, MF_SEPARATOR, 0, 0);
529         }
530         AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
531         AppendMenu(m, MF_SEPARATOR, 0, 0);
532         AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
533         AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
534         s = CreateMenu();
535         get_sesslist(TRUE);
536         for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
537             AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
538                        sessions[i]);
539         AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
540         AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
541         AppendMenu(m, MF_SEPARATOR, 0, 0);
542         AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
543         AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
544         AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
545         AppendMenu(m, MF_SEPARATOR, 0, 0);
546         AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
547     }
548
549     /*
550      * Finally show the window!
551      */
552     ShowWindow(hwnd, show);
553
554     /*
555      * Open the initial log file if there is one.
556      */
557     logfopen();
558
559     /*
560      * Set the palette up.
561      */
562     pal = NULL;
563     logpal = NULL;
564     init_palette();
565
566     has_focus = (GetForegroundWindow() == hwnd);
567     UpdateWindow(hwnd);
568
569     if (GetMessage(&msg, NULL, 0, 0) == 1) {
570         int timer_id = 0, long_timer = 0;
571
572         while (msg.message != WM_QUIT) {
573             /* Sometimes DispatchMessage calls routines that use their own
574              * GetMessage loop, setup this timer so we get some control back.
575              *
576              * Also call term_update() from the timer so that if the host
577              * is sending data flat out we still do redraws.
578              */
579             if (timer_id && long_timer) {
580                 KillTimer(hwnd, timer_id);
581                 long_timer = timer_id = 0;
582             }
583             if (!timer_id)
584                 timer_id = SetTimer(hwnd, 1, 20, NULL);
585             if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
586                 DispatchMessage(&msg);
587
588             /* Make sure we blink everything that needs it. */
589             term_blink(0);
590
591             /* Send the paste buffer if there's anything to send */
592             term_paste();
593
594             /* If there's nothing new in the queue then we can do everything
595              * we've delayed, reading the socket, writing, and repainting
596              * the window.
597              */
598             if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
599                 continue;
600
601             if (pending_netevent) {
602                 enact_pending_netevent();
603
604                 /* Force the cursor blink on */
605                 term_blink(1);
606
607                 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
608                     continue;
609             }
610
611             /* Okay there is now nothing to do so we make sure the screen is
612              * completely up to date then tell windows to call us in a little 
613              * while.
614              */
615             if (timer_id) {
616                 KillTimer(hwnd, timer_id);
617                 timer_id = 0;
618             }
619             HideCaret(hwnd);
620             if (inbuf_head)
621                 term_out();
622             term_update();
623             ShowCaret(hwnd);
624             if (in_vbell)
625                 /* Hmm, term_update didn't want to do an update too soon ... */
626                 timer_id = SetTimer(hwnd, 1, 50, NULL);
627             else if (!has_focus)
628                 timer_id = SetTimer(hwnd, 1, 2000, NULL);
629             else
630                 timer_id = SetTimer(hwnd, 1, 100, NULL);
631             long_timer = 1;
632
633             /* There's no point rescanning everything in the message queue
634              * so we do an apparently unnecessary wait here
635              */
636             WaitMessage();
637             if (GetMessage(&msg, NULL, 0, 0) != 1)
638                 break;
639         }
640     }
641
642     /*
643      * Clean up.
644      */
645     {
646         int i;
647         for (i = 0; i < 8; i++)
648             if (fonts[i])
649                 DeleteObject(fonts[i]);
650     }
651     sfree(logpal);
652     if (pal)
653         DeleteObject(pal);
654     WSACleanup();
655
656     if (cfg.protocol == PROT_SSH) {
657         random_save_seed();
658 #ifdef MSCRYPTOAPI
659         crypto_wrapup();
660 #endif
661     }
662
663     return msg.wParam;
664 }
665
666 /*
667  * Set up, or shut down, an AsyncSelect. Called from winnet.c.
668  */
669 char *do_select(SOCKET skt, int startup)
670 {
671     int msg, events;
672     if (startup) {
673         msg = WM_NETEVENT;
674         events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE;
675     } else {
676         msg = events = 0;
677     }
678     if (!hwnd)
679         return "do_select(): internal error (hwnd==NULL)";
680     if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
681         switch (WSAGetLastError()) {
682           case WSAENETDOWN:
683             return "Network is down";
684           default:
685             return "WSAAsyncSelect(): unknown error";
686         }
687     }
688     return NULL;
689 }
690
691 /*
692  * set or clear the "raw mouse message" mode
693  */
694 void set_raw_mouse_mode(int activate)
695 {
696     send_raw_mouse = activate;
697     SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
698 }
699
700 /*
701  * Print a message box and close the connection.
702  */
703 void connection_fatal(char *fmt, ...)
704 {
705     va_list ap;
706     char stuff[200];
707
708     va_start(ap, fmt);
709     vsprintf(stuff, fmt, ap);
710     va_end(ap);
711     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
712     if (cfg.close_on_exit == COE_ALWAYS)
713         PostQuitMessage(1);
714     else {
715         session_closed = TRUE;
716         SetWindowText(hwnd, "PuTTY (inactive)");
717     }
718 }
719
720 /*
721  * Actually do the job requested by a WM_NETEVENT
722  */
723 static void enact_pending_netevent(void)
724 {
725     static int reentering = 0;
726     extern int select_result(WPARAM, LPARAM);
727     int ret;
728
729     if (reentering)
730         return;                        /* don't unpend the pending */
731
732     pending_netevent = FALSE;
733
734     reentering = 1;
735     ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
736     reentering = 0;
737
738     if (ret == 0 && !session_closed) {
739         /* Abnormal exits will already have set session_closed and taken
740          * appropriate action. */
741         if (cfg.close_on_exit == COE_ALWAYS ||
742             cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
743         else {
744             session_closed = TRUE;
745             SetWindowText(hwnd, "PuTTY (inactive)");
746             MessageBox(hwnd, "Connection closed by remote host",
747                        "PuTTY", MB_OK | MB_ICONINFORMATION);
748         }
749     }
750 }
751
752 /*
753  * Copy the colour palette from the configuration data into defpal.
754  * This is non-trivial because the colour indices are different.
755  */
756 static void cfgtopalette(void)
757 {
758     int i;
759     static const int ww[] = {
760         6, 7, 8, 9, 10, 11, 12, 13,
761         14, 15, 16, 17, 18, 19, 20, 21,
762         0, 1, 2, 3, 4, 4, 5, 5
763     };
764
765     for (i = 0; i < 24; i++) {
766         int w = ww[i];
767         defpal[i].rgbtRed = cfg.colours[w][0];
768         defpal[i].rgbtGreen = cfg.colours[w][1];
769         defpal[i].rgbtBlue = cfg.colours[w][2];
770     }
771 }
772
773 /*
774  * Set up the colour palette.
775  */
776 static void init_palette(void)
777 {
778     int i;
779     HDC hdc = GetDC(hwnd);
780     if (hdc) {
781         if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
782             logpal = smalloc(sizeof(*logpal)
783                              - sizeof(logpal->palPalEntry)
784                              + NCOLOURS * sizeof(PALETTEENTRY));
785             logpal->palVersion = 0x300;
786             logpal->palNumEntries = NCOLOURS;
787             for (i = 0; i < NCOLOURS; i++) {
788                 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
789                 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
790                 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
791                 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
792             }
793             pal = CreatePalette(logpal);
794             if (pal) {
795                 SelectPalette(hdc, pal, FALSE);
796                 RealizePalette(hdc);
797                 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
798             }
799         }
800         ReleaseDC(hwnd, hdc);
801     }
802     if (pal)
803         for (i = 0; i < NCOLOURS; i++)
804             colours[i] = PALETTERGB(defpal[i].rgbtRed,
805                                     defpal[i].rgbtGreen,
806                                     defpal[i].rgbtBlue);
807     else
808         for (i = 0; i < NCOLOURS; i++)
809             colours[i] = RGB(defpal[i].rgbtRed,
810                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
811 }
812
813 /*
814  * Initialise all the fonts we will need. There may be as many as
815  * eight or as few as one. We also:
816  *
817  * - check the font width and height, correcting our guesses if
818  *   necessary.
819  *
820  * - verify that the bold font is the same width as the ordinary
821  *   one, and engage shadow bolding if not.
822  * 
823  * - verify that the underlined font is the same width as the
824  *   ordinary one (manual underlining by means of line drawing can
825  *   be done in a pinch).
826  */
827 static void init_fonts(int pick_width)
828 {
829     TEXTMETRIC tm;
830     int i;
831     int fsize[8];
832     HDC hdc;
833     int fw_dontcare, fw_bold;
834     int firstchar = ' ';
835
836 #ifdef CHECKOEMFONT
837   font_messup:
838 #endif
839     for (i = 0; i < 8; i++)
840         fonts[i] = NULL;
841
842     if (cfg.fontisbold) {
843         fw_dontcare = FW_BOLD;
844         fw_bold = FW_HEAVY;
845     } else {
846         fw_dontcare = FW_DONTCARE;
847         fw_bold = FW_BOLD;
848     }
849
850     hdc = GetDC(hwnd);
851
852     font_height = cfg.fontheight;
853     if (font_height > 0) {
854         font_height =
855             -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
856     }
857     font_width = pick_width;
858
859 #define f(i,c,w,u) \
860     fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
861                            c, OUT_DEFAULT_PRECIS, \
862                            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
863                            FIXED_PITCH | FF_DONTCARE, cfg.font)
864
865     if (cfg.vtmode != VT_OEMONLY) {
866         f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
867
868         SelectObject(hdc, fonts[FONT_NORMAL]);
869         GetTextMetrics(hdc, &tm);
870         font_height = tm.tmHeight;
871         font_width = tm.tmAveCharWidth;
872
873         f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
874
875         /*
876          * Some fonts, e.g. 9-pt Courier, draw their underlines
877          * outside their character cell. We successfully prevent
878          * screen corruption by clipping the text output, but then
879          * we lose the underline completely. Here we try to work
880          * out whether this is such a font, and if it is, we set a
881          * flag that causes underlines to be drawn by hand.
882          *
883          * Having tried other more sophisticated approaches (such
884          * as examining the TEXTMETRIC structure or requesting the
885          * height of a string), I think we'll do this the brute
886          * force way: we create a small bitmap, draw an underlined
887          * space on it, and test to see whether any pixels are
888          * foreground-coloured. (Since we expect the underline to
889          * go all the way across the character cell, we only search
890          * down a single column of the bitmap, half way across.)
891          */
892         {
893             HDC und_dc;
894             HBITMAP und_bm, und_oldbm;
895             int i, gotit;
896             COLORREF c;
897
898             und_dc = CreateCompatibleDC(hdc);
899             und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
900             und_oldbm = SelectObject(und_dc, und_bm);
901             SelectObject(und_dc, fonts[FONT_UNDERLINE]);
902             SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
903             SetTextColor(und_dc, RGB(255, 255, 255));
904             SetBkColor(und_dc, RGB(0, 0, 0));
905             SetBkMode(und_dc, OPAQUE);
906             ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
907             gotit = FALSE;
908             for (i = 0; i < font_height; i++) {
909                 c = GetPixel(und_dc, font_width / 2, i);
910                 if (c != RGB(0, 0, 0))
911                     gotit = TRUE;
912             }
913             SelectObject(und_dc, und_oldbm);
914             DeleteObject(und_bm);
915             DeleteDC(und_dc);
916             font_needs_hand_underlining = !gotit;
917         }
918
919         if (bold_mode == BOLD_FONT) {
920             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
921             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
922         }
923
924         if (cfg.vtmode == VT_OEMANSI) {
925             f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
926             f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
927
928             if (bold_mode == BOLD_FONT) {
929                 f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
930                 f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
931             }
932         }
933     } else {
934         f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
935
936         SelectObject(hdc, fonts[FONT_OEM]);
937         GetTextMetrics(hdc, &tm);
938         font_height = tm.tmHeight;
939         font_width = tm.tmAveCharWidth;
940
941         f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
942
943         if (bold_mode == BOLD_FONT) {
944             f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
945             f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
946         }
947     }
948 #undef f
949
950     descent = tm.tmAscent + 1;
951     if (descent >= font_height)
952         descent = font_height - 1;
953     firstchar = tm.tmFirstChar;
954
955     for (i = 0; i < 8; i++) {
956         if (fonts[i]) {
957             if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
958                 fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
959             else
960                 fsize[i] = -i;
961         } else
962             fsize[i] = -i;
963     }
964
965     ReleaseDC(hwnd, hdc);
966
967     /* ... This is wrong in OEM only mode */
968     if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
969         (bold_mode == BOLD_FONT &&
970          fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
971         und_mode = UND_LINE;
972         DeleteObject(fonts[FONT_UNDERLINE]);
973         if (bold_mode == BOLD_FONT)
974             DeleteObject(fonts[FONT_BOLDUND]);
975     }
976
977     if (bold_mode == BOLD_FONT && fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
978         bold_mode = BOLD_SHADOW;
979         DeleteObject(fonts[FONT_BOLD]);
980         if (und_mode == UND_FONT)
981             DeleteObject(fonts[FONT_BOLDUND]);
982     }
983 #ifdef CHECKOEMFONT
984     /* With the fascist font painting it doesn't matter if the linedraw font
985      * isn't exactly the right size anymore so we don't have to check this.
986      */
987     if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL]) {
988         if (cfg.fontcharset == OEM_CHARSET) {
989             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
990                        "different sizes. Using OEM-only mode instead",
991                        "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
992             cfg.vtmode = VT_OEMONLY;
993         } else if (firstchar < ' ') {
994             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
995                        "different sizes. Using XTerm mode instead",
996                        "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
997             cfg.vtmode = VT_XWINDOWS;
998         } else {
999             MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
1000                        "different sizes. Using ISO8859-1 mode instead",
1001                        "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
1002             cfg.vtmode = VT_POORMAN;
1003         }
1004
1005         for (i = 0; i < 8; i++)
1006             if (fonts[i])
1007                 DeleteObject(fonts[i]);
1008         goto font_messup;
1009     }
1010 #endif
1011 }
1012
1013 void request_resize(int w, int h, int refont)
1014 {
1015     int width, height;
1016
1017     /* If the window is maximized supress resizing attempts */
1018     if (IsZoomed(hwnd))
1019         return;
1020
1021 #ifdef CHECKOEMFONT
1022     /* Don't do this in OEMANSI, you may get disable messages */
1023     if (refont && w != cols && (cols == 80 || cols == 132)
1024         && cfg.vtmode != VT_OEMANSI)
1025 #else
1026     if (refont && w != cols && (cols == 80 || cols == 132))
1027 #endif
1028     {
1029         /* If font width too big for screen should we shrink the font more ? */
1030         if (w == 132)
1031             font_width = ((font_width * cols + w / 2) / w);
1032         else
1033             font_width = 0;
1034         {
1035             int i;
1036             for (i = 0; i < 8; i++)
1037                 if (fonts[i])
1038                     DeleteObject(fonts[i]);
1039         }
1040         bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1041         und_mode = UND_FONT;
1042         init_fonts(font_width);
1043     } else {
1044         static int first_time = 1;
1045         static RECT ss;
1046
1047         switch (first_time) {
1048           case 1:
1049             /* Get the size of the screen */
1050             if (GetClientRect(GetDesktopWindow(), &ss))
1051                 /* first_time = 0 */ ;
1052             else {
1053                 first_time = 2;
1054                 break;
1055             }
1056           case 0:
1057             /* Make sure the values are sane */
1058             width = (ss.right - ss.left - extra_width) / font_width;
1059             height = (ss.bottom - ss.top - extra_height) / font_height;
1060
1061             if (w > width)
1062                 w = width;
1063             if (h > height)
1064                 h = height;
1065             if (w < 15)
1066                 w = 15;
1067             if (h < 1)
1068                 w = 1;
1069         }
1070     }
1071
1072     width = extra_width + font_width * w;
1073     height = extra_height + font_height * h;
1074
1075     SetWindowPos(hwnd, NULL, 0, 0, width, height,
1076                  SWP_NOACTIVATE | SWP_NOCOPYBITS |
1077                  SWP_NOMOVE | SWP_NOZORDER);
1078 }
1079
1080 static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1081 {
1082     int thistime = GetMessageTime();
1083
1084     if (send_raw_mouse) {
1085         term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1086         return;
1087     }
1088
1089     if (lastbtn == b && thistime - lasttime < dbltime) {
1090         lastact = (lastact == MA_CLICK ? MA_2CLK :
1091                    lastact == MA_2CLK ? MA_3CLK :
1092                    lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1093     } else {
1094         lastbtn = b;
1095         lastact = MA_CLICK;
1096     }
1097     if (lastact != MA_NOTHING)
1098         term_mouse(b, lastact, x, y, shift, ctrl);
1099     lasttime = thistime;
1100 }
1101
1102 /*
1103  * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1104  * into a cooked one (SELECT, EXTEND, PASTE).
1105  */
1106 Mouse_Button translate_button(Mouse_Button button)
1107 {
1108     if (button == MBT_LEFT)
1109         return MBT_SELECT;
1110     if (button == MBT_MIDDLE)
1111         return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1112     if (button == MBT_RIGHT)
1113         return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1114 }
1115
1116 static void show_mouseptr(int show)
1117 {
1118     static int cursor_visible = 1;
1119     if (!cfg.hide_mouseptr)            /* override if this feature disabled */
1120         show = 1;
1121     if (cursor_visible && !show)
1122         ShowCursor(FALSE);
1123     else if (!cursor_visible && show)
1124         ShowCursor(TRUE);
1125     cursor_visible = show;
1126 }
1127
1128 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1129                                 WPARAM wParam, LPARAM lParam)
1130 {
1131     HDC hdc;
1132     static int ignore_size = FALSE;
1133     static int ignore_clip = FALSE;
1134     static int just_reconfigged = FALSE;
1135     static int resizing = FALSE;
1136     static int need_backend_resize = FALSE;
1137     static int defered_resize = FALSE;
1138
1139     switch (message) {
1140       case WM_TIMER:
1141         if (pending_netevent)
1142             enact_pending_netevent();
1143         if (inbuf_head)
1144             term_out();
1145         noise_regular();
1146         HideCaret(hwnd);
1147         term_update();
1148         ShowCaret(hwnd);
1149         if (cfg.ping_interval > 0) {
1150             time_t now;
1151             time(&now);
1152             if (now - last_movement > cfg.ping_interval) {
1153                 back->special(TS_PING);
1154                 last_movement = now;
1155             }
1156         }
1157         return 0;
1158       case WM_CREATE:
1159         break;
1160       case WM_CLOSE:
1161         show_mouseptr(1);
1162         if (!cfg.warn_on_close || session_closed ||
1163             MessageBox(hwnd,
1164                        "Are you sure you want to close this session?",
1165                        "PuTTY Exit Confirmation",
1166                        MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1167             DestroyWindow(hwnd);
1168         return 0;
1169       case WM_DESTROY:
1170         show_mouseptr(1);
1171         PostQuitMessage(0);
1172         return 0;
1173       case WM_SYSCOMMAND:
1174         switch (wParam & ~0xF) {       /* low 4 bits reserved to Windows */
1175           case IDM_SHOWLOG:
1176             showeventlog(hwnd);
1177             break;
1178           case IDM_NEWSESS:
1179           case IDM_DUPSESS:
1180           case IDM_SAVEDSESS:
1181             {
1182                 char b[2048];
1183                 char c[30], *cl;
1184                 int freecl = FALSE;
1185                 STARTUPINFO si;
1186                 PROCESS_INFORMATION pi;
1187                 HANDLE filemap = NULL;
1188
1189                 if (wParam == IDM_DUPSESS) {
1190                     /*
1191                      * Allocate a file-mapping memory chunk for the
1192                      * config structure.
1193                      */
1194                     SECURITY_ATTRIBUTES sa;
1195                     Config *p;
1196
1197                     sa.nLength = sizeof(sa);
1198                     sa.lpSecurityDescriptor = NULL;
1199                     sa.bInheritHandle = TRUE;
1200                     filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1201                                                 &sa,
1202                                                 PAGE_READWRITE,
1203                                                 0, sizeof(Config), NULL);
1204                     if (filemap) {
1205                         p = (Config *) MapViewOfFile(filemap,
1206                                                      FILE_MAP_WRITE,
1207                                                      0, 0, sizeof(Config));
1208                         if (p) {
1209                             *p = cfg;  /* structure copy */
1210                             UnmapViewOfFile(p);
1211                         }
1212                     }
1213                     sprintf(c, "putty &%p", filemap);
1214                     cl = c;
1215                 } else if (wParam == IDM_SAVEDSESS) {
1216                     char *session =
1217                         sessions[(lParam - IDM_SAVED_MIN) / 16];
1218                     cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1219                     if (!cl)
1220                         cl = NULL;     /* not a very important failure mode */
1221                     else {
1222                         sprintf(cl, "putty @%s", session);
1223                         freecl = TRUE;
1224                     }
1225                 } else
1226                     cl = NULL;
1227
1228                 GetModuleFileName(NULL, b, sizeof(b) - 1);
1229                 si.cb = sizeof(si);
1230                 si.lpReserved = NULL;
1231                 si.lpDesktop = NULL;
1232                 si.lpTitle = NULL;
1233                 si.dwFlags = 0;
1234                 si.cbReserved2 = 0;
1235                 si.lpReserved2 = NULL;
1236                 CreateProcess(b, cl, NULL, NULL, TRUE,
1237                               NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1238
1239                 if (filemap)
1240                     CloseHandle(filemap);
1241                 if (freecl)
1242                     sfree(cl);
1243             }
1244             break;
1245           case IDM_RECONF:
1246             {
1247                 int prev_alwaysontop = cfg.alwaysontop;
1248                 int prev_sunken_edge = cfg.sunken_edge;
1249                 char oldlogfile[FILENAME_MAX];
1250                 int oldlogtype;
1251                 int need_setwpos = FALSE;
1252                 int old_fwidth, old_fheight;
1253
1254                 strcpy(oldlogfile, cfg.logfilename);
1255                 oldlogtype = cfg.logtype;
1256                 old_fwidth = font_width;
1257                 old_fheight = font_height;
1258                 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1259
1260                 if (!do_reconfig(hwnd))
1261                     break;
1262
1263                 if (strcmp(oldlogfile, cfg.logfilename) ||
1264                     oldlogtype != cfg.logtype) {
1265                     logfclose();       /* reset logging */
1266                     logfopen();
1267                 }
1268
1269                 just_reconfigged = TRUE;
1270                 {
1271                     int i;
1272                     for (i = 0; i < 8; i++)
1273                         if (fonts[i])
1274                             DeleteObject(fonts[i]);
1275                 }
1276                 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1277                 und_mode = UND_FONT;
1278                 init_fonts(0);
1279                 sfree(logpal);
1280                 /*
1281                  * Flush the line discipline's edit buffer in the
1282                  * case where local editing has just been disabled.
1283                  */
1284                 ldisc_send(NULL, 0);
1285                 if (pal)
1286                     DeleteObject(pal);
1287                 logpal = NULL;
1288                 pal = NULL;
1289                 cfgtopalette();
1290                 init_palette();
1291
1292                 /* Enable or disable the scroll bar, etc */
1293                 {
1294                     LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1295                     LONG nexflag, exflag =
1296                         GetWindowLong(hwnd, GWL_EXSTYLE);
1297
1298                     nexflag = exflag;
1299                     if (cfg.alwaysontop != prev_alwaysontop) {
1300                         if (cfg.alwaysontop) {
1301                             nexflag |= WS_EX_TOPMOST;
1302                             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1303                                          SWP_NOMOVE | SWP_NOSIZE);
1304                         } else {
1305                             nexflag &= ~(WS_EX_TOPMOST);
1306                             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1307                                          SWP_NOMOVE | SWP_NOSIZE);
1308                         }
1309                     }
1310                     if (cfg.sunken_edge)
1311                         nexflag |= WS_EX_CLIENTEDGE;
1312                     else
1313                         nexflag &= ~(WS_EX_CLIENTEDGE);
1314
1315                     nflg = flag;
1316                     if (cfg.scrollbar)
1317                         nflg |= WS_VSCROLL;
1318                     else
1319                         nflg &= ~WS_VSCROLL;
1320                     if (cfg.locksize)
1321                         nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1322                     else
1323                         nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1324
1325                     if (nflg != flag || nexflag != exflag) {
1326                         RECT cr, wr;
1327
1328                         if (nflg != flag)
1329                             SetWindowLong(hwnd, GWL_STYLE, nflg);
1330                         if (nexflag != exflag)
1331                             SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1332
1333                         SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1334
1335                         SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1336                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1337                                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1338                                      | SWP_FRAMECHANGED);
1339
1340                         GetWindowRect(hwnd, &wr);
1341                         GetClientRect(hwnd, &cr);
1342                         extra_width =
1343                             wr.right - wr.left - cr.right + cr.left;
1344                         extra_height =
1345                             wr.bottom - wr.top - cr.bottom + cr.top;
1346                         need_setwpos = TRUE;
1347                     }
1348                 }
1349
1350                 if (cfg.height != rows ||
1351                     cfg.width != cols ||
1352                     old_fwidth != font_width ||
1353                     old_fheight != font_height ||
1354                     cfg.savelines != savelines ||
1355                     cfg.sunken_edge != prev_sunken_edge)
1356                         need_setwpos = TRUE;
1357
1358                 if (IsZoomed(hwnd)) {
1359                     int w, h;
1360                     RECT cr;
1361                     if (need_setwpos)
1362                         defered_resize = TRUE;
1363
1364                     GetClientRect(hwnd, &cr);
1365                     w = cr.right - cr.left;
1366                     h = cr.bottom - cr.top;
1367                     w = w / font_width;
1368                     if (w < 1)
1369                         w = 1;
1370                     h = h / font_height;
1371                     if (h < 1)
1372                         h = 1;
1373
1374                     term_size(h, w, cfg.savelines);
1375                     InvalidateRect(hwnd, NULL, TRUE);
1376                     back->size();
1377                 } else {
1378                     term_size(cfg.height, cfg.width, cfg.savelines);
1379                     InvalidateRect(hwnd, NULL, TRUE);
1380                     if (need_setwpos) {
1381                         SetWindowPos(hwnd, NULL, 0, 0,
1382                                      extra_width + font_width * cfg.width,
1383                                      extra_height +
1384                                      font_height * cfg.height,
1385                                      SWP_NOACTIVATE | SWP_NOCOPYBITS |
1386                                      SWP_NOMOVE | SWP_NOZORDER);
1387                     }
1388                 }
1389                 /* Oops */
1390                 if (cfg.locksize && IsZoomed(hwnd))
1391                     force_normal(hwnd);
1392                 set_title(cfg.wintitle);
1393                 if (IsIconic(hwnd)) {
1394                     SetWindowText(hwnd,
1395                                   cfg.win_name_always ? window_name :
1396                                   icon_name);
1397                 }
1398             }
1399             break;
1400           case IDM_COPYALL:
1401             term_copyall();
1402             break;
1403           case IDM_CLRSB:
1404             term_clrsb();
1405             break;
1406           case IDM_RESET:
1407             term_pwron();
1408             break;
1409           case IDM_TEL_AYT:
1410             back->special(TS_AYT);
1411             break;
1412           case IDM_TEL_BRK:
1413             back->special(TS_BRK);
1414             break;
1415           case IDM_TEL_SYNCH:
1416             back->special(TS_SYNCH);
1417             break;
1418           case IDM_TEL_EC:
1419             back->special(TS_EC);
1420             break;
1421           case IDM_TEL_EL:
1422             back->special(TS_EL);
1423             break;
1424           case IDM_TEL_GA:
1425             back->special(TS_GA);
1426             break;
1427           case IDM_TEL_NOP:
1428             back->special(TS_NOP);
1429             break;
1430           case IDM_TEL_ABORT:
1431             back->special(TS_ABORT);
1432             break;
1433           case IDM_TEL_AO:
1434             back->special(TS_AO);
1435             break;
1436           case IDM_TEL_IP:
1437             back->special(TS_IP);
1438             break;
1439           case IDM_TEL_SUSP:
1440             back->special(TS_SUSP);
1441             break;
1442           case IDM_TEL_EOR:
1443             back->special(TS_EOR);
1444             break;
1445           case IDM_TEL_EOF:
1446             back->special(TS_EOF);
1447             break;
1448           case IDM_ABOUT:
1449             showabout(hwnd);
1450             break;
1451           default:
1452             if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1453                 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1454             }
1455         }
1456         break;
1457
1458 #define X_POS(l) ((int)(short)LOWORD(l))
1459 #define Y_POS(l) ((int)(short)HIWORD(l))
1460
1461 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1462 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1463 #define WHEEL_DELTA 120
1464       case WM_MOUSEWHEEL:
1465         {
1466             wheel_accumulator += (short) HIWORD(wParam);
1467             wParam = LOWORD(wParam);
1468
1469             /* process events when the threshold is reached */
1470             while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1471                 int b;
1472
1473                 /* reduce amount for next time */
1474                 if (wheel_accumulator > 0) {
1475                     b = MBT_WHEEL_UP;
1476                     wheel_accumulator -= WHEEL_DELTA;
1477                 } else if (wheel_accumulator < 0) {
1478                     b = MBT_WHEEL_DOWN;
1479                     wheel_accumulator += WHEEL_DELTA;
1480                 } else
1481                     break;
1482
1483                 if (send_raw_mouse) {
1484                     /* send a mouse-down followed by a mouse up */
1485                     term_mouse(b,
1486                                MA_CLICK,
1487                                TO_CHR_X(X_POS(lParam)),
1488                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1489                                wParam & MK_CONTROL);
1490                     term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1491                                TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1492                                wParam & MK_CONTROL);
1493                 } else {
1494                     /* trigger a scroll */
1495                     term_scroll(0,
1496                                 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1497                 }
1498             }
1499             return 0;
1500         }
1501       case WM_LBUTTONDOWN:
1502       case WM_MBUTTONDOWN:
1503       case WM_RBUTTONDOWN:
1504       case WM_LBUTTONUP:
1505       case WM_MBUTTONUP:
1506       case WM_RBUTTONUP:
1507         {
1508             int button, press;
1509             switch (message) {
1510               case WM_LBUTTONDOWN:
1511                 button = MBT_LEFT;
1512                 press = 1;
1513                 break;
1514               case WM_MBUTTONDOWN:
1515                 button = MBT_MIDDLE;
1516                 press = 1;
1517                 break;
1518               case WM_RBUTTONDOWN:
1519                 button = MBT_RIGHT;
1520                 press = 1;
1521                 break;
1522               case WM_LBUTTONUP:
1523                 button = MBT_LEFT;
1524                 press = 0;
1525                 break;
1526               case WM_MBUTTONUP:
1527                 button = MBT_MIDDLE;
1528                 press = 0;
1529                 break;
1530               case WM_RBUTTONUP:
1531                 button = MBT_RIGHT;
1532                 press = 0;
1533                 break;
1534             }
1535             show_mouseptr(1);
1536             if (press) {
1537                 click(button,
1538                       TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1539                       wParam & MK_SHIFT, wParam & MK_CONTROL);
1540                 SetCapture(hwnd);
1541             } else {
1542                 term_mouse(button, MA_RELEASE,
1543                            TO_CHR_X(X_POS(lParam)),
1544                            TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1545                            wParam & MK_CONTROL);
1546                 ReleaseCapture();
1547             }
1548         }
1549         return 0;
1550       case WM_MOUSEMOVE:
1551         show_mouseptr(1);
1552         /*
1553          * Add the mouse position and message time to the random
1554          * number noise.
1555          */
1556         noise_ultralight(lParam);
1557
1558         if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1559             Mouse_Button b;
1560             if (wParam & MK_LBUTTON)
1561                 b = MBT_SELECT;
1562             else if (wParam & MK_MBUTTON)
1563                 b = cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1564             else
1565                 b = cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1566             term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1567                        TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1568                        wParam & MK_CONTROL);
1569         }
1570         return 0;
1571       case WM_NCMOUSEMOVE:
1572         show_mouseptr(1);
1573         noise_ultralight(lParam);
1574         return 0;
1575       case WM_IGNORE_CLIP:
1576         ignore_clip = wParam;          /* don't panic on DESTROYCLIPBOARD */
1577         break;
1578       case WM_DESTROYCLIPBOARD:
1579         if (!ignore_clip)
1580             term_deselect();
1581         ignore_clip = FALSE;
1582         return 0;
1583       case WM_PAINT:
1584         {
1585             PAINTSTRUCT p;
1586             HideCaret(hwnd);
1587             hdc = BeginPaint(hwnd, &p);
1588             if (pal) {
1589                 SelectPalette(hdc, pal, TRUE);
1590                 RealizePalette(hdc);
1591             }
1592             term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1593                        p.rcPaint.right, p.rcPaint.bottom);
1594             SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1595             SelectObject(hdc, GetStockObject(WHITE_PEN));
1596             EndPaint(hwnd, &p);
1597             ShowCaret(hwnd);
1598         }
1599         return 0;
1600       case WM_NETEVENT:
1601         /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1602          * but the only one that's likely to try to overload us is FD_READ.
1603          * This means buffering just one is fine.
1604          */
1605         if (pending_netevent)
1606             enact_pending_netevent();
1607
1608         pending_netevent = TRUE;
1609         pend_netevent_wParam = wParam;
1610         pend_netevent_lParam = lParam;
1611         time(&last_movement);
1612         return 0;
1613       case WM_SETFOCUS:
1614         has_focus = TRUE;
1615         CreateCaret(hwnd, caretbm, font_width, font_height);
1616         ShowCaret(hwnd);
1617         compose_state = 0;
1618         term_out();
1619         term_update();
1620         break;
1621       case WM_KILLFOCUS:
1622         show_mouseptr(1);
1623         has_focus = FALSE;
1624         DestroyCaret();
1625         term_out();
1626         term_update();
1627         break;
1628       case WM_IGNORE_SIZE:
1629         ignore_size = TRUE;            /* don't panic on next WM_SIZE msg */
1630         break;
1631       case WM_ENTERSIZEMOVE:
1632         EnableSizeTip(1);
1633         resizing = TRUE;
1634         need_backend_resize = FALSE;
1635         break;
1636       case WM_EXITSIZEMOVE:
1637         EnableSizeTip(0);
1638         resizing = FALSE;
1639         if (need_backend_resize)
1640             back->size();
1641         break;
1642       case WM_SIZING:
1643         {
1644             int width, height, w, h, ew, eh;
1645             LPRECT r = (LPRECT) lParam;
1646
1647             width = r->right - r->left - extra_width;
1648             height = r->bottom - r->top - extra_height;
1649             w = (width + font_width / 2) / font_width;
1650             if (w < 1)
1651                 w = 1;
1652             h = (height + font_height / 2) / font_height;
1653             if (h < 1)
1654                 h = 1;
1655             UpdateSizeTip(hwnd, w, h);
1656             ew = width - w * font_width;
1657             eh = height - h * font_height;
1658             if (ew != 0) {
1659                 if (wParam == WMSZ_LEFT ||
1660                     wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1661                     r->left += ew;
1662                 else
1663                     r->right -= ew;
1664             }
1665             if (eh != 0) {
1666                 if (wParam == WMSZ_TOP ||
1667                     wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1668                     r->top += eh;
1669                 else
1670                     r->bottom -= eh;
1671             }
1672             if (ew || eh)
1673                 return 1;
1674             else
1675                 return 0;
1676         }
1677         /* break;  (never reached) */
1678       case WM_SIZE:
1679         if (wParam == SIZE_MINIMIZED) {
1680             SetWindowText(hwnd,
1681                           cfg.win_name_always ? window_name : icon_name);
1682             break;
1683         }
1684         if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1685             SetWindowText(hwnd, window_name);
1686         if (!ignore_size) {
1687             int width, height, w, h;
1688 #if 0                                  /* we have fixed this using WM_SIZING now */
1689             int ew, eh;
1690 #endif
1691
1692             width = LOWORD(lParam);
1693             height = HIWORD(lParam);
1694             w = width / font_width;
1695             if (w < 1)
1696                 w = 1;
1697             h = height / font_height;
1698             if (h < 1)
1699                 h = 1;
1700 #if 0                                  /* we have fixed this using WM_SIZING now */
1701             ew = width - w * font_width;
1702             eh = height - h * font_height;
1703             if (ew != 0 || eh != 0) {
1704                 RECT r;
1705                 GetWindowRect(hwnd, &r);
1706                 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1707                 SetWindowPos(hwnd, NULL, 0, 0,
1708                              r.right - r.left - ew, r.bottom - r.top - eh,
1709                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1710             }
1711 #endif
1712             if (w != cols || h != rows || just_reconfigged) {
1713                 term_invalidate();
1714                 term_size(h, w, cfg.savelines);
1715                 /*
1716                  * Don't call back->size in mid-resize. (To prevent
1717                  * massive numbers of resize events getting sent
1718                  * down the connection during an NT opaque drag.)
1719                  */
1720                 if (!resizing)
1721                     back->size();
1722                 else {
1723                     need_backend_resize = TRUE;
1724                     cfg.height = h;
1725                     cfg.width = w;
1726                 }
1727                 just_reconfigged = FALSE;
1728             }
1729         }
1730         if (wParam == SIZE_RESTORED && defered_resize) {
1731             defered_resize = FALSE;
1732             SetWindowPos(hwnd, NULL, 0, 0,
1733                          extra_width + font_width * cfg.width,
1734                          extra_height + font_height * cfg.height,
1735                          SWP_NOACTIVATE | SWP_NOCOPYBITS |
1736                          SWP_NOMOVE | SWP_NOZORDER);
1737         }
1738         ignore_size = FALSE;
1739         return 0;
1740       case WM_VSCROLL:
1741         switch (LOWORD(wParam)) {
1742           case SB_BOTTOM:
1743             term_scroll(-1, 0);
1744             break;
1745           case SB_TOP:
1746             term_scroll(+1, 0);
1747             break;
1748           case SB_LINEDOWN:
1749             term_scroll(0, +1);
1750             break;
1751           case SB_LINEUP:
1752             term_scroll(0, -1);
1753             break;
1754           case SB_PAGEDOWN:
1755             term_scroll(0, +rows / 2);
1756             break;
1757           case SB_PAGEUP:
1758             term_scroll(0, -rows / 2);
1759             break;
1760           case SB_THUMBPOSITION:
1761           case SB_THUMBTRACK:
1762             term_scroll(1, HIWORD(wParam));
1763             break;
1764         }
1765         break;
1766       case WM_PALETTECHANGED:
1767         if ((HWND) wParam != hwnd && pal != NULL) {
1768             HDC hdc = get_ctx();
1769             if (hdc) {
1770                 if (RealizePalette(hdc) > 0)
1771                     UpdateColors(hdc);
1772                 free_ctx(hdc);
1773             }
1774         }
1775         break;
1776       case WM_QUERYNEWPALETTE:
1777         if (pal != NULL) {
1778             HDC hdc = get_ctx();
1779             if (hdc) {
1780                 if (RealizePalette(hdc) > 0)
1781                     UpdateColors(hdc);
1782                 free_ctx(hdc);
1783                 return TRUE;
1784             }
1785         }
1786         return FALSE;
1787       case WM_KEYDOWN:
1788       case WM_SYSKEYDOWN:
1789       case WM_KEYUP:
1790       case WM_SYSKEYUP:
1791         /*
1792          * Add the scan code and keypress timing to the random
1793          * number noise.
1794          */
1795         noise_ultralight(lParam);
1796
1797         /*
1798          * We don't do TranslateMessage since it disassociates the
1799          * resulting CHAR message from the KEYDOWN that sparked it,
1800          * which we occasionally don't want. Instead, we process
1801          * KEYDOWN, and call the Win32 translator functions so that
1802          * we get the translations under _our_ control.
1803          */
1804         {
1805             unsigned char buf[20];
1806             int len;
1807
1808             if (wParam == VK_PROCESSKEY) {
1809                 MSG m;
1810                 m.hwnd = hwnd;
1811                 m.message = WM_KEYDOWN;
1812                 m.wParam = wParam;
1813                 m.lParam = lParam & 0xdfff;
1814                 TranslateMessage(&m);
1815             } else {
1816                 len = TranslateKey(message, wParam, lParam, buf);
1817                 if (len == -1)
1818                     return DefWindowProc(hwnd, message, wParam, lParam);
1819                 ldisc_send(buf, len);
1820
1821                 if (len > 0)
1822                     show_mouseptr(0);
1823             }
1824         }
1825         return 0;
1826       case WM_IME_CHAR:
1827         {
1828             unsigned char buf[2];
1829
1830             buf[1] = wParam;
1831             buf[0] = wParam >> 8;
1832             ldisc_send(buf, 2);
1833         }
1834       case WM_CHAR:
1835       case WM_SYSCHAR:
1836         /*
1837          * Nevertheless, we are prepared to deal with WM_CHAR
1838          * messages, should they crop up. So if someone wants to
1839          * post the things to us as part of a macro manoeuvre,
1840          * we're ready to cope.
1841          */
1842         {
1843             char c = xlat_kbd2tty((unsigned char) wParam);
1844             ldisc_send(&c, 1);
1845         }
1846         return 0;
1847       case WM_SETCURSOR:
1848         if (send_raw_mouse) {
1849             SetCursor(LoadCursor(NULL, IDC_ARROW));
1850             return TRUE;
1851         }
1852     }
1853
1854     return DefWindowProc(hwnd, message, wParam, lParam);
1855 }
1856
1857 /*
1858  * Move the system caret. (We maintain one, even though it's
1859  * invisible, for the benefit of blind people: apparently some
1860  * helper software tracks the system caret, so we should arrange to
1861  * have one.)
1862  */
1863 void sys_cursor(int x, int y)
1864 {
1865     if (has_focus)
1866         SetCaretPos(x * font_width, y * font_height);
1867 }
1868
1869 /*
1870  * Draw a line of text in the window, at given character
1871  * coordinates, in given attributes.
1872  *
1873  * We are allowed to fiddle with the contents of `text'.
1874  */
1875 void do_text(Context ctx, int x, int y, char *text, int len,
1876              unsigned long attr, int lattr)
1877 {
1878     COLORREF fg, bg, t;
1879     int nfg, nbg, nfont;
1880     HDC hdc = ctx;
1881     RECT line_box;
1882     int force_manual_underline = 0;
1883     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
1884     static int *IpDx = 0, IpDxLEN = 0;;
1885
1886     if (len > IpDxLEN || IpDx[0] != fnt_width) {
1887         int i;
1888         if (len > IpDxLEN) {
1889             sfree(IpDx);
1890             IpDx = smalloc((len + 16) * sizeof(int));
1891             IpDxLEN = (len + 16);
1892         }
1893         for (i = 0; i < IpDxLEN; i++)
1894             IpDx[i] = fnt_width;
1895     }
1896
1897     x *= fnt_width;
1898     y *= font_height;
1899
1900     if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
1901         attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
1902         attr ^= ATTR_CUR_XOR;
1903     }
1904
1905     nfont = 0;
1906     if (cfg.vtmode == VT_OEMONLY)
1907         nfont |= FONT_OEM;
1908
1909     /*
1910      * Map high-half characters in order to approximate ISO using
1911      * OEM character set. No characters are missing if the OEM codepage
1912      * is CP850.
1913      */
1914     if (nfont & FONT_OEM) {
1915         int i;
1916         for (i = 0; i < len; i++)
1917             if (text[i] >= '\xA0' && text[i] <= '\xFF') {
1918 #if 0
1919                 /* This is CP850 ... perfect translation */
1920                 static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5"    /* A0-A7 */
1921                     "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE"  /* A8-AF */
1922                     "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA"  /* B0-B7 */
1923                     "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8"  /* B8-BF */
1924                     "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80"  /* C0-C7 */
1925                     "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8"  /* C8-CF */
1926                     "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E"  /* D0-D7 */
1927                     "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1"  /* D8-DF */
1928                     "\x85\xA0\x83\xC6\x84\x86\x91\x87"  /* E0-E7 */
1929                     "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B"  /* E8-EF */
1930                     "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6"  /* F0-F7 */
1931                     "\x9B\x97\xA3\x96\x81\xEC\xE7\x98"  /* F8-FF */
1932                 ;
1933 #endif
1934                 /* This is CP437 ... junk translation */
1935                 static const unsigned char oemhighhalf[] = {
1936                     0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
1937                     0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
1938                     0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
1939                     0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
1940                     0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
1941                     0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
1942                     0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
1943                     0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
1944                     0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
1945                     0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
1946                     0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
1947                     0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
1948                 };
1949
1950                 text[i] = oemhighhalf[(unsigned char) text[i] - 0xA0];
1951             }
1952     }
1953
1954     if (attr & ATTR_LINEDRW) {
1955         int i;
1956         /* ISO 8859-1 */
1957         static const char poorman[] =
1958             "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
1959
1960         /* CP437 */
1961         static const char oemmap_437[] =
1962             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1963             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
1964
1965         /* CP850 */
1966         static const char oemmap_850[] =
1967             "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1968             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1969
1970         /* Poor windows font ... eg: windows courier */
1971         static const char oemmap[] =
1972             "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
1973             "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
1974
1975         /*
1976          * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
1977          * VT100 line drawing chars; everything else stays normal.
1978          *
1979          * Actually '_' maps to space too, but that's done before.
1980          */
1981         switch (cfg.vtmode) {
1982           case VT_XWINDOWS:
1983             for (i = 0; i < len; i++)
1984                 if (text[i] >= '\x60' && text[i] <= '\x7E')
1985                     text[i] += '\x01' - '\x60';
1986             break;
1987           case VT_OEMANSI:
1988             /* Make sure we actually have an OEM font */
1989             if (fonts[nfont | FONT_OEM]) {
1990           case VT_OEMONLY:
1991                 nfont |= FONT_OEM;
1992                 for (i = 0; i < len; i++)
1993                     if (text[i] >= '\x60' && text[i] <= '\x7E')
1994                         text[i] = oemmap[(unsigned char) text[i] - 0x60];
1995                 break;
1996             }
1997           case VT_POORMAN:
1998             for (i = 0; i < len; i++)
1999                 if (text[i] >= '\x60' && text[i] <= '\x7E')
2000                     text[i] = poorman[(unsigned char) text[i] - 0x60];
2001             break;
2002         }
2003     }
2004
2005     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2006     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2007     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2008         nfont |= FONT_BOLD;
2009     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2010         nfont |= FONT_UNDERLINE;
2011     if (!fonts[nfont]) {
2012         if (nfont & FONT_UNDERLINE)
2013             force_manual_underline = 1;
2014         /* Don't do the same for manual bold, it could be bad news. */
2015
2016         nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2017     }
2018     if (font_needs_hand_underlining && (attr & ATTR_UNDER))
2019         force_manual_underline = 1;
2020     if (attr & ATTR_REVERSE) {
2021         t = nfg;
2022         nfg = nbg;
2023         nbg = t;
2024     }
2025     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2026         nfg++;
2027     if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2028         nbg++;
2029     fg = colours[nfg];
2030     bg = colours[nbg];
2031     SelectObject(hdc, fonts[nfont]);
2032     SetTextColor(hdc, fg);
2033     SetBkColor(hdc, bg);
2034     SetBkMode(hdc, OPAQUE);
2035     line_box.left = x;
2036     line_box.top = y;
2037     line_box.right = x + fnt_width * len;
2038     line_box.bottom = y + font_height;
2039     ExtTextOut(hdc, x, y, ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len,
2040                IpDx);
2041     if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2042         SetBkMode(hdc, TRANSPARENT);
2043
2044         /* GRR: This draws the character outside it's box and can leave
2045          * 'droppings' even with the clip box! I suppose I could loop it
2046          * one character at a time ... yuk. 
2047          * 
2048          * Or ... I could do a test print with "W", and use +1 or -1 for this
2049          * shift depending on if the leftmost column is blank...
2050          */
2051         ExtTextOut(hdc, x - 1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
2052     }
2053     if (force_manual_underline ||
2054         (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
2055         HPEN oldpen;
2056         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2057         MoveToEx(hdc, x, y + descent, NULL);
2058         LineTo(hdc, x + len * fnt_width, y + descent);
2059         oldpen = SelectObject(hdc, oldpen);
2060         DeleteObject(oldpen);
2061     }
2062     if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
2063         POINT pts[5];
2064         HPEN oldpen;
2065         pts[0].x = pts[1].x = pts[4].x = x;
2066         pts[2].x = pts[3].x = x + fnt_width - 1;
2067         pts[0].y = pts[3].y = pts[4].y = y;
2068         pts[1].y = pts[2].y = y + font_height - 1;
2069         oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2070         Polyline(hdc, pts, 5);
2071         oldpen = SelectObject(hdc, oldpen);
2072         DeleteObject(oldpen);
2073     }
2074     if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
2075         int startx, starty, dx, dy, length, i;
2076         if (cfg.cursor_type == 1) {
2077             startx = x;
2078             starty = y + descent;
2079             dx = 1;
2080             dy = 0;
2081             length = fnt_width;
2082         } else {
2083             int xadjust = 0;
2084             if (attr & ATTR_RIGHTCURS)
2085                 xadjust = fnt_width - 1;
2086             startx = x + xadjust;
2087             starty = y;
2088             dx = 0;
2089             dy = 1;
2090             length = font_height;
2091         }
2092         if (attr & ATTR_ACTCURS) {
2093             HPEN oldpen;
2094             oldpen =
2095                 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2096             MoveToEx(hdc, startx, starty, NULL);
2097             LineTo(hdc, startx + dx * length, starty + dy * length);
2098             oldpen = SelectObject(hdc, oldpen);
2099             DeleteObject(oldpen);
2100         } else {
2101             for (i = 0; i < length; i++) {
2102                 if (i % 2 == 0) {
2103                     SetPixel(hdc, startx, starty, colours[23]);
2104                 }
2105                 startx += dx;
2106                 starty += dy;
2107             }
2108         }
2109     }
2110 }
2111
2112 static int check_compose(int first, int second)
2113 {
2114
2115     static char *composetbl[] = {
2116         "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
2117         "C/¢",
2118         "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
2119         "S!§",
2120         "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
2121         "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
2122         ",,¸",
2123         "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
2124         "^AÂ",
2125         "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
2126         "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
2127         "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
2128         "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
2129         ",cç",
2130         "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
2131         "~nñ",
2132         "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
2133         "^uû",
2134         "\"uü", "'yý", "htþ", "\"yÿ",
2135         0
2136     };
2137
2138     char **c;
2139     static int recurse = 0;
2140     int nc = -1;
2141
2142     for (c = composetbl; *c; c++) {
2143         if ((*c)[0] == first && (*c)[1] == second) {
2144             return (*c)[2] & 0xFF;
2145         }
2146     }
2147
2148     if (recurse == 0) {
2149         recurse = 1;
2150         nc = check_compose(second, first);
2151         if (nc == -1)
2152             nc = check_compose(toupper(first), toupper(second));
2153         if (nc == -1)
2154             nc = check_compose(toupper(second), toupper(first));
2155         recurse = 0;
2156     }
2157     return nc;
2158 }
2159
2160
2161 /*
2162  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2163  * codes. Returns number of bytes used or zero to drop the message
2164  * or -1 to forward the message to windows.
2165  */
2166 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2167                         unsigned char *output)
2168 {
2169     BYTE keystate[256];
2170     int scan, left_alt = 0, key_down, shift_state;
2171     int r, i, code;
2172     unsigned char *p = output;
2173     static int alt_state = 0;
2174
2175     HKL kbd_layout = GetKeyboardLayout(0);
2176
2177     static WORD keys[3];
2178     static int compose_char = 0;
2179     static WPARAM compose_key = 0;
2180
2181     r = GetKeyboardState(keystate);
2182     if (!r)
2183         memset(keystate, 0, sizeof(keystate));
2184     else {
2185 #if 0
2186         {                              /* Tell us all about key events */
2187             static BYTE oldstate[256];
2188             static int first = 1;
2189             static int scan;
2190             int ch;
2191             if (first)
2192                 memcpy(oldstate, keystate, sizeof(oldstate));
2193             first = 0;
2194
2195             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2196                 debug(("+"));
2197             } else if ((HIWORD(lParam) & KF_UP)
2198                        && scan == (HIWORD(lParam) & 0xFF)) {
2199                 debug((". U"));
2200             } else {
2201                 debug((".\n"));
2202                 if (wParam >= VK_F1 && wParam <= VK_F20)
2203                     debug(("K_F%d", wParam + 1 - VK_F1));
2204                 else
2205                     switch (wParam) {
2206                       case VK_SHIFT:
2207                         debug(("SHIFT"));
2208                         break;
2209                       case VK_CONTROL:
2210                         debug(("CTRL"));
2211                         break;
2212                       case VK_MENU:
2213                         debug(("ALT"));
2214                         break;
2215                       default:
2216                         debug(("VK_%02x", wParam));
2217                     }
2218                 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2219                     debug(("*"));
2220                 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2221
2222                 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2223                 if (ch >= ' ' && ch <= '~')
2224                     debug((", '%c'", ch));
2225                 else if (ch)
2226                     debug((", $%02x", ch));
2227
2228                 if (keys[0])
2229                     debug((", KB0=%02x", keys[0]));
2230                 if (keys[1])
2231                     debug((", KB1=%02x", keys[1]));
2232                 if (keys[2])
2233                     debug((", KB2=%02x", keys[2]));
2234
2235                 if ((keystate[VK_SHIFT] & 0x80) != 0)
2236                     debug((", S"));
2237                 if ((keystate[VK_CONTROL] & 0x80) != 0)
2238                     debug((", C"));
2239                 if ((HIWORD(lParam) & KF_EXTENDED))
2240                     debug((", E"));
2241                 if ((HIWORD(lParam) & KF_UP))
2242                     debug((", U"));
2243             }
2244
2245             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2246             else if ((HIWORD(lParam) & KF_UP))
2247                 oldstate[wParam & 0xFF] ^= 0x80;
2248             else
2249                 oldstate[wParam & 0xFF] ^= 0x81;
2250
2251             for (ch = 0; ch < 256; ch++)
2252                 if (oldstate[ch] != keystate[ch])
2253                     debug((", M%02x=%02x", ch, keystate[ch]));
2254
2255             memcpy(oldstate, keystate, sizeof(oldstate));
2256         }
2257 #endif
2258
2259         if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2260             keystate[VK_RMENU] = keystate[VK_MENU];
2261         }
2262
2263
2264         /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2265         if ((cfg.funky_type == 3 ||
2266              (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2267             && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2268
2269             wParam = VK_EXECUTE;
2270
2271             /* UnToggle NUMLock */
2272             if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2273                 keystate[VK_NUMLOCK] ^= 1;
2274         }
2275
2276         /* And write back the 'adjusted' state */
2277         SetKeyboardState(keystate);
2278     }
2279
2280     /* Disable Auto repeat if required */
2281     if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2282         return 0;
2283
2284     if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2285         left_alt = 1;
2286
2287     key_down = ((HIWORD(lParam) & KF_UP) == 0);
2288
2289     /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2290     if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2291         if (cfg.ctrlaltkeys)
2292             keystate[VK_MENU] = 0;
2293         else {
2294             keystate[VK_RMENU] = 0x80;
2295             left_alt = 0;
2296         }
2297     }
2298
2299     scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2300     shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2301         + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2302
2303     /* Note if AltGr was pressed and if it was used as a compose key */
2304     if (!compose_state) {
2305         compose_key = 0x100;
2306         if (cfg.compose_key) {
2307             if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2308                 compose_key = wParam;
2309         }
2310         if (wParam == VK_APPS)
2311             compose_key = wParam;
2312     }
2313
2314     if (wParam == compose_key) {
2315         if (compose_state == 0
2316             && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2317                 1;
2318         else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2319             compose_state = 2;
2320         else
2321             compose_state = 0;
2322     } else if (compose_state == 1 && wParam != VK_CONTROL)
2323         compose_state = 0;
2324
2325     /* 
2326      * Record that we pressed key so the scroll window can be reset, but
2327      * be careful to avoid Shift-UP/Down
2328      */
2329     if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2330         seen_key_event = 1;
2331     }
2332
2333     /* Make sure we're not pasting */
2334     if (key_down)
2335         term_nopaste();
2336
2337     if (compose_state > 1 && left_alt)
2338         compose_state = 0;
2339
2340     /* Sanitize the number pad if not using a PC NumPad */
2341     if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2342                      && cfg.funky_type != 2)
2343         || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2344         if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2345             int nParam = 0;
2346             switch (wParam) {
2347               case VK_INSERT:
2348                 nParam = VK_NUMPAD0;
2349                 break;
2350               case VK_END:
2351                 nParam = VK_NUMPAD1;
2352                 break;
2353               case VK_DOWN:
2354                 nParam = VK_NUMPAD2;
2355                 break;
2356               case VK_NEXT:
2357                 nParam = VK_NUMPAD3;
2358                 break;
2359               case VK_LEFT:
2360                 nParam = VK_NUMPAD4;
2361                 break;
2362               case VK_CLEAR:
2363                 nParam = VK_NUMPAD5;
2364                 break;
2365               case VK_RIGHT:
2366                 nParam = VK_NUMPAD6;
2367                 break;
2368               case VK_HOME:
2369                 nParam = VK_NUMPAD7;
2370                 break;
2371               case VK_UP:
2372                 nParam = VK_NUMPAD8;
2373                 break;
2374               case VK_PRIOR:
2375                 nParam = VK_NUMPAD9;
2376                 break;
2377               case VK_DELETE:
2378                 nParam = VK_DECIMAL;
2379                 break;
2380             }
2381             if (nParam) {
2382                 if (keystate[VK_NUMLOCK] & 1)
2383                     shift_state |= 1;
2384                 wParam = nParam;
2385             }
2386         }
2387     }
2388
2389     /* If a key is pressed and AltGr is not active */
2390     if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2391         /* Okay, prepare for most alts then ... */
2392         if (left_alt)
2393             *p++ = '\033';
2394
2395         /* Lets see if it's a pattern we know all about ... */
2396         if (wParam == VK_PRIOR && shift_state == 1) {
2397             SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2398             return 0;
2399         }
2400         if (wParam == VK_NEXT && shift_state == 1) {
2401             SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2402             return 0;
2403         }
2404         if (wParam == VK_INSERT && shift_state == 1) {
2405             term_mouse(MBT_PASTE, MA_CLICK, 0, 0, 0, 0);
2406             term_mouse(MBT_PASTE, MA_RELEASE, 0, 0, 0, 0);
2407             return 0;
2408         }
2409         if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2410             return -1;
2411         }
2412         if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2413             alt_state = 0;
2414             PostMessage(hwnd, WM_CHAR, ' ', 0);
2415             SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2416             return -1;
2417         }
2418         /* Control-Numlock for app-keypad mode switch */
2419         if (wParam == VK_PAUSE && shift_state == 2) {
2420             app_keypad_keys ^= 1;
2421             return 0;
2422         }
2423
2424         /* Nethack keypad */
2425         if (cfg.nethack_keypad && !left_alt) {
2426             switch (wParam) {
2427               case VK_NUMPAD1:
2428                 *p++ = shift_state ? 'B' : 'b';
2429                 return p - output;
2430               case VK_NUMPAD2:
2431                 *p++ = shift_state ? 'J' : 'j';
2432                 return p - output;
2433               case VK_NUMPAD3:
2434                 *p++ = shift_state ? 'N' : 'n';
2435                 return p - output;
2436               case VK_NUMPAD4:
2437                 *p++ = shift_state ? 'H' : 'h';
2438                 return p - output;
2439               case VK_NUMPAD5:
2440                 *p++ = shift_state ? '.' : '.';
2441                 return p - output;
2442               case VK_NUMPAD6:
2443                 *p++ = shift_state ? 'L' : 'l';
2444                 return p - output;
2445               case VK_NUMPAD7:
2446                 *p++ = shift_state ? 'Y' : 'y';
2447                 return p - output;
2448               case VK_NUMPAD8:
2449                 *p++ = shift_state ? 'K' : 'k';
2450                 return p - output;
2451               case VK_NUMPAD9:
2452                 *p++ = shift_state ? 'U' : 'u';
2453                 return p - output;
2454             }
2455         }
2456
2457         /* Application Keypad */
2458         if (!left_alt) {
2459             int xkey = 0;
2460
2461             if (cfg.funky_type == 3 ||
2462                 (cfg.funky_type <= 1 &&
2463                  app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2464                   case VK_EXECUTE:
2465                     xkey = 'P';
2466                     break;
2467                   case VK_DIVIDE:
2468                     xkey = 'Q';
2469                     break;
2470                   case VK_MULTIPLY:
2471                     xkey = 'R';
2472                     break;
2473                   case VK_SUBTRACT:
2474                     xkey = 'S';
2475                     break;
2476                 }
2477             if (app_keypad_keys && !cfg.no_applic_k)
2478                 switch (wParam) {
2479                   case VK_NUMPAD0:
2480                     xkey = 'p';
2481                     break;
2482                   case VK_NUMPAD1:
2483                     xkey = 'q';
2484                     break;
2485                   case VK_NUMPAD2:
2486                     xkey = 'r';
2487                     break;
2488                   case VK_NUMPAD3:
2489                     xkey = 's';
2490                     break;
2491                   case VK_NUMPAD4:
2492                     xkey = 't';
2493                     break;
2494                   case VK_NUMPAD5:
2495                     xkey = 'u';
2496                     break;
2497                   case VK_NUMPAD6:
2498                     xkey = 'v';
2499                     break;
2500                   case VK_NUMPAD7:
2501                     xkey = 'w';
2502                     break;
2503                   case VK_NUMPAD8:
2504                     xkey = 'x';
2505                     break;
2506                   case VK_NUMPAD9:
2507                     xkey = 'y';
2508                     break;
2509
2510                   case VK_DECIMAL:
2511                     xkey = 'n';
2512                     break;
2513                   case VK_ADD:
2514                     if (cfg.funky_type == 2) {
2515                         if (shift_state)
2516                             xkey = 'l';
2517                         else
2518                             xkey = 'k';
2519                     } else if (shift_state)
2520                         xkey = 'm';
2521                     else
2522                         xkey = 'l';
2523                     break;
2524
2525                   case VK_DIVIDE:
2526                     if (cfg.funky_type == 2)
2527                         xkey = 'o';
2528                     break;
2529                   case VK_MULTIPLY:
2530                     if (cfg.funky_type == 2)
2531                         xkey = 'j';
2532                     break;
2533                   case VK_SUBTRACT:
2534                     if (cfg.funky_type == 2)
2535                         xkey = 'm';
2536                     break;
2537
2538                   case VK_RETURN:
2539                     if (HIWORD(lParam) & KF_EXTENDED)
2540                         xkey = 'M';
2541                     break;
2542                 }
2543             if (xkey) {
2544                 if (vt52_mode) {
2545                     if (xkey >= 'P' && xkey <= 'S')
2546                         p += sprintf((char *) p, "\x1B%c", xkey);
2547                     else
2548                         p += sprintf((char *) p, "\x1B?%c", xkey);
2549                 } else
2550                     p += sprintf((char *) p, "\x1BO%c", xkey);
2551                 return p - output;
2552             }
2553         }
2554
2555         if (wParam == VK_BACK && shift_state == 0) {    /* Backspace */
2556             *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2557             *p++ = 0;
2558             return -2;
2559         }
2560         if (wParam == VK_TAB && shift_state == 1) {     /* Shift tab */
2561             *p++ = 0x1B;
2562             *p++ = '[';
2563             *p++ = 'Z';
2564             return p - output;
2565         }
2566         if (wParam == VK_SPACE && shift_state == 2) {   /* Ctrl-Space */
2567             *p++ = 0;
2568             return p - output;
2569         }
2570         if (wParam == VK_SPACE && shift_state == 3) {   /* Ctrl-Shift-Space */
2571             *p++ = 160;
2572             return p - output;
2573         }
2574         if (wParam == VK_CANCEL && shift_state == 2) {  /* Ctrl-Break */
2575             *p++ = 3;
2576             *p++ = 0;
2577             return -2;
2578         }
2579         if (wParam == VK_PAUSE) {      /* Break/Pause */
2580             *p++ = 26;
2581             *p++ = 0;
2582             return -2;
2583         }
2584         /* Control-2 to Control-8 are special */
2585         if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2586             *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2587             return p - output;
2588         }
2589         if (shift_state == 2 && wParam == 0xBD) {
2590             *p++ = 0x1F;
2591             return p - output;
2592         }
2593         if (shift_state == 2 && wParam == 0xDF) {
2594             *p++ = 0x1C;
2595             return p - output;
2596         }
2597         if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2598             *p++ = '\r';
2599             *p++ = '\n';
2600             return p - output;
2601         }
2602
2603         /*
2604          * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2605          * for integer decimal nn.)
2606          *
2607          * We also deal with the weird ones here. Linux VCs replace F1
2608          * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2609          * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2610          * respectively.
2611          */
2612         code = 0;
2613         switch (wParam) {
2614           case VK_F1:
2615             code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2616             break;
2617           case VK_F2:
2618             code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2619             break;
2620           case VK_F3:
2621             code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2622             break;
2623           case VK_F4:
2624             code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2625             break;
2626           case VK_F5:
2627             code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2628             break;
2629           case VK_F6:
2630             code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2631             break;
2632           case VK_F7:
2633             code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2634             break;
2635           case VK_F8:
2636             code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2637             break;
2638           case VK_F9:
2639             code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2640             break;
2641           case VK_F10:
2642             code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2643             break;
2644           case VK_F11:
2645             code = 23;
2646             break;
2647           case VK_F12:
2648             code = 24;
2649             break;
2650           case VK_F13:
2651             code = 25;
2652             break;
2653           case VK_F14:
2654             code = 26;
2655             break;
2656           case VK_F15:
2657             code = 28;
2658             break;
2659           case VK_F16:
2660             code = 29;
2661             break;
2662           case VK_F17:
2663             code = 31;
2664             break;
2665           case VK_F18:
2666             code = 32;
2667             break;
2668           case VK_F19:
2669             code = 33;
2670             break;
2671           case VK_F20:
2672             code = 34;
2673             break;
2674           case VK_HOME:
2675             code = 1;
2676             break;
2677           case VK_INSERT:
2678             code = 2;
2679             break;
2680           case VK_DELETE:
2681             code = 3;
2682             break;
2683           case VK_END:
2684             code = 4;
2685             break;
2686           case VK_PRIOR:
2687             code = 5;
2688             break;
2689           case VK_NEXT:
2690             code = 6;
2691             break;
2692         }
2693         /* Reorder edit keys to physical order */
2694         if (cfg.funky_type == 3 && code <= 6)
2695             code = "\0\2\1\4\5\3\6"[code];
2696
2697         if (vt52_mode && code > 0 && code <= 6) {
2698             p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2699             return p - output;
2700         }
2701
2702         if (cfg.funky_type == 5 && code >= 11 && code <= 24) {
2703             p += sprintf((char *) p, "\x1B[%c", code + 'M' - 11);
2704             return p - output;
2705         }
2706         if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2707             int offt = 0;
2708             if (code > 15)
2709                 offt++;
2710             if (code > 21)
2711                 offt++;
2712             if (vt52_mode)
2713                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2714             else
2715                 p +=
2716                     sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2717             return p - output;
2718         }
2719         if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2720             p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2721             return p - output;
2722         }
2723         if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2724             if (vt52_mode)
2725                 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2726             else
2727                 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2728             return p - output;
2729         }
2730         if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2731             p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2732             return p - output;
2733         }
2734         if (code) {
2735             p += sprintf((char *) p, "\x1B[%d~", code);
2736             return p - output;
2737         }
2738
2739         /*
2740          * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2741          * some reason seems to send VK_CLEAR to Windows...).
2742          */
2743         {
2744             char xkey = 0;
2745             switch (wParam) {
2746               case VK_UP:
2747                 xkey = 'A';
2748                 break;
2749               case VK_DOWN:
2750                 xkey = 'B';
2751                 break;
2752               case VK_RIGHT:
2753                 xkey = 'C';
2754                 break;
2755               case VK_LEFT:
2756                 xkey = 'D';
2757                 break;
2758               case VK_CLEAR:
2759                 xkey = 'G';
2760                 break;
2761             }
2762             if (xkey) {
2763                 if (vt52_mode)
2764                     p += sprintf((char *) p, "\x1B%c", xkey);
2765                 else {
2766                     int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2767                     /* VT100 & VT102 manuals both state the app cursor keys
2768                      * only work if the app keypad is on.
2769                      */
2770                     if (!app_keypad_keys)
2771                         app_flg = 0;
2772                     /* Useful mapping of Ctrl-arrows */
2773                     if (shift_state == 2)
2774                         app_flg = !app_flg;
2775
2776                     if (app_flg)
2777                         p += sprintf((char *) p, "\x1BO%c", xkey);
2778                     else
2779                         p += sprintf((char *) p, "\x1B[%c", xkey);
2780                 }
2781                 return p - output;
2782             }
2783         }
2784
2785         /*
2786          * Finally, deal with Return ourselves. (Win95 seems to
2787          * foul it up when Alt is pressed, for some reason.)
2788          */
2789         if (wParam == VK_RETURN) {     /* Return */
2790             *p++ = 0x0D;
2791             *p++ = 0;
2792             return -2;
2793         }
2794     }
2795
2796     /* Okay we've done everything interesting; let windows deal with 
2797      * the boring stuff */
2798     {
2799         BOOL capsOn = keystate[VK_CAPITAL] != 0;
2800
2801         /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
2802         if (cfg.xlat_capslockcyr)
2803             keystate[VK_CAPITAL] = 0;
2804
2805         r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2806         if (r > 0) {
2807             p = output;
2808             for (i = 0; i < r; i++) {
2809                 unsigned char ch = (unsigned char) keys[i];
2810
2811                 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2812                     compose_char = ch;
2813                     compose_state++;
2814                     continue;
2815                 }
2816                 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2817                     int nc;
2818                     compose_state = 0;
2819
2820                     if ((nc = check_compose(compose_char, ch)) == -1) {
2821                         MessageBeep(MB_ICONHAND);
2822                         return 0;
2823                     }
2824                     *p++ = xlat_kbd2tty((unsigned char) nc);
2825                     return p - output;
2826                 }
2827
2828                 compose_state = 0;
2829
2830                 if (left_alt && key_down)
2831                     *p++ = '\033';
2832                 if (!key_down)
2833                     *p++ = ch;
2834                 else {
2835                     if (capsOn)
2836                         ch = xlat_latkbd2win(ch);
2837                     *p++ = xlat_kbd2tty(ch);
2838                 }
2839             }
2840
2841             /* This is so the ALT-Numpad and dead keys work correctly. */
2842             keys[0] = 0;
2843
2844             return p - output;
2845         }
2846         /* If we're definitly not building up an ALT-54321 then clear it */
2847         if (!left_alt)
2848             keys[0] = 0;
2849     }
2850
2851     /* ALT alone may or may not want to bring up the System menu */
2852     if (wParam == VK_MENU) {
2853         if (cfg.alt_only) {
2854             if (message == WM_SYSKEYDOWN)
2855                 alt_state = 1;
2856             else if (message == WM_SYSKEYUP && alt_state)
2857                 PostMessage(hwnd, WM_CHAR, ' ', 0);
2858             if (message == WM_SYSKEYUP)
2859                 alt_state = 0;
2860         } else
2861             return 0;
2862     } else
2863         alt_state = 0;
2864
2865     return -1;
2866 }
2867
2868 void set_title(char *title)
2869 {
2870     sfree(window_name);
2871     window_name = smalloc(1 + strlen(title));
2872     strcpy(window_name, title);
2873     if (cfg.win_name_always || !IsIconic(hwnd))
2874         SetWindowText(hwnd, title);
2875 }
2876
2877 void set_icon(char *title)
2878 {
2879     sfree(icon_name);
2880     icon_name = smalloc(1 + strlen(title));
2881     strcpy(icon_name, title);
2882     if (!cfg.win_name_always && IsIconic(hwnd))
2883         SetWindowText(hwnd, title);
2884 }
2885
2886 void set_sbar(int total, int start, int page)
2887 {
2888     SCROLLINFO si;
2889
2890     if (!cfg.scrollbar)
2891         return;
2892
2893     si.cbSize = sizeof(si);
2894     si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
2895     si.nMin = 0;
2896     si.nMax = total - 1;
2897     si.nPage = page;
2898     si.nPos = start;
2899     if (hwnd)
2900         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
2901 }
2902
2903 Context get_ctx(void)
2904 {
2905     HDC hdc;
2906     if (hwnd) {
2907         hdc = GetDC(hwnd);
2908         if (hdc && pal)
2909             SelectPalette(hdc, pal, FALSE);
2910         return hdc;
2911     } else
2912         return NULL;
2913 }
2914
2915 void free_ctx(Context ctx)
2916 {
2917     SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
2918     ReleaseDC(hwnd, ctx);
2919 }
2920
2921 static void real_palette_set(int n, int r, int g, int b)
2922 {
2923     if (pal) {
2924         logpal->palPalEntry[n].peRed = r;
2925         logpal->palPalEntry[n].peGreen = g;
2926         logpal->palPalEntry[n].peBlue = b;
2927         logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
2928         colours[n] = PALETTERGB(r, g, b);
2929         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2930     } else
2931         colours[n] = RGB(r, g, b);
2932 }
2933
2934 void palette_set(int n, int r, int g, int b)
2935 {
2936     static const int first[21] = {
2937         0, 2, 4, 6, 8, 10, 12, 14,
2938         1, 3, 5, 7, 9, 11, 13, 15,
2939         16, 17, 18, 20, 22
2940     };
2941     real_palette_set(first[n], r, g, b);
2942     if (first[n] >= 18)
2943         real_palette_set(first[n] + 1, r, g, b);
2944     if (pal) {
2945         HDC hdc = get_ctx();
2946         UnrealizeObject(pal);
2947         RealizePalette(hdc);
2948         free_ctx(hdc);
2949     }
2950 }
2951
2952 void palette_reset(void)
2953 {
2954     int i;
2955
2956     for (i = 0; i < NCOLOURS; i++) {
2957         if (pal) {
2958             logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
2959             logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
2960             logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
2961             logpal->palPalEntry[i].peFlags = 0;
2962             colours[i] = PALETTERGB(defpal[i].rgbtRed,
2963                                     defpal[i].rgbtGreen,
2964                                     defpal[i].rgbtBlue);
2965         } else
2966             colours[i] = RGB(defpal[i].rgbtRed,
2967                              defpal[i].rgbtGreen, defpal[i].rgbtBlue);
2968     }
2969
2970     if (pal) {
2971         HDC hdc;
2972         SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
2973         hdc = get_ctx();
2974         RealizePalette(hdc);
2975         free_ctx(hdc);
2976     }
2977 }
2978
2979 void write_clip(void *data, int len, int must_deselect)
2980 {
2981     HGLOBAL clipdata;
2982     void *lock;
2983
2984     clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
2985     if (!clipdata)
2986         return;
2987     lock = GlobalLock(clipdata);
2988     if (!lock)
2989         return;
2990     memcpy(lock, data, len);
2991     ((unsigned char *) lock)[len] = 0;
2992     GlobalUnlock(clipdata);
2993
2994     if (!must_deselect)
2995         SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
2996
2997     if (OpenClipboard(hwnd)) {
2998         EmptyClipboard();
2999         SetClipboardData(CF_TEXT, clipdata);
3000         CloseClipboard();
3001     } else
3002         GlobalFree(clipdata);
3003
3004     if (!must_deselect)
3005         SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3006 }
3007
3008 void get_clip(void **p, int *len)
3009 {
3010     static HGLOBAL clipdata = NULL;
3011
3012     if (!p) {
3013         if (clipdata)
3014             GlobalUnlock(clipdata);
3015         clipdata = NULL;
3016         return;
3017     } else {
3018         if (OpenClipboard(NULL)) {
3019             clipdata = GetClipboardData(CF_TEXT);
3020             CloseClipboard();
3021             if (clipdata) {
3022                 *p = GlobalLock(clipdata);
3023                 if (*p) {
3024                     *len = strlen(*p);
3025                     return;
3026                 }
3027             }
3028         }
3029     }
3030
3031     *p = NULL;
3032     *len = 0;
3033 }
3034
3035 /*
3036  * Move `lines' lines from position `from' to position `to' in the
3037  * window.
3038  */
3039 void optimised_move(int to, int from, int lines)
3040 {
3041     RECT r;
3042     int min, max;
3043
3044     min = (to < from ? to : from);
3045     max = to + from - min;
3046
3047     r.left = 0;
3048     r.right = cols * font_width;
3049     r.top = min * font_height;
3050     r.bottom = (max + lines) * font_height;
3051     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3052 }
3053
3054 /*
3055  * Print a message box and perform a fatal exit.
3056  */
3057 void fatalbox(char *fmt, ...)
3058 {
3059     va_list ap;
3060     char stuff[200];
3061
3062     va_start(ap, fmt);
3063     vsprintf(stuff, fmt, ap);
3064     va_end(ap);
3065     MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3066     exit(1);
3067 }
3068
3069 /*
3070  * Beep.
3071  */
3072 void beep(int mode)
3073 {
3074     if (mode == BELL_DEFAULT) {
3075         /*
3076          * For MessageBeep style bells, we want to be careful of
3077          * timing, because they don't have the nice property of
3078          * PlaySound bells that each one cancels the previous
3079          * active one. So we limit the rate to one per 50ms or so.
3080          */
3081         static long lastbeep = 0;
3082         long beepdiff;
3083
3084         beepdiff = GetTickCount() - lastbeep;
3085         if (beepdiff >= 0 && beepdiff < 50)
3086             return;
3087         MessageBeep(MB_OK);
3088         /*
3089          * The above MessageBeep call takes time, so we record the
3090          * time _after_ it finishes rather than before it starts.
3091          */
3092         lastbeep = GetTickCount();
3093     } else if (mode == BELL_WAVEFILE) {
3094         if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3095             char buf[sizeof(cfg.bell_wavefile) + 80];
3096             sprintf(buf, "Unable to play sound file\n%s\n"
3097                     "Using default sound instead", cfg.bell_wavefile);
3098             MessageBox(hwnd, buf, "PuTTY Sound Error",
3099                        MB_OK | MB_ICONEXCLAMATION);
3100             cfg.beep = BELL_DEFAULT;
3101         }
3102     }
3103 }