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