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