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