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