Jacob's patch to cause Shift to return to copy-and-paste when xterm
[sgt/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 && !(cfg.mouse_override && shift)) {
1133 lastbtn = MBT_NOTHING;
1134 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1135 return;
1136 }
1137
1138 if (lastbtn == b && thistime - lasttime < dbltime) {
1139 lastact = (lastact == MA_CLICK ? MA_2CLK :
1140 lastact == MA_2CLK ? MA_3CLK :
1141 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1142 } else {
1143 lastbtn = b;
1144 lastact = MA_CLICK;
1145 }
1146 if (lastact != MA_NOTHING)
1147 term_mouse(b, lastact, x, y, shift, ctrl);
1148 lasttime = thistime;
1149 }
1150
1151 /*
1152 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1153 * into a cooked one (SELECT, EXTEND, PASTE).
1154 */
1155 Mouse_Button translate_button(Mouse_Button button)
1156 {
1157 if (button == MBT_LEFT)
1158 return MBT_SELECT;
1159 if (button == MBT_MIDDLE)
1160 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1161 if (button == MBT_RIGHT)
1162 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1163 return 0; /* shouldn't happen */
1164 }
1165
1166 static void show_mouseptr(int show)
1167 {
1168 static int cursor_visible = 1;
1169 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1170 show = 1;
1171 if (cursor_visible && !show)
1172 ShowCursor(FALSE);
1173 else if (!cursor_visible && show)
1174 ShowCursor(TRUE);
1175 cursor_visible = show;
1176 }
1177
1178 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1179 WPARAM wParam, LPARAM lParam)
1180 {
1181 HDC hdc;
1182 static int ignore_size = FALSE;
1183 static int ignore_clip = FALSE;
1184 static int just_reconfigged = FALSE;
1185 static int resizing = FALSE;
1186 static int need_backend_resize = FALSE;
1187 static int defered_resize = FALSE;
1188
1189 switch (message) {
1190 case WM_TIMER:
1191 if (pending_netevent)
1192 enact_pending_netevent();
1193 if (inbuf_head)
1194 term_out();
1195 noise_regular();
1196 HideCaret(hwnd);
1197 term_update();
1198 ShowCaret(hwnd);
1199 if (cfg.ping_interval > 0) {
1200 time_t now;
1201 time(&now);
1202 if (now - last_movement > cfg.ping_interval) {
1203 back->special(TS_PING);
1204 last_movement = now;
1205 }
1206 }
1207 return 0;
1208 case WM_CREATE:
1209 break;
1210 case WM_CLOSE:
1211 show_mouseptr(1);
1212 if (!cfg.warn_on_close || session_closed ||
1213 MessageBox(hwnd,
1214 "Are you sure you want to close this session?",
1215 "PuTTY Exit Confirmation",
1216 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1217 DestroyWindow(hwnd);
1218 return 0;
1219 case WM_DESTROY:
1220 show_mouseptr(1);
1221 PostQuitMessage(0);
1222 return 0;
1223 case WM_SYSCOMMAND:
1224 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1225 case IDM_SHOWLOG:
1226 showeventlog(hwnd);
1227 break;
1228 case IDM_NEWSESS:
1229 case IDM_DUPSESS:
1230 case IDM_SAVEDSESS:
1231 {
1232 char b[2048];
1233 char c[30], *cl;
1234 int freecl = FALSE;
1235 STARTUPINFO si;
1236 PROCESS_INFORMATION pi;
1237 HANDLE filemap = NULL;
1238
1239 if (wParam == IDM_DUPSESS) {
1240 /*
1241 * Allocate a file-mapping memory chunk for the
1242 * config structure.
1243 */
1244 SECURITY_ATTRIBUTES sa;
1245 Config *p;
1246
1247 sa.nLength = sizeof(sa);
1248 sa.lpSecurityDescriptor = NULL;
1249 sa.bInheritHandle = TRUE;
1250 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1251 &sa,
1252 PAGE_READWRITE,
1253 0, sizeof(Config), NULL);
1254 if (filemap) {
1255 p = (Config *) MapViewOfFile(filemap,
1256 FILE_MAP_WRITE,
1257 0, 0, sizeof(Config));
1258 if (p) {
1259 *p = cfg; /* structure copy */
1260 UnmapViewOfFile(p);
1261 }
1262 }
1263 sprintf(c, "putty &%p", filemap);
1264 cl = c;
1265 } else if (wParam == IDM_SAVEDSESS) {
1266 char *session =
1267 sessions[(lParam - IDM_SAVED_MIN) / 16];
1268 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1269 if (!cl)
1270 cl = NULL; /* not a very important failure mode */
1271 else {
1272 sprintf(cl, "putty @%s", session);
1273 freecl = TRUE;
1274 }
1275 } else
1276 cl = NULL;
1277
1278 GetModuleFileName(NULL, b, sizeof(b) - 1);
1279 si.cb = sizeof(si);
1280 si.lpReserved = NULL;
1281 si.lpDesktop = NULL;
1282 si.lpTitle = NULL;
1283 si.dwFlags = 0;
1284 si.cbReserved2 = 0;
1285 si.lpReserved2 = NULL;
1286 CreateProcess(b, cl, NULL, NULL, TRUE,
1287 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1288
1289 if (filemap)
1290 CloseHandle(filemap);
1291 if (freecl)
1292 sfree(cl);
1293 }
1294 break;
1295 case IDM_RECONF:
1296 {
1297 int prev_alwaysontop = cfg.alwaysontop;
1298 int prev_sunken_edge = cfg.sunken_edge;
1299 char oldlogfile[FILENAME_MAX];
1300 int oldlogtype;
1301 int need_setwpos = FALSE;
1302 int old_fwidth, old_fheight;
1303
1304 strcpy(oldlogfile, cfg.logfilename);
1305 oldlogtype = cfg.logtype;
1306 old_fwidth = font_width;
1307 old_fheight = font_height;
1308 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1309
1310 if (!do_reconfig(hwnd))
1311 break;
1312
1313 if (strcmp(oldlogfile, cfg.logfilename) ||
1314 oldlogtype != cfg.logtype) {
1315 logfclose(); /* reset logging */
1316 logfopen();
1317 }
1318
1319 just_reconfigged = TRUE;
1320 deinit_fonts();
1321 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1322 und_mode = UND_FONT;
1323 init_fonts(0);
1324 sfree(logpal);
1325 /*
1326 * Flush the line discipline's edit buffer in the
1327 * case where local editing has just been disabled.
1328 */
1329 ldisc_send(NULL, 0);
1330 if (pal)
1331 DeleteObject(pal);
1332 logpal = NULL;
1333 pal = NULL;
1334 cfgtopalette();
1335 init_palette();
1336
1337 /* Enable or disable the scroll bar, etc */
1338 {
1339 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1340 LONG nexflag, exflag =
1341 GetWindowLong(hwnd, GWL_EXSTYLE);
1342
1343 nexflag = exflag;
1344 if (cfg.alwaysontop != prev_alwaysontop) {
1345 if (cfg.alwaysontop) {
1346 nexflag |= WS_EX_TOPMOST;
1347 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1348 SWP_NOMOVE | SWP_NOSIZE);
1349 } else {
1350 nexflag &= ~(WS_EX_TOPMOST);
1351 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1352 SWP_NOMOVE | SWP_NOSIZE);
1353 }
1354 }
1355 if (cfg.sunken_edge)
1356 nexflag |= WS_EX_CLIENTEDGE;
1357 else
1358 nexflag &= ~(WS_EX_CLIENTEDGE);
1359
1360 nflg = flag;
1361 if (cfg.scrollbar)
1362 nflg |= WS_VSCROLL;
1363 else
1364 nflg &= ~WS_VSCROLL;
1365 if (cfg.locksize)
1366 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1367 else
1368 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1369
1370 if (nflg != flag || nexflag != exflag) {
1371 RECT cr, wr;
1372
1373 if (nflg != flag)
1374 SetWindowLong(hwnd, GWL_STYLE, nflg);
1375 if (nexflag != exflag)
1376 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1377
1378 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1379
1380 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1381 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1382 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1383 | SWP_FRAMECHANGED);
1384
1385 GetWindowRect(hwnd, &wr);
1386 GetClientRect(hwnd, &cr);
1387 extra_width =
1388 wr.right - wr.left - cr.right + cr.left;
1389 extra_height =
1390 wr.bottom - wr.top - cr.bottom + cr.top;
1391 need_setwpos = TRUE;
1392 }
1393 }
1394
1395 if (cfg.height != rows ||
1396 cfg.width != cols ||
1397 old_fwidth != font_width ||
1398 old_fheight != font_height ||
1399 cfg.savelines != savelines ||
1400 cfg.sunken_edge != prev_sunken_edge)
1401 need_setwpos = TRUE;
1402
1403 if (IsZoomed(hwnd)) {
1404 int w, h;
1405 RECT cr;
1406 if (need_setwpos)
1407 defered_resize = TRUE;
1408
1409 GetClientRect(hwnd, &cr);
1410 w = cr.right - cr.left;
1411 h = cr.bottom - cr.top;
1412 w = w / font_width;
1413 if (w < 1)
1414 w = 1;
1415 h = h / font_height;
1416 if (h < 1)
1417 h = 1;
1418
1419 term_size(h, w, cfg.savelines);
1420 InvalidateRect(hwnd, NULL, TRUE);
1421 back->size();
1422 } else {
1423 term_size(cfg.height, cfg.width, cfg.savelines);
1424 InvalidateRect(hwnd, NULL, TRUE);
1425 if (need_setwpos) {
1426 SetWindowPos(hwnd, NULL, 0, 0,
1427 extra_width + font_width * cfg.width,
1428 extra_height +
1429 font_height * cfg.height,
1430 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1431 SWP_NOMOVE | SWP_NOZORDER);
1432 }
1433 }
1434 /* Oops */
1435 if (cfg.locksize && IsZoomed(hwnd))
1436 force_normal(hwnd);
1437 set_title(cfg.wintitle);
1438 if (IsIconic(hwnd)) {
1439 SetWindowText(hwnd,
1440 cfg.win_name_always ? window_name :
1441 icon_name);
1442 }
1443 }
1444 break;
1445 case IDM_COPYALL:
1446 term_copyall();
1447 break;
1448 case IDM_CLRSB:
1449 term_clrsb();
1450 break;
1451 case IDM_RESET:
1452 term_pwron();
1453 break;
1454 case IDM_TEL_AYT:
1455 back->special(TS_AYT);
1456 break;
1457 case IDM_TEL_BRK:
1458 back->special(TS_BRK);
1459 break;
1460 case IDM_TEL_SYNCH:
1461 back->special(TS_SYNCH);
1462 break;
1463 case IDM_TEL_EC:
1464 back->special(TS_EC);
1465 break;
1466 case IDM_TEL_EL:
1467 back->special(TS_EL);
1468 break;
1469 case IDM_TEL_GA:
1470 back->special(TS_GA);
1471 break;
1472 case IDM_TEL_NOP:
1473 back->special(TS_NOP);
1474 break;
1475 case IDM_TEL_ABORT:
1476 back->special(TS_ABORT);
1477 break;
1478 case IDM_TEL_AO:
1479 back->special(TS_AO);
1480 break;
1481 case IDM_TEL_IP:
1482 back->special(TS_IP);
1483 break;
1484 case IDM_TEL_SUSP:
1485 back->special(TS_SUSP);
1486 break;
1487 case IDM_TEL_EOR:
1488 back->special(TS_EOR);
1489 break;
1490 case IDM_TEL_EOF:
1491 back->special(TS_EOF);
1492 break;
1493 case IDM_ABOUT:
1494 showabout(hwnd);
1495 break;
1496 default:
1497 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1498 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1499 }
1500 }
1501 break;
1502
1503 #define X_POS(l) ((int)(short)LOWORD(l))
1504 #define Y_POS(l) ((int)(short)HIWORD(l))
1505
1506 #define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1507 #define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
1508 #define WHEEL_DELTA 120
1509 case WM_MOUSEWHEEL:
1510 {
1511 wheel_accumulator += (short) HIWORD(wParam);
1512 wParam = LOWORD(wParam);
1513
1514 /* process events when the threshold is reached */
1515 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1516 int b;
1517
1518 /* reduce amount for next time */
1519 if (wheel_accumulator > 0) {
1520 b = MBT_WHEEL_UP;
1521 wheel_accumulator -= WHEEL_DELTA;
1522 } else if (wheel_accumulator < 0) {
1523 b = MBT_WHEEL_DOWN;
1524 wheel_accumulator += WHEEL_DELTA;
1525 } else
1526 break;
1527
1528 if (send_raw_mouse) {
1529 /* send a mouse-down followed by a mouse up */
1530 term_mouse(b,
1531 MA_CLICK,
1532 TO_CHR_X(X_POS(lParam)),
1533 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1534 wParam & MK_CONTROL);
1535 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1536 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1537 wParam & MK_CONTROL);
1538 } else {
1539 /* trigger a scroll */
1540 term_scroll(0,
1541 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1542 }
1543 }
1544 return 0;
1545 }
1546 case WM_LBUTTONDOWN:
1547 case WM_MBUTTONDOWN:
1548 case WM_RBUTTONDOWN:
1549 case WM_LBUTTONUP:
1550 case WM_MBUTTONUP:
1551 case WM_RBUTTONUP:
1552 {
1553 int button, press;
1554 switch (message) {
1555 case WM_LBUTTONDOWN:
1556 button = MBT_LEFT;
1557 press = 1;
1558 break;
1559 case WM_MBUTTONDOWN:
1560 button = MBT_MIDDLE;
1561 press = 1;
1562 break;
1563 case WM_RBUTTONDOWN:
1564 button = MBT_RIGHT;
1565 press = 1;
1566 break;
1567 case WM_LBUTTONUP:
1568 button = MBT_LEFT;
1569 press = 0;
1570 break;
1571 case WM_MBUTTONUP:
1572 button = MBT_MIDDLE;
1573 press = 0;
1574 break;
1575 case WM_RBUTTONUP:
1576 button = MBT_RIGHT;
1577 press = 0;
1578 break;
1579 default:
1580 button = press = 0; /* shouldn't happen */
1581 }
1582 show_mouseptr(1);
1583 if (press) {
1584 click(button,
1585 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1586 wParam & MK_SHIFT, wParam & MK_CONTROL);
1587 SetCapture(hwnd);
1588 } else {
1589 term_mouse(button, MA_RELEASE,
1590 TO_CHR_X(X_POS(lParam)),
1591 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1592 wParam & MK_CONTROL);
1593 ReleaseCapture();
1594 }
1595 }
1596 return 0;
1597 case WM_MOUSEMOVE:
1598 show_mouseptr(1);
1599 /*
1600 * Add the mouse position and message time to the random
1601 * number noise.
1602 */
1603 noise_ultralight(lParam);
1604
1605 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1606 Mouse_Button b;
1607 if (wParam & MK_LBUTTON)
1608 b = MBT_LEFT;
1609 else if (wParam & MK_MBUTTON)
1610 b = MBT_MIDDLE;
1611 else
1612 b = MBT_RIGHT;
1613 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1614 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1615 wParam & MK_CONTROL);
1616 }
1617 return 0;
1618 case WM_NCMOUSEMOVE:
1619 show_mouseptr(1);
1620 noise_ultralight(lParam);
1621 return 0;
1622 case WM_IGNORE_CLIP:
1623 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1624 break;
1625 case WM_DESTROYCLIPBOARD:
1626 if (!ignore_clip)
1627 term_deselect();
1628 ignore_clip = FALSE;
1629 return 0;
1630 case WM_PAINT:
1631 {
1632 PAINTSTRUCT p;
1633 HideCaret(hwnd);
1634 hdc = BeginPaint(hwnd, &p);
1635 if (pal) {
1636 SelectPalette(hdc, pal, TRUE);
1637 RealizePalette(hdc);
1638 }
1639 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1640 p.rcPaint.right, p.rcPaint.bottom);
1641 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1642 SelectObject(hdc, GetStockObject(WHITE_PEN));
1643 EndPaint(hwnd, &p);
1644 ShowCaret(hwnd);
1645 }
1646 return 0;
1647 case WM_NETEVENT:
1648 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1649 * but the only one that's likely to try to overload us is FD_READ.
1650 * This means buffering just one is fine.
1651 */
1652 if (pending_netevent)
1653 enact_pending_netevent();
1654
1655 pending_netevent = TRUE;
1656 pend_netevent_wParam = wParam;
1657 pend_netevent_lParam = lParam;
1658 time(&last_movement);
1659 return 0;
1660 case WM_SETFOCUS:
1661 has_focus = TRUE;
1662 CreateCaret(hwnd, caretbm, font_width, font_height);
1663 ShowCaret(hwnd);
1664 flash_window(0); /* stop */
1665 compose_state = 0;
1666 term_out();
1667 term_update();
1668 break;
1669 case WM_KILLFOCUS:
1670 show_mouseptr(1);
1671 has_focus = FALSE;
1672 DestroyCaret();
1673 term_out();
1674 term_update();
1675 break;
1676 case WM_IGNORE_SIZE:
1677 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1678 break;
1679 case WM_ENTERSIZEMOVE:
1680 EnableSizeTip(1);
1681 resizing = TRUE;
1682 need_backend_resize = FALSE;
1683 break;
1684 case WM_EXITSIZEMOVE:
1685 EnableSizeTip(0);
1686 resizing = FALSE;
1687 if (need_backend_resize)
1688 back->size();
1689 break;
1690 case WM_SIZING:
1691 {
1692 int width, height, w, h, ew, eh;
1693 LPRECT r = (LPRECT) lParam;
1694
1695 width = r->right - r->left - extra_width;
1696 height = r->bottom - r->top - extra_height;
1697 w = (width + font_width / 2) / font_width;
1698 if (w < 1)
1699 w = 1;
1700 h = (height + font_height / 2) / font_height;
1701 if (h < 1)
1702 h = 1;
1703 UpdateSizeTip(hwnd, w, h);
1704 ew = width - w * font_width;
1705 eh = height - h * font_height;
1706 if (ew != 0) {
1707 if (wParam == WMSZ_LEFT ||
1708 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1709 r->left += ew;
1710 else
1711 r->right -= ew;
1712 }
1713 if (eh != 0) {
1714 if (wParam == WMSZ_TOP ||
1715 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1716 r->top += eh;
1717 else
1718 r->bottom -= eh;
1719 }
1720 if (ew || eh)
1721 return 1;
1722 else
1723 return 0;
1724 }
1725 /* break; (never reached) */
1726 case WM_SIZE:
1727 if (wParam == SIZE_MINIMIZED) {
1728 SetWindowText(hwnd,
1729 cfg.win_name_always ? window_name : icon_name);
1730 break;
1731 }
1732 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
1733 SetWindowText(hwnd, window_name);
1734 if (!ignore_size) {
1735 int width, height, w, h;
1736 #if 0 /* we have fixed this using WM_SIZING now */
1737 int ew, eh;
1738 #endif
1739
1740 width = LOWORD(lParam);
1741 height = HIWORD(lParam);
1742 w = width / font_width;
1743 if (w < 1)
1744 w = 1;
1745 h = height / font_height;
1746 if (h < 1)
1747 h = 1;
1748 #if 0 /* we have fixed this using WM_SIZING now */
1749 ew = width - w * font_width;
1750 eh = height - h * font_height;
1751 if (ew != 0 || eh != 0) {
1752 RECT r;
1753 GetWindowRect(hwnd, &r);
1754 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1755 SetWindowPos(hwnd, NULL, 0, 0,
1756 r.right - r.left - ew, r.bottom - r.top - eh,
1757 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
1758 }
1759 #endif
1760 if (w != cols || h != rows || just_reconfigged) {
1761 term_invalidate();
1762 term_size(h, w, cfg.savelines);
1763 /*
1764 * Don't call back->size in mid-resize. (To prevent
1765 * massive numbers of resize events getting sent
1766 * down the connection during an NT opaque drag.)
1767 */
1768 if (!resizing)
1769 back->size();
1770 else {
1771 need_backend_resize = TRUE;
1772 cfg.height = h;
1773 cfg.width = w;
1774 }
1775 just_reconfigged = FALSE;
1776 }
1777 }
1778 if (wParam == SIZE_RESTORED && defered_resize) {
1779 defered_resize = FALSE;
1780 SetWindowPos(hwnd, NULL, 0, 0,
1781 extra_width + font_width * cfg.width,
1782 extra_height + font_height * cfg.height,
1783 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1784 SWP_NOMOVE | SWP_NOZORDER);
1785 }
1786 ignore_size = FALSE;
1787 return 0;
1788 case WM_VSCROLL:
1789 switch (LOWORD(wParam)) {
1790 case SB_BOTTOM:
1791 term_scroll(-1, 0);
1792 break;
1793 case SB_TOP:
1794 term_scroll(+1, 0);
1795 break;
1796 case SB_LINEDOWN:
1797 term_scroll(0, +1);
1798 break;
1799 case SB_LINEUP:
1800 term_scroll(0, -1);
1801 break;
1802 case SB_PAGEDOWN:
1803 term_scroll(0, +rows / 2);
1804 break;
1805 case SB_PAGEUP:
1806 term_scroll(0, -rows / 2);
1807 break;
1808 case SB_THUMBPOSITION:
1809 case SB_THUMBTRACK:
1810 term_scroll(1, HIWORD(wParam));
1811 break;
1812 }
1813 break;
1814 case WM_PALETTECHANGED:
1815 if ((HWND) wParam != hwnd && pal != NULL) {
1816 HDC hdc = get_ctx();
1817 if (hdc) {
1818 if (RealizePalette(hdc) > 0)
1819 UpdateColors(hdc);
1820 free_ctx(hdc);
1821 }
1822 }
1823 break;
1824 case WM_QUERYNEWPALETTE:
1825 if (pal != NULL) {
1826 HDC hdc = get_ctx();
1827 if (hdc) {
1828 if (RealizePalette(hdc) > 0)
1829 UpdateColors(hdc);
1830 free_ctx(hdc);
1831 return TRUE;
1832 }
1833 }
1834 return FALSE;
1835 case WM_KEYDOWN:
1836 case WM_SYSKEYDOWN:
1837 case WM_KEYUP:
1838 case WM_SYSKEYUP:
1839 /*
1840 * Add the scan code and keypress timing to the random
1841 * number noise.
1842 */
1843 noise_ultralight(lParam);
1844
1845 /*
1846 * We don't do TranslateMessage since it disassociates the
1847 * resulting CHAR message from the KEYDOWN that sparked it,
1848 * which we occasionally don't want. Instead, we process
1849 * KEYDOWN, and call the Win32 translator functions so that
1850 * we get the translations under _our_ control.
1851 */
1852 {
1853 unsigned char buf[20];
1854 int len;
1855
1856 if (wParam == VK_PROCESSKEY) {
1857 MSG m;
1858 m.hwnd = hwnd;
1859 m.message = WM_KEYDOWN;
1860 m.wParam = wParam;
1861 m.lParam = lParam & 0xdfff;
1862 TranslateMessage(&m);
1863 } else {
1864 len = TranslateKey(message, wParam, lParam, buf);
1865 if (len == -1)
1866 return DefWindowProc(hwnd, message, wParam, lParam);
1867
1868 if (len != 0) {
1869 /*
1870 * We need not bother about stdin backlogs
1871 * here, because in GUI PuTTY we can't do
1872 * anything about it anyway; there's no means
1873 * of asking Windows to hold off on KEYDOWN
1874 * messages. We _have_ to buffer everything
1875 * we're sent.
1876 */
1877 ldisc_send(buf, len);
1878 show_mouseptr(0);
1879 }
1880 }
1881 }
1882 return 0;
1883 case WM_INPUTLANGCHANGE:
1884 {
1885 /* wParam == Font number */
1886 /* lParam == Locale */
1887 char lbuf[20];
1888 HKL NewInputLocale = (HKL) lParam;
1889
1890 // lParam == GetKeyboardLayout(0);
1891
1892 GetLocaleInfo(LOWORD(NewInputLocale),
1893 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1894
1895 kbd_codepage = atoi(lbuf);
1896 }
1897 break;
1898 case WM_IME_COMPOSITION:
1899 {
1900 HIMC hIMC;
1901 int n;
1902 char *buff;
1903
1904 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
1905 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1906
1907 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1908 break; /* fall back to DefWindowProc */
1909
1910 hIMC = ImmGetContext(hwnd);
1911 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1912
1913 if (n > 0) {
1914 buff = (char*) smalloc(n);
1915 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1916 luni_send((unsigned short *)buff, n / 2);
1917 free(buff);
1918 }
1919 ImmReleaseContext(hwnd, hIMC);
1920 return 1;
1921 }
1922
1923 case WM_IME_CHAR:
1924 if (wParam & 0xFF00) {
1925 unsigned char buf[2];
1926
1927 buf[1] = wParam;
1928 buf[0] = wParam >> 8;
1929 lpage_send(kbd_codepage, buf, 2);
1930 } else {
1931 char c = (unsigned char) wParam;
1932 lpage_send(kbd_codepage, &c, 1);
1933 }
1934 return (0);
1935 case WM_CHAR:
1936 case WM_SYSCHAR:
1937 /*
1938 * Nevertheless, we are prepared to deal with WM_CHAR
1939 * messages, should they crop up. So if someone wants to
1940 * post the things to us as part of a macro manoeuvre,
1941 * we're ready to cope.
1942 */
1943 {
1944 char c = (unsigned char)wParam;
1945 lpage_send(CP_ACP, &c, 1);
1946 }
1947 return 0;
1948 case WM_SETCURSOR:
1949 if (send_raw_mouse) {
1950 SetCursor(LoadCursor(NULL, IDC_ARROW));
1951 return TRUE;
1952 }
1953 }
1954
1955 return DefWindowProc(hwnd, message, wParam, lParam);
1956 }
1957
1958 /*
1959 * Move the system caret. (We maintain one, even though it's
1960 * invisible, for the benefit of blind people: apparently some
1961 * helper software tracks the system caret, so we should arrange to
1962 * have one.)
1963 */
1964 void sys_cursor(int x, int y)
1965 {
1966 COMPOSITIONFORM cf;
1967 HIMC hIMC;
1968
1969 if (!has_focus) return;
1970
1971 SetCaretPos(x * font_width, y * font_height);
1972
1973 /* IMM calls on Win98 and beyond only */
1974 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1975
1976 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1977 osVersion.dwMinorVersion == 0) return; /* 95 */
1978
1979 /* we should have the IMM functions */
1980 hIMC = ImmGetContext(hwnd);
1981 cf.dwStyle = CFS_POINT;
1982 cf.ptCurrentPos.x = x * font_width;
1983 cf.ptCurrentPos.y = y * font_height;
1984 ImmSetCompositionWindow(hIMC, &cf);
1985
1986 ImmReleaseContext(hwnd, hIMC);
1987 }
1988
1989 /*
1990 * Draw a line of text in the window, at given character
1991 * coordinates, in given attributes.
1992 *
1993 * We are allowed to fiddle with the contents of `text'.
1994 */
1995 void do_text(Context ctx, int x, int y, char *text, int len,
1996 unsigned long attr, int lattr)
1997 {
1998 COLORREF fg, bg, t;
1999 int nfg, nbg, nfont;
2000 HDC hdc = ctx;
2001 RECT line_box;
2002 int force_manual_underline = 0;
2003 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2004 int char_width = fnt_width;
2005 int text_adjust = 0;
2006 static int *IpDx = 0, IpDxLEN = 0;
2007
2008 if (attr & ATTR_WIDE)
2009 char_width *= 2;
2010
2011 if (len > IpDxLEN || IpDx[0] != char_width) {
2012 int i;
2013 if (len > IpDxLEN) {
2014 sfree(IpDx);
2015 IpDx = smalloc((len + 16) * sizeof(int));
2016 IpDxLEN = (len + 16);
2017 }
2018 for (i = 0; i < IpDxLEN; i++)
2019 IpDx[i] = char_width;
2020 }
2021
2022 x *= fnt_width;
2023 y *= font_height;
2024
2025 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2026 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2027 attr ^= ATTR_CUR_XOR;
2028 }
2029
2030 nfont = 0;
2031 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2032 /* Assume a poorman font is borken in other ways too. */
2033 lattr = LATTR_WIDE;
2034 } else
2035 switch (lattr) {
2036 case LATTR_NORM:
2037 break;
2038 case LATTR_WIDE:
2039 nfont |= FONT_WIDE;
2040 break;
2041 default:
2042 nfont |= FONT_WIDE + FONT_HIGH;
2043 break;
2044 }
2045
2046 /* Special hack for the VT100 linedraw glyphs. */
2047 if ((attr & CSET_MASK) == 0x2300) {
2048 if (!dbcs_screenfont &&
2049 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2050 switch ((unsigned char) (text[0])) {
2051 case 0xBA:
2052 text_adjust = -2 * font_height / 5;
2053 break;
2054 case 0xBB:
2055 text_adjust = -1 * font_height / 5;
2056 break;
2057 case 0xBC:
2058 text_adjust = font_height / 5;
2059 break;
2060 case 0xBD:
2061 text_adjust = 2 * font_height / 5;
2062 break;
2063 }
2064 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2065 text_adjust *= 2;
2066 attr &= ~CSET_MASK;
2067 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2068 attr |= (unitab_xterm['q'] & CSET_MASK);
2069 if (attr & ATTR_UNDER) {
2070 attr &= ~ATTR_UNDER;
2071 force_manual_underline = 1;
2072 }
2073 }
2074 }
2075
2076 /* Anything left as an original character set is unprintable. */
2077 if (DIRECT_CHAR(attr)) {
2078 attr &= ~CSET_MASK;
2079 attr |= 0xFF00;
2080 memset(text, 0xFF, len);
2081 }
2082
2083 /* OEM CP */
2084 if ((attr & CSET_MASK) == ATTR_OEMCP)
2085 nfont |= FONT_OEM;
2086
2087 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2088 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2089 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2090 nfont |= FONT_BOLD;
2091 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2092 nfont |= FONT_UNDERLINE;
2093 another_font(nfont);
2094 if (!fonts[nfont]) {
2095 if (nfont & FONT_UNDERLINE)
2096 force_manual_underline = 1;
2097 /* Don't do the same for manual bold, it could be bad news. */
2098
2099 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2100 }
2101 another_font(nfont);
2102 if (!fonts[nfont])
2103 nfont = FONT_NORMAL;
2104 if (attr & ATTR_REVERSE) {
2105 t = nfg;
2106 nfg = nbg;
2107 nbg = t;
2108 }
2109 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2110 nfg++;
2111 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2112 nbg++;
2113 fg = colours[nfg];
2114 bg = colours[nbg];
2115 SelectObject(hdc, fonts[nfont]);
2116 SetTextColor(hdc, fg);
2117 SetBkColor(hdc, bg);
2118 SetBkMode(hdc, OPAQUE);
2119 line_box.left = x;
2120 line_box.top = y;
2121 line_box.right = x + char_width * len;
2122 line_box.bottom = y + font_height;
2123
2124 /* We're using a private area for direct to font. (512 chars.) */
2125 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2126 /* Ho Hum, dbcs fonts are a PITA! */
2127 /* To display on W9x I have to convert to UCS */
2128 static wchar_t *uni_buf = 0;
2129 static int uni_len = 0;
2130 int nlen;
2131 if (len > uni_len) {
2132 sfree(uni_buf);
2133 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2134 }
2135 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2136 text, len, uni_buf, uni_len);
2137
2138 if (nlen <= 0)
2139 return; /* Eeek! */
2140
2141 ExtTextOutW(hdc, x,
2142 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2143 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2144 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2145 SetBkMode(hdc, TRANSPARENT);
2146 ExtTextOutW(hdc, x - 1,
2147 y - font_height * (lattr ==
2148 LATTR_BOT) + text_adjust,
2149 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2150 }
2151 } else if (DIRECT_FONT(attr)) {
2152 ExtTextOut(hdc, x,
2153 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2154 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2155 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2156 SetBkMode(hdc, TRANSPARENT);
2157
2158 /* GRR: This draws the character outside it's box and can leave
2159 * 'droppings' even with the clip box! I suppose I could loop it
2160 * one character at a time ... yuk.
2161 *
2162 * Or ... I could do a test print with "W", and use +1 or -1 for this
2163 * shift depending on if the leftmost column is blank...
2164 */
2165 ExtTextOut(hdc, x - 1,
2166 y - font_height * (lattr ==
2167 LATTR_BOT) + text_adjust,
2168 ETO_CLIPPED, &line_box, text, len, IpDx);
2169 }
2170 } else {
2171 /* And 'normal' unicode characters */
2172 static WCHAR *wbuf = NULL;
2173 static int wlen = 0;
2174 int i;
2175 if (wlen < len) {
2176 sfree(wbuf);
2177 wlen = len;
2178 wbuf = smalloc(wlen * sizeof(WCHAR));
2179 }
2180 for (i = 0; i < len; i++)
2181 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2182
2183 ExtTextOutW(hdc, x,
2184 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2185 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2186
2187 /* And the shadow bold hack. */
2188 if (bold_mode == BOLD_SHADOW) {
2189 SetBkMode(hdc, TRANSPARENT);
2190 ExtTextOutW(hdc, x - 1,
2191 y - font_height * (lattr ==
2192 LATTR_BOT) + text_adjust,
2193 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2194 }
2195 }
2196 if (lattr != LATTR_TOP && (force_manual_underline ||
2197 (und_mode == UND_LINE
2198 && (attr & ATTR_UNDER)))) {
2199 HPEN oldpen;
2200 int dec = descent;
2201 if (lattr == LATTR_BOT)
2202 dec = dec * 2 - font_height;
2203
2204 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2205 MoveToEx(hdc, x, y + dec, NULL);
2206 LineTo(hdc, x + len * char_width, y + dec);
2207 oldpen = SelectObject(hdc, oldpen);
2208 DeleteObject(oldpen);
2209 }
2210 }
2211
2212 void do_cursor(Context ctx, int x, int y, char *text, int len,
2213 unsigned long attr, int lattr)
2214 {
2215
2216 int fnt_width;
2217 int char_width;
2218 HDC hdc = ctx;
2219 int ctype = cfg.cursor_type;
2220
2221 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2222 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2223 do_text(ctx, x, y, text, len, attr, lattr);
2224 return;
2225 }
2226 ctype = 2;
2227 attr |= TATTR_RIGHTCURS;
2228 }
2229
2230 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2231 if (attr & ATTR_WIDE)
2232 char_width *= 2;
2233 x *= fnt_width;
2234 y *= font_height;
2235
2236 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2237 POINT pts[5];
2238 HPEN oldpen;
2239 pts[0].x = pts[1].x = pts[4].x = x;
2240 pts[2].x = pts[3].x = x + char_width - 1;
2241 pts[0].y = pts[3].y = pts[4].y = y;
2242 pts[1].y = pts[2].y = y + font_height - 1;
2243 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2244 Polyline(hdc, pts, 5);
2245 oldpen = SelectObject(hdc, oldpen);
2246 DeleteObject(oldpen);
2247 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2248 int startx, starty, dx, dy, length, i;
2249 if (ctype == 1) {
2250 startx = x;
2251 starty = y + descent;
2252 dx = 1;
2253 dy = 0;
2254 length = char_width;
2255 } else {
2256 int xadjust = 0;
2257 if (attr & TATTR_RIGHTCURS)
2258 xadjust = char_width - 1;
2259 startx = x + xadjust;
2260 starty = y;
2261 dx = 0;
2262 dy = 1;
2263 length = font_height;
2264 }
2265 if (attr & TATTR_ACTCURS) {
2266 HPEN oldpen;
2267 oldpen =
2268 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2269 MoveToEx(hdc, startx, starty, NULL);
2270 LineTo(hdc, startx + dx * length, starty + dy * length);
2271 oldpen = SelectObject(hdc, oldpen);
2272 DeleteObject(oldpen);
2273 } else {
2274 for (i = 0; i < length; i++) {
2275 if (i % 2 == 0) {
2276 SetPixel(hdc, startx, starty, colours[23]);
2277 }
2278 startx += dx;
2279 starty += dy;
2280 }
2281 }
2282 }
2283 }
2284
2285 /*
2286 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2287 * codes. Returns number of bytes used or zero to drop the message
2288 * or -1 to forward the message to windows.
2289 */
2290 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2291 unsigned char *output)
2292 {
2293 BYTE keystate[256];
2294 int scan, left_alt = 0, key_down, shift_state;
2295 int r, i, code;
2296 unsigned char *p = output;
2297 static int alt_state = 0;
2298 static int alt_sum = 0;
2299
2300 HKL kbd_layout = GetKeyboardLayout(0);
2301
2302 static WORD keys[3];
2303 static int compose_char = 0;
2304 static WPARAM compose_key = 0;
2305
2306 r = GetKeyboardState(keystate);
2307 if (!r)
2308 memset(keystate, 0, sizeof(keystate));
2309 else {
2310 #if 0
2311 #define SHOW_TOASCII_RESULT
2312 { /* Tell us all about key events */
2313 static BYTE oldstate[256];
2314 static int first = 1;
2315 static int scan;
2316 int ch;
2317 if (first)
2318 memcpy(oldstate, keystate, sizeof(oldstate));
2319 first = 0;
2320
2321 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2322 debug(("+"));
2323 } else if ((HIWORD(lParam) & KF_UP)
2324 && scan == (HIWORD(lParam) & 0xFF)) {
2325 debug((". U"));
2326 } else {
2327 debug((".\n"));
2328 if (wParam >= VK_F1 && wParam <= VK_F20)
2329 debug(("K_F%d", wParam + 1 - VK_F1));
2330 else
2331 switch (wParam) {
2332 case VK_SHIFT:
2333 debug(("SHIFT"));
2334 break;
2335 case VK_CONTROL:
2336 debug(("CTRL"));
2337 break;
2338 case VK_MENU:
2339 debug(("ALT"));
2340 break;
2341 default:
2342 debug(("VK_%02x", wParam));
2343 }
2344 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2345 debug(("*"));
2346 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2347
2348 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2349 if (ch >= ' ' && ch <= '~')
2350 debug((", '%c'", ch));
2351 else if (ch)
2352 debug((", $%02x", ch));
2353
2354 if (keys[0])
2355 debug((", KB0=%02x", keys[0]));
2356 if (keys[1])
2357 debug((", KB1=%02x", keys[1]));
2358 if (keys[2])
2359 debug((", KB2=%02x", keys[2]));
2360
2361 if ((keystate[VK_SHIFT] & 0x80) != 0)
2362 debug((", S"));
2363 if ((keystate[VK_CONTROL] & 0x80) != 0)
2364 debug((", C"));
2365 if ((HIWORD(lParam) & KF_EXTENDED))
2366 debug((", E"));
2367 if ((HIWORD(lParam) & KF_UP))
2368 debug((", U"));
2369 }
2370
2371 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2372 else if ((HIWORD(lParam) & KF_UP))
2373 oldstate[wParam & 0xFF] ^= 0x80;
2374 else
2375 oldstate[wParam & 0xFF] ^= 0x81;
2376
2377 for (ch = 0; ch < 256; ch++)
2378 if (oldstate[ch] != keystate[ch])
2379 debug((", M%02x=%02x", ch, keystate[ch]));
2380
2381 memcpy(oldstate, keystate, sizeof(oldstate));
2382 }
2383 #endif
2384
2385 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2386 keystate[VK_RMENU] = keystate[VK_MENU];
2387 }
2388
2389
2390 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2391 if ((cfg.funky_type == 3 ||
2392 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2393 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2394
2395 wParam = VK_EXECUTE;
2396
2397 /* UnToggle NUMLock */
2398 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2399 keystate[VK_NUMLOCK] ^= 1;
2400 }
2401
2402 /* And write back the 'adjusted' state */
2403 SetKeyboardState(keystate);
2404 }
2405
2406 /* Disable Auto repeat if required */
2407 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2408 return 0;
2409
2410 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2411 left_alt = 1;
2412
2413 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2414
2415 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2416 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2417 if (cfg.ctrlaltkeys)
2418 keystate[VK_MENU] = 0;
2419 else {
2420 keystate[VK_RMENU] = 0x80;
2421 left_alt = 0;
2422 }
2423 }
2424
2425 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2426 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2427 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2428
2429 /* Note if AltGr was pressed and if it was used as a compose key */
2430 if (!compose_state) {
2431 compose_key = 0x100;
2432 if (cfg.compose_key) {
2433 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2434 compose_key = wParam;
2435 }
2436 if (wParam == VK_APPS)
2437 compose_key = wParam;
2438 }
2439
2440 if (wParam == compose_key) {
2441 if (compose_state == 0
2442 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2443 1;
2444 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2445 compose_state = 2;
2446 else
2447 compose_state = 0;
2448 } else if (compose_state == 1 && wParam != VK_CONTROL)
2449 compose_state = 0;
2450
2451 /*
2452 * Record that we pressed key so the scroll window can be reset, but
2453 * be careful to avoid Shift-UP/Down
2454 */
2455 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2456 seen_key_event = 1;
2457 }
2458
2459 /* Make sure we're not pasting */
2460 if (key_down)
2461 term_nopaste();
2462
2463 if (compose_state > 1 && left_alt)
2464 compose_state = 0;
2465
2466 /* Sanitize the number pad if not using a PC NumPad */
2467 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2468 && cfg.funky_type != 2)
2469 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2470 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2471 int nParam = 0;
2472 switch (wParam) {
2473 case VK_INSERT:
2474 nParam = VK_NUMPAD0;
2475 break;
2476 case VK_END:
2477 nParam = VK_NUMPAD1;
2478 break;
2479 case VK_DOWN:
2480 nParam = VK_NUMPAD2;
2481 break;
2482 case VK_NEXT:
2483 nParam = VK_NUMPAD3;
2484 break;
2485 case VK_LEFT:
2486 nParam = VK_NUMPAD4;
2487 break;
2488 case VK_CLEAR:
2489 nParam = VK_NUMPAD5;
2490 break;
2491 case VK_RIGHT:
2492 nParam = VK_NUMPAD6;
2493 break;
2494 case VK_HOME:
2495 nParam = VK_NUMPAD7;
2496 break;
2497 case VK_UP:
2498 nParam = VK_NUMPAD8;
2499 break;
2500 case VK_PRIOR:
2501 nParam = VK_NUMPAD9;
2502 break;
2503 case VK_DELETE:
2504 nParam = VK_DECIMAL;
2505 break;
2506 }
2507 if (nParam) {
2508 if (keystate[VK_NUMLOCK] & 1)
2509 shift_state |= 1;
2510 wParam = nParam;
2511 }
2512 }
2513 }
2514
2515 /* If a key is pressed and AltGr is not active */
2516 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2517 /* Okay, prepare for most alts then ... */
2518 if (left_alt)
2519 *p++ = '\033';
2520
2521 /* Lets see if it's a pattern we know all about ... */
2522 if (wParam == VK_PRIOR && shift_state == 1) {
2523 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2524 return 0;
2525 }
2526 if (wParam == VK_NEXT && shift_state == 1) {
2527 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2528 return 0;
2529 }
2530 if (wParam == VK_INSERT && shift_state == 1) {
2531 term_do_paste();
2532 return 0;
2533 }
2534 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2535 return -1;
2536 }
2537 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2538 alt_state = 0;
2539 PostMessage(hwnd, WM_CHAR, ' ', 0);
2540 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2541 return -1;
2542 }
2543 /* Control-Numlock for app-keypad mode switch */
2544 if (wParam == VK_PAUSE && shift_state == 2) {
2545 app_keypad_keys ^= 1;
2546 return 0;
2547 }
2548
2549 /* Nethack keypad */
2550 if (cfg.nethack_keypad && !left_alt) {
2551 switch (wParam) {
2552 case VK_NUMPAD1:
2553 *p++ = shift_state ? 'B' : 'b';
2554 return p - output;
2555 case VK_NUMPAD2:
2556 *p++ = shift_state ? 'J' : 'j';
2557 return p - output;
2558 case VK_NUMPAD3:
2559 *p++ = shift_state ? 'N' : 'n';
2560 return p - output;
2561 case VK_NUMPAD4:
2562 *p++ = shift_state ? 'H' : 'h';
2563 return p - output;
2564 case VK_NUMPAD5:
2565 *p++ = shift_state ? '.' : '.';
2566 return p - output;
2567 case VK_NUMPAD6:
2568 *p++ = shift_state ? 'L' : 'l';
2569 return p - output;
2570 case VK_NUMPAD7:
2571 *p++ = shift_state ? 'Y' : 'y';
2572 return p - output;
2573 case VK_NUMPAD8:
2574 *p++ = shift_state ? 'K' : 'k';
2575 return p - output;
2576 case VK_NUMPAD9:
2577 *p++ = shift_state ? 'U' : 'u';
2578 return p - output;
2579 }
2580 }
2581
2582 /* Application Keypad */
2583 if (!left_alt) {
2584 int xkey = 0;
2585
2586 if (cfg.funky_type == 3 ||
2587 (cfg.funky_type <= 1 &&
2588 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2589 case VK_EXECUTE:
2590 xkey = 'P';
2591 break;
2592 case VK_DIVIDE:
2593 xkey = 'Q';
2594 break;
2595 case VK_MULTIPLY:
2596 xkey = 'R';
2597 break;
2598 case VK_SUBTRACT:
2599 xkey = 'S';
2600 break;
2601 }
2602 if (app_keypad_keys && !cfg.no_applic_k)
2603 switch (wParam) {
2604 case VK_NUMPAD0:
2605 xkey = 'p';
2606 break;
2607 case VK_NUMPAD1:
2608 xkey = 'q';
2609 break;
2610 case VK_NUMPAD2:
2611 xkey = 'r';
2612 break;
2613 case VK_NUMPAD3:
2614 xkey = 's';
2615 break;
2616 case VK_NUMPAD4:
2617 xkey = 't';
2618 break;
2619 case VK_NUMPAD5:
2620 xkey = 'u';
2621 break;
2622 case VK_NUMPAD6:
2623 xkey = 'v';
2624 break;
2625 case VK_NUMPAD7:
2626 xkey = 'w';
2627 break;
2628 case VK_NUMPAD8:
2629 xkey = 'x';
2630 break;
2631 case VK_NUMPAD9:
2632 xkey = 'y';
2633 break;
2634
2635 case VK_DECIMAL:
2636 xkey = 'n';
2637 break;
2638 case VK_ADD:
2639 if (cfg.funky_type == 2) {
2640 if (shift_state)
2641 xkey = 'l';
2642 else
2643 xkey = 'k';
2644 } else if (shift_state)
2645 xkey = 'm';
2646 else
2647 xkey = 'l';
2648 break;
2649
2650 case VK_DIVIDE:
2651 if (cfg.funky_type == 2)
2652 xkey = 'o';
2653 break;
2654 case VK_MULTIPLY:
2655 if (cfg.funky_type == 2)
2656 xkey = 'j';
2657 break;
2658 case VK_SUBTRACT:
2659 if (cfg.funky_type == 2)
2660 xkey = 'm';
2661 break;
2662
2663 case VK_RETURN:
2664 if (HIWORD(lParam) & KF_EXTENDED)
2665 xkey = 'M';
2666 break;
2667 }
2668 if (xkey) {
2669 if (vt52_mode) {
2670 if (xkey >= 'P' && xkey <= 'S')
2671 p += sprintf((char *) p, "\x1B%c", xkey);
2672 else
2673 p += sprintf((char *) p, "\x1B?%c", xkey);
2674 } else
2675 p += sprintf((char *) p, "\x1BO%c", xkey);
2676 return p - output;
2677 }
2678 }
2679
2680 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
2681 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
2682 *p++ = 0;
2683 return -2;
2684 }
2685 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2686 *p++ = 0x1B;
2687 *p++ = '[';
2688 *p++ = 'Z';
2689 return p - output;
2690 }
2691 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2692 *p++ = 0;
2693 return p - output;
2694 }
2695 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2696 *p++ = 160;
2697 return p - output;
2698 }
2699 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2700 *p++ = 3;
2701 *p++ = 0;
2702 return -2;
2703 }
2704 if (wParam == VK_PAUSE) { /* Break/Pause */
2705 *p++ = 26;
2706 *p++ = 0;
2707 return -2;
2708 }
2709 /* Control-2 to Control-8 are special */
2710 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2711 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
2712 return p - output;
2713 }
2714 if (shift_state == 2 && wParam == 0xBD) {
2715 *p++ = 0x1F;
2716 return p - output;
2717 }
2718 if (shift_state == 2 && wParam == 0xDF) {
2719 *p++ = 0x1C;
2720 return p - output;
2721 }
2722 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
2723 *p++ = '\r';
2724 *p++ = '\n';
2725 return p - output;
2726 }
2727
2728 /*
2729 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2730 * for integer decimal nn.)
2731 *
2732 * We also deal with the weird ones here. Linux VCs replace F1
2733 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2734 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2735 * respectively.
2736 */
2737 code = 0;
2738 switch (wParam) {
2739 case VK_F1:
2740 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2741 break;
2742 case VK_F2:
2743 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2744 break;
2745 case VK_F3:
2746 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2747 break;
2748 case VK_F4:
2749 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2750 break;
2751 case VK_F5:
2752 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2753 break;
2754 case VK_F6:
2755 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2756 break;
2757 case VK_F7:
2758 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2759 break;
2760 case VK_F8:
2761 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2762 break;
2763 case VK_F9:
2764 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2765 break;
2766 case VK_F10:
2767 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2768 break;
2769 case VK_F11:
2770 code = 23;
2771 break;
2772 case VK_F12:
2773 code = 24;
2774 break;
2775 case VK_F13:
2776 code = 25;
2777 break;
2778 case VK_F14:
2779 code = 26;
2780 break;
2781 case VK_F15:
2782 code = 28;
2783 break;
2784 case VK_F16:
2785 code = 29;
2786 break;
2787 case VK_F17:
2788 code = 31;
2789 break;
2790 case VK_F18:
2791 code = 32;
2792 break;
2793 case VK_F19:
2794 code = 33;
2795 break;
2796 case VK_F20:
2797 code = 34;
2798 break;
2799 case VK_HOME:
2800 code = 1;
2801 break;
2802 case VK_INSERT:
2803 code = 2;
2804 break;
2805 case VK_DELETE:
2806 code = 3;
2807 break;
2808 case VK_END:
2809 code = 4;
2810 break;
2811 case VK_PRIOR:
2812 code = 5;
2813 break;
2814 case VK_NEXT:
2815 code = 6;
2816 break;
2817 }
2818 /* Reorder edit keys to physical order */
2819 if (cfg.funky_type == 3 && code <= 6)
2820 code = "\0\2\1\4\5\3\6"[code];
2821
2822 if (vt52_mode && code > 0 && code <= 6) {
2823 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
2824 return p - output;
2825 }
2826
2827 if (cfg.funky_type == 5 && /* SCO function keys */
2828 code >= 11 && code <= 34) {
2829 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2830 int index = 0;
2831 switch (wParam) {
2832 case VK_F1: index = 0; break;
2833 case VK_F2: index = 1; break;
2834 case VK_F3: index = 2; break;
2835 case VK_F4: index = 3; break;
2836 case VK_F5: index = 4; break;
2837 case VK_F6: index = 5; break;
2838 case VK_F7: index = 6; break;
2839 case VK_F8: index = 7; break;
2840 case VK_F9: index = 8; break;
2841 case VK_F10: index = 9; break;
2842 case VK_F11: index = 10; break;
2843 case VK_F12: index = 11; break;
2844 }
2845 if (keystate[VK_SHIFT] & 0x80) index += 12;
2846 if (keystate[VK_CONTROL] & 0x80) index += 24;
2847 p += sprintf((char *) p, "\x1B[%c", codes[index]);
2848 return p - output;
2849 }
2850 if (cfg.funky_type == 5 && /* SCO small keypad */
2851 code >= 1 && code <= 6) {
2852 char codes[] = "HL.FIG";
2853 if (code == 3) {
2854 *p++ = '\x7F';
2855 } else {
2856 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2857 }
2858 return p - output;
2859 }
2860 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2861 int offt = 0;
2862 if (code > 15)
2863 offt++;
2864 if (code > 21)
2865 offt++;
2866 if (vt52_mode)
2867 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
2868 else
2869 p +=
2870 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
2871 return p - output;
2872 }
2873 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
2874 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
2875 return p - output;
2876 }
2877 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
2878 if (vt52_mode)
2879 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
2880 else
2881 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
2882 return p - output;
2883 }
2884 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
2885 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
2886 return p - output;
2887 }
2888 if (code) {
2889 p += sprintf((char *) p, "\x1B[%d~", code);
2890 return p - output;
2891 }
2892
2893 /*
2894 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2895 * some reason seems to send VK_CLEAR to Windows...).
2896 */
2897 {
2898 char xkey = 0;
2899 switch (wParam) {
2900 case VK_UP:
2901 xkey = 'A';
2902 break;
2903 case VK_DOWN:
2904 xkey = 'B';
2905 break;
2906 case VK_RIGHT:
2907 xkey = 'C';
2908 break;
2909 case VK_LEFT:
2910 xkey = 'D';
2911 break;
2912 case VK_CLEAR:
2913 xkey = 'G';
2914 break;
2915 }
2916 if (xkey) {
2917 if (vt52_mode)
2918 p += sprintf((char *) p, "\x1B%c", xkey);
2919 else {
2920 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2921 /* VT100 & VT102 manuals both state the app cursor keys
2922 * only work if the app keypad is on.
2923 */
2924 if (!app_keypad_keys)
2925 app_flg = 0;
2926 /* Useful mapping of Ctrl-arrows */
2927 if (shift_state == 2)
2928 app_flg = !app_flg;
2929
2930 if (app_flg)
2931 p += sprintf((char *) p, "\x1BO%c", xkey);
2932 else
2933 p += sprintf((char *) p, "\x1B[%c", xkey);
2934 }
2935 return p - output;
2936 }
2937 }
2938
2939 /*
2940 * Finally, deal with Return ourselves. (Win95 seems to
2941 * foul it up when Alt is pressed, for some reason.)
2942 */
2943 if (wParam == VK_RETURN) { /* Return */
2944 *p++ = 0x0D;
2945 *p++ = 0;
2946 return -2;
2947 }
2948
2949 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2950 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2951 else
2952 alt_sum = 0;
2953 }
2954
2955 /* Okay we've done everything interesting; let windows deal with
2956 * the boring stuff */
2957 {
2958 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
2959 #ifdef SHOW_TOASCII_RESULT
2960 if (r == 1 && !key_down) {
2961 if (alt_sum) {
2962 if (in_utf || dbcs_screenfont)
2963 debug((", (U+%04x)", alt_sum));
2964 else
2965 debug((", LCH(%d)", alt_sum));
2966 } else {
2967 debug((", ACH(%d)", keys[0]));
2968 }
2969 } else if (r > 0) {
2970 int r1;
2971 debug((", ASC("));
2972 for (r1 = 0; r1 < r; r1++) {
2973 debug(("%s%d", r1 ? "," : "", keys[r1]));
2974 }
2975 debug((")"));
2976 }
2977 #endif
2978 if (r > 0) {
2979 WCHAR keybuf;
2980 p = output;
2981 for (i = 0; i < r; i++) {
2982 unsigned char ch = (unsigned char) keys[i];
2983
2984 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
2985 compose_char = ch;
2986 compose_state++;
2987 continue;
2988 }
2989 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
2990 int nc;
2991 compose_state = 0;
2992
2993 if ((nc = check_compose(compose_char, ch)) == -1) {
2994 MessageBeep(MB_ICONHAND);
2995 return 0;
2996 }
2997 keybuf = nc;
2998 luni_send(&keybuf, 1);
2999 continue;
3000 }
3001
3002 compose_state = 0;
3003
3004 if (!key_down) {
3005 if (alt_sum) {
3006 if (in_utf || dbcs_screenfont) {
3007 keybuf = alt_sum;
3008 luni_send(&keybuf, 1);
3009 } else {
3010 ch = (char) alt_sum;
3011 /*
3012 * We need not bother about stdin
3013 * backlogs here, because in GUI PuTTY
3014 * we can't do anything about it
3015 * anyway; there's no means of asking
3016 * Windows to hold off on KEYDOWN
3017 * messages. We _have_ to buffer
3018 * everything we're sent.
3019 */
3020 ldisc_send(&ch, 1);
3021 }
3022 alt_sum = 0;
3023 } else
3024 lpage_send(kbd_codepage, &ch, 1);
3025 } else {
3026 static char cbuf[] = "\033 ";
3027 cbuf[1] = ch;
3028 lpage_send(kbd_codepage, cbuf + !left_alt,
3029 1 + !!left_alt);
3030 }
3031 show_mouseptr(0);
3032 }
3033
3034 /* This is so the ALT-Numpad and dead keys work correctly. */
3035 keys[0] = 0;
3036
3037 return p - output;
3038 }
3039 /* If we're definitly not building up an ALT-54321 then clear it */
3040 if (!left_alt)
3041 keys[0] = 0;
3042 /* If we will be using alt_sum fix the 256s */
3043 else if (keys[0] && (in_utf || dbcs_screenfont))
3044 keys[0] = 10;
3045 }
3046
3047 /* ALT alone may or may not want to bring up the System menu */
3048 if (wParam == VK_MENU) {
3049 if (cfg.alt_only) {
3050 if (message == WM_SYSKEYDOWN)
3051 alt_state = 1;
3052 else if (message == WM_SYSKEYUP && alt_state)
3053 PostMessage(hwnd, WM_CHAR, ' ', 0);
3054 if (message == WM_SYSKEYUP)
3055 alt_state = 0;
3056 } else
3057 return 0;
3058 } else
3059 alt_state = 0;
3060
3061 return -1;
3062 }
3063
3064 void set_title(char *title)
3065 {
3066 sfree(window_name);
3067 window_name = smalloc(1 + strlen(title));
3068 strcpy(window_name, title);
3069 if (cfg.win_name_always || !IsIconic(hwnd))
3070 SetWindowText(hwnd, title);
3071 }
3072
3073 void set_icon(char *title)
3074 {
3075 sfree(icon_name);
3076 icon_name = smalloc(1 + strlen(title));
3077 strcpy(icon_name, title);
3078 if (!cfg.win_name_always && IsIconic(hwnd))
3079 SetWindowText(hwnd, title);
3080 }
3081
3082 void set_sbar(int total, int start, int page)
3083 {
3084 SCROLLINFO si;
3085
3086 if (!cfg.scrollbar)
3087 return;
3088
3089 si.cbSize = sizeof(si);
3090 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3091 si.nMin = 0;
3092 si.nMax = total - 1;
3093 si.nPage = page;
3094 si.nPos = start;
3095 if (hwnd)
3096 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3097 }
3098
3099 Context get_ctx(void)
3100 {
3101 HDC hdc;
3102 if (hwnd) {
3103 hdc = GetDC(hwnd);
3104 if (hdc && pal)
3105 SelectPalette(hdc, pal, FALSE);
3106 return hdc;
3107 } else
3108 return NULL;
3109 }
3110
3111 void free_ctx(Context ctx)
3112 {
3113 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3114 ReleaseDC(hwnd, ctx);
3115 }
3116
3117 static void real_palette_set(int n, int r, int g, int b)
3118 {
3119 if (pal) {
3120 logpal->palPalEntry[n].peRed = r;
3121 logpal->palPalEntry[n].peGreen = g;
3122 logpal->palPalEntry[n].peBlue = b;
3123 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3124 colours[n] = PALETTERGB(r, g, b);
3125 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3126 } else
3127 colours[n] = RGB(r, g, b);
3128 }
3129
3130 void palette_set(int n, int r, int g, int b)
3131 {
3132 static const int first[21] = {
3133 0, 2, 4, 6, 8, 10, 12, 14,
3134 1, 3, 5, 7, 9, 11, 13, 15,
3135 16, 17, 18, 20, 22
3136 };
3137 real_palette_set(first[n], r, g, b);
3138 if (first[n] >= 18)
3139 real_palette_set(first[n] + 1, r, g, b);
3140 if (pal) {
3141 HDC hdc = get_ctx();
3142 UnrealizeObject(pal);
3143 RealizePalette(hdc);
3144 free_ctx(hdc);
3145 }
3146 }
3147
3148 void palette_reset(void)
3149 {
3150 int i;
3151
3152 for (i = 0; i < NCOLOURS; i++) {
3153 if (pal) {
3154 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3155 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3156 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3157 logpal->palPalEntry[i].peFlags = 0;
3158 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3159 defpal[i].rgbtGreen,
3160 defpal[i].rgbtBlue);
3161 } else
3162 colours[i] = RGB(defpal[i].rgbtRed,
3163 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3164 }
3165
3166 if (pal) {
3167 HDC hdc;
3168 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3169 hdc = get_ctx();
3170 RealizePalette(hdc);
3171 free_ctx(hdc);
3172 }
3173 }
3174
3175 void write_aclip(char *data, int len, int must_deselect)
3176 {
3177 HGLOBAL clipdata;
3178 void *lock;
3179
3180 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3181 if (!clipdata)
3182 return;
3183 lock = GlobalLock(clipdata);
3184 if (!lock)
3185 return;
3186 memcpy(lock, data, len);
3187 ((unsigned char *) lock)[len] = 0;
3188 GlobalUnlock(clipdata);
3189
3190 if (!must_deselect)
3191 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3192
3193 if (OpenClipboard(hwnd)) {
3194 EmptyClipboard();
3195 SetClipboardData(CF_TEXT, clipdata);
3196 CloseClipboard();
3197 } else
3198 GlobalFree(clipdata);
3199
3200 if (!must_deselect)
3201 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3202 }
3203
3204 /*
3205 * Note: unlike write_aclip() this will not append a nul.
3206 */
3207 void write_clip(wchar_t * data, int len, int must_deselect)
3208 {
3209 HGLOBAL clipdata;
3210 HGLOBAL clipdata2;
3211 int len2;
3212 void *lock, *lock2;
3213
3214 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3215
3216 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3217 len * sizeof(wchar_t));
3218 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3219
3220 if (!clipdata || !clipdata2) {
3221 if (clipdata)
3222 GlobalFree(clipdata);
3223 if (clipdata2)
3224 GlobalFree(clipdata2);
3225 return;
3226 }
3227 if (!(lock = GlobalLock(clipdata)))
3228 return;
3229 if (!(lock2 = GlobalLock(clipdata2)))
3230 return;
3231
3232 memcpy(lock, data, len * sizeof(wchar_t));
3233 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3234
3235 GlobalUnlock(clipdata);
3236 GlobalUnlock(clipdata2);
3237
3238 if (!must_deselect)
3239 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3240
3241 if (OpenClipboard(hwnd)) {
3242 EmptyClipboard();
3243 SetClipboardData(CF_UNICODETEXT, clipdata);
3244 SetClipboardData(CF_TEXT, clipdata2);
3245 CloseClipboard();
3246 } else {
3247 GlobalFree(clipdata);
3248 GlobalFree(clipdata2);
3249 }
3250
3251 if (!must_deselect)
3252 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3253 }
3254
3255 void get_clip(wchar_t ** p, int *len)
3256 {
3257 static HGLOBAL clipdata = NULL;
3258 static wchar_t *converted = 0;
3259 wchar_t *p2;
3260
3261 if (converted) {
3262 sfree(converted);
3263 converted = 0;
3264 }
3265 if (!p) {
3266 if (clipdata)
3267 GlobalUnlock(clipdata);
3268 clipdata = NULL;
3269 return;
3270 } else if (OpenClipboard(NULL)) {
3271 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3272 CloseClipboard();
3273 *p = GlobalLock(clipdata);
3274 if (*p) {
3275 for (p2 = *p; *p2; p2++);
3276 *len = p2 - *p;
3277 return;
3278 }
3279 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3280 char *s;
3281 int i;
3282 CloseClipboard();
3283 s = GlobalLock(clipdata);
3284 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3285 *p = converted = smalloc(i * sizeof(wchar_t));
3286 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3287 *len = i - 1;
3288 return;
3289 } else
3290 CloseClipboard();
3291 }
3292
3293 *p = NULL;
3294 *len = 0;
3295 }
3296
3297 #if 0
3298 /*
3299 * Move `lines' lines from position `from' to position `to' in the
3300 * window.
3301 */
3302 void optimised_move(int to, int from, int lines)
3303 {
3304 RECT r;
3305 int min, max;
3306
3307 min = (to < from ? to : from);
3308 max = to + from - min;
3309
3310 r.left = 0;
3311 r.right = cols * font_width;
3312 r.top = min * font_height;
3313 r.bottom = (max + lines) * font_height;
3314 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3315 }
3316 #endif
3317
3318 /*
3319 * Print a message box and perform a fatal exit.
3320 */
3321 void fatalbox(char *fmt, ...)
3322 {
3323 va_list ap;
3324 char stuff[200];
3325
3326 va_start(ap, fmt);
3327 vsprintf(stuff, fmt, ap);
3328 va_end(ap);
3329 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3330 exit(1);
3331 }
3332
3333 /*
3334 * Manage window caption / taskbar flashing, if enabled.
3335 * 0 = stop, 1 = maintain, 2 = start
3336 */
3337 static void flash_window(int mode)
3338 {
3339 static long last_flash = 0;
3340 static int flashing = 0;
3341 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3342 /* stop */
3343 if (flashing) {
3344 FlashWindow(hwnd, FALSE);
3345 flashing = 0;
3346 }
3347
3348 } else if (mode == 2) {
3349 /* start */
3350 if (!flashing) {
3351 last_flash = GetTickCount();
3352 flashing = 1;
3353 FlashWindow(hwnd, TRUE);
3354 }
3355
3356 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3357 /* maintain */
3358 if (flashing) {
3359 long now = GetTickCount();
3360 long fdiff = now - last_flash;
3361 if (fdiff < 0 || fdiff > 450) {
3362 last_flash = now;
3363 FlashWindow(hwnd, TRUE); /* toggle */
3364 }
3365 }
3366 }
3367 }
3368
3369 /*
3370 * Beep.
3371 */
3372 void beep(int mode)
3373 {
3374 if (mode == BELL_DEFAULT) {
3375 /*
3376 * For MessageBeep style bells, we want to be careful of
3377 * timing, because they don't have the nice property of
3378 * PlaySound bells that each one cancels the previous
3379 * active one. So we limit the rate to one per 50ms or so.
3380 */
3381 static long lastbeep = 0;
3382 long beepdiff;
3383
3384 beepdiff = GetTickCount() - lastbeep;
3385 if (beepdiff >= 0 && beepdiff < 50)
3386 return;
3387 MessageBeep(MB_OK);
3388 /*
3389 * The above MessageBeep call takes time, so we record the
3390 * time _after_ it finishes rather than before it starts.
3391 */
3392 lastbeep = GetTickCount();
3393 } else if (mode == BELL_WAVEFILE) {
3394 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3395 char buf[sizeof(cfg.bell_wavefile) + 80];
3396 sprintf(buf, "Unable to play sound file\n%s\n"
3397 "Using default sound instead", cfg.bell_wavefile);
3398 MessageBox(hwnd, buf, "PuTTY Sound Error",
3399 MB_OK | MB_ICONEXCLAMATION);
3400 cfg.beep = BELL_DEFAULT;
3401 }
3402 }
3403 /* Otherwise, either visual bell or disabled; do nothing here */
3404 if (!has_focus) {
3405 flash_window(2); /* start */
3406 }
3407 }