Add a couple more FAQs (`where's the Tunnels panel' and `how do I do
[sgt/putty] / window.c
... / ...
CommitLineData
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
71static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
72static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
73 unsigned char *output);
74static void cfgtopalette(void);
75static void init_palette(void);
76static void init_fonts(int, int);
77static void another_font(int);
78static void deinit_fonts(void);
79
80/* Window layout information */
81static void reset_window(int);
82static int full_screen = 0, want_full_screen = 0;
83static int extra_width, extra_height;
84static int font_width, font_height, font_dualwidth;
85static int offset_width, offset_height;
86static int was_zoomed = 0;
87static int was_full_screen = 0;
88static int prev_rows, prev_cols;
89static int pre_fs_rows, pre_fs_cols;
90
91static LONG old_wind_style;
92static WINDOWPLACEMENT old_wind_placement;
93
94static int pending_netevent = 0;
95static WPARAM pend_netevent_wParam = 0;
96static LPARAM pend_netevent_lParam = 0;
97static void enact_pending_netevent(void);
98static void flash_window(int mode);
99static void flip_full_screen(void);
100
101static 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
118static HFONT fonts[FONT_MAXNO];
119static int fontflag[FONT_MAXNO];
120static enum {
121 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
122} bold_mode;
123static enum {
124 UND_LINE, UND_FONT
125} und_mode;
126static int descent;
127
128#define NCOLOURS 24
129static COLORREF colours[NCOLOURS];
130static HPALETTE pal;
131static LPLOGPALETTE logpal;
132static RGBTRIPLE defpal[NCOLOURS];
133
134static HWND hwnd;
135
136static HBITMAP caretbm;
137
138static int dbltime, lasttime, lastact;
139static Mouse_Button lastbtn;
140
141/* this allows xterm-style mouse handling. */
142static int send_raw_mouse = 0;
143static int wheel_accumulator = 0;
144
145static char *window_name, *icon_name;
146
147static int compose_state = 0;
148
149static OSVERSIONINFO osVersion;
150
151/* Dummy routine, only required in plink. */
152void ldisc_update(int echo, int edit)
153{
154}
155
156int 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 */
715char *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 */
741void 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 */
750void 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 */
770static 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 */
803static 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 */
823static 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 */
877static 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
1042static 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
1090static 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
1101void 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
1157static 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
1338static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
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);
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);
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 */
1365Mouse_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
1376static 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
1388static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1389 WPARAM wParam, LPARAM lParam)
1390{
1391 HDC hdc;
1392 static int ignore_clip = FALSE;
1393 static int resizing = FALSE;
1394 static int need_backend_resize = FALSE;
1395
1396 switch (message) {
1397 case WM_TIMER:
1398 if (pending_netevent)
1399 enact_pending_netevent();
1400 term_out();
1401 noise_regular();
1402 HideCaret(hwnd);
1403 term_update();
1404 ShowCaret(hwnd);
1405 if (cfg.ping_interval > 0) {
1406 time_t now;
1407 time(&now);
1408 if (now - last_movement > cfg.ping_interval) {
1409 back->special(TS_PING);
1410 last_movement = now;
1411 }
1412 }
1413 net_pending_errors();
1414 return 0;
1415 case WM_CREATE:
1416 break;
1417 case WM_CLOSE:
1418 show_mouseptr(1);
1419 if (!cfg.warn_on_close || session_closed ||
1420 MessageBox(hwnd,
1421 "Are you sure you want to close this session?",
1422 "PuTTY Exit Confirmation",
1423 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1424 DestroyWindow(hwnd);
1425 return 0;
1426 case WM_DESTROY:
1427 show_mouseptr(1);
1428 PostQuitMessage(0);
1429 return 0;
1430 case WM_SYSCOMMAND:
1431 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1432 case IDM_SHOWLOG:
1433 showeventlog(hwnd);
1434 break;
1435 case IDM_NEWSESS:
1436 case IDM_DUPSESS:
1437 case IDM_SAVEDSESS:
1438 {
1439 char b[2048];
1440 char c[30], *cl;
1441 int freecl = FALSE;
1442 STARTUPINFO si;
1443 PROCESS_INFORMATION pi;
1444 HANDLE filemap = NULL;
1445
1446 if (wParam == IDM_DUPSESS) {
1447 /*
1448 * Allocate a file-mapping memory chunk for the
1449 * config structure.
1450 */
1451 SECURITY_ATTRIBUTES sa;
1452 Config *p;
1453
1454 sa.nLength = sizeof(sa);
1455 sa.lpSecurityDescriptor = NULL;
1456 sa.bInheritHandle = TRUE;
1457 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1458 &sa,
1459 PAGE_READWRITE,
1460 0, sizeof(Config), NULL);
1461 if (filemap) {
1462 p = (Config *) MapViewOfFile(filemap,
1463 FILE_MAP_WRITE,
1464 0, 0, sizeof(Config));
1465 if (p) {
1466 *p = cfg; /* structure copy */
1467 UnmapViewOfFile(p);
1468 }
1469 }
1470 sprintf(c, "putty &%p", filemap);
1471 cl = c;
1472 } else if (wParam == IDM_SAVEDSESS) {
1473 char *session =
1474 sessions[(lParam - IDM_SAVED_MIN) / 16];
1475 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
1476 if (!cl)
1477 cl = NULL; /* not a very important failure mode */
1478 else {
1479 sprintf(cl, "putty @%s", session);
1480 freecl = TRUE;
1481 }
1482 } else
1483 cl = NULL;
1484
1485 GetModuleFileName(NULL, b, sizeof(b) - 1);
1486 si.cb = sizeof(si);
1487 si.lpReserved = NULL;
1488 si.lpDesktop = NULL;
1489 si.lpTitle = NULL;
1490 si.dwFlags = 0;
1491 si.cbReserved2 = 0;
1492 si.lpReserved2 = NULL;
1493 CreateProcess(b, cl, NULL, NULL, TRUE,
1494 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1495
1496 if (filemap)
1497 CloseHandle(filemap);
1498 if (freecl)
1499 sfree(cl);
1500 }
1501 break;
1502 case IDM_RECONF:
1503 {
1504 Config prev_cfg;
1505 int init_lvl = 1;
1506
1507 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1508 prev_cfg = cfg;
1509
1510 if (!do_reconfig(hwnd))
1511 break;
1512
1513 /* If user forcibly disables full-screen, gracefully unzoom */
1514 if (full_screen && !cfg.fullscreenonaltenter) {
1515 flip_full_screen();
1516 }
1517
1518 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1519 prev_cfg.logtype != cfg.logtype) {
1520 logfclose(); /* reset logging */
1521 logfopen();
1522 }
1523
1524 sfree(logpal);
1525 /*
1526 * Flush the line discipline's edit buffer in the
1527 * case where local editing has just been disabled.
1528 */
1529 ldisc_send(NULL, 0, 0);
1530 if (pal)
1531 DeleteObject(pal);
1532 logpal = NULL;
1533 pal = NULL;
1534 cfgtopalette();
1535 init_palette();
1536
1537 /* Screen size changed ? */
1538 if (cfg.height != prev_cfg.height ||
1539 cfg.width != prev_cfg.width ||
1540 cfg.savelines != prev_cfg.savelines ||
1541 cfg.resize_action != RESIZE_TERM)
1542 term_size(cfg.height, cfg.width, cfg.savelines);
1543
1544 /* Enable or disable the scroll bar, etc */
1545 {
1546 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1547 LONG nexflag, exflag =
1548 GetWindowLong(hwnd, GWL_EXSTYLE);
1549
1550 nexflag = exflag;
1551 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1552 if (cfg.alwaysontop) {
1553 nexflag |= WS_EX_TOPMOST;
1554 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1555 SWP_NOMOVE | SWP_NOSIZE);
1556 } else {
1557 nexflag &= ~(WS_EX_TOPMOST);
1558 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1559 SWP_NOMOVE | SWP_NOSIZE);
1560 }
1561 }
1562 if (cfg.sunken_edge)
1563 nexflag |= WS_EX_CLIENTEDGE;
1564 else
1565 nexflag &= ~(WS_EX_CLIENTEDGE);
1566
1567 nflg = flag;
1568 if (cfg.scrollbar)
1569 nflg |= WS_VSCROLL;
1570 else
1571 nflg &= ~WS_VSCROLL;
1572 if (cfg.resize_action == RESIZE_DISABLED)
1573 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1574 else
1575 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1576
1577 if (nflg != flag || nexflag != exflag) {
1578 if (nflg != flag)
1579 SetWindowLong(hwnd, GWL_STYLE, nflg);
1580 if (nexflag != exflag)
1581 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1582
1583 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1584 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1585 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1586 SWP_FRAMECHANGED);
1587
1588 init_lvl = 2;
1589 }
1590 }
1591
1592 /* Oops */
1593 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1594 force_normal(hwnd);
1595 init_lvl = 2;
1596 }
1597
1598 set_title(cfg.wintitle);
1599 if (IsIconic(hwnd)) {
1600 SetWindowText(hwnd,
1601 cfg.win_name_always ? window_name :
1602 icon_name);
1603 }
1604
1605 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1606 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1607 cfg.fontisbold != prev_cfg.fontisbold ||
1608 cfg.fontheight != prev_cfg.fontheight ||
1609 cfg.fontcharset != prev_cfg.fontcharset ||
1610 cfg.vtmode != prev_cfg.vtmode ||
1611 cfg.bold_colour != prev_cfg.bold_colour ||
1612 (cfg.resize_action != RESIZE_FONT &&
1613 prev_cfg.resize_action == RESIZE_FONT))
1614 init_lvl = 2;
1615
1616 InvalidateRect(hwnd, NULL, TRUE);
1617 reset_window(init_lvl);
1618 net_pending_errors();
1619 }
1620 break;
1621 case IDM_COPYALL:
1622 term_copyall();
1623 break;
1624 case IDM_CLRSB:
1625 term_clrsb();
1626 break;
1627 case IDM_RESET:
1628 term_pwron();
1629 break;
1630 case IDM_TEL_AYT:
1631 back->special(TS_AYT);
1632 net_pending_errors();
1633 break;
1634 case IDM_TEL_BRK:
1635 back->special(TS_BRK);
1636 net_pending_errors();
1637 break;
1638 case IDM_TEL_SYNCH:
1639 back->special(TS_SYNCH);
1640 net_pending_errors();
1641 break;
1642 case IDM_TEL_EC:
1643 back->special(TS_EC);
1644 net_pending_errors();
1645 break;
1646 case IDM_TEL_EL:
1647 back->special(TS_EL);
1648 net_pending_errors();
1649 break;
1650 case IDM_TEL_GA:
1651 back->special(TS_GA);
1652 net_pending_errors();
1653 break;
1654 case IDM_TEL_NOP:
1655 back->special(TS_NOP);
1656 net_pending_errors();
1657 break;
1658 case IDM_TEL_ABORT:
1659 back->special(TS_ABORT);
1660 net_pending_errors();
1661 break;
1662 case IDM_TEL_AO:
1663 back->special(TS_AO);
1664 net_pending_errors();
1665 break;
1666 case IDM_TEL_IP:
1667 back->special(TS_IP);
1668 net_pending_errors();
1669 break;
1670 case IDM_TEL_SUSP:
1671 back->special(TS_SUSP);
1672 net_pending_errors();
1673 break;
1674 case IDM_TEL_EOR:
1675 back->special(TS_EOR);
1676 net_pending_errors();
1677 break;
1678 case IDM_TEL_EOF:
1679 back->special(TS_EOF);
1680 net_pending_errors();
1681 break;
1682 case IDM_ABOUT:
1683 showabout(hwnd);
1684 break;
1685 case SC_KEYMENU:
1686 /*
1687 * We get this if the System menu has been activated.
1688 * This might happen from within TranslateKey, in which
1689 * case it really wants to be followed by a `space'
1690 * character to actually _bring the menu up_ rather
1691 * than just sitting there in `ready to appear' state.
1692 */
1693 if( lParam == 0 )
1694 PostMessage(hwnd, WM_CHAR, ' ', 0);
1695 break;
1696 case IDM_FULLSCREEN:
1697 flip_full_screen();
1698 break;
1699 default:
1700 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1701 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1702 }
1703 }
1704 break;
1705
1706#define X_POS(l) ((int)(short)LOWORD(l))
1707#define Y_POS(l) ((int)(short)HIWORD(l))
1708
1709#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1710#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1711#define WHEEL_DELTA 120
1712 case WM_MOUSEWHEEL:
1713 {
1714 wheel_accumulator += (short) HIWORD(wParam);
1715 wParam = LOWORD(wParam);
1716
1717 /* process events when the threshold is reached */
1718 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1719 int b;
1720
1721 /* reduce amount for next time */
1722 if (wheel_accumulator > 0) {
1723 b = MBT_WHEEL_UP;
1724 wheel_accumulator -= WHEEL_DELTA;
1725 } else if (wheel_accumulator < 0) {
1726 b = MBT_WHEEL_DOWN;
1727 wheel_accumulator += WHEEL_DELTA;
1728 } else
1729 break;
1730
1731 if (send_raw_mouse) {
1732 /* send a mouse-down followed by a mouse up */
1733 term_mouse(b,
1734 MA_CLICK,
1735 TO_CHR_X(X_POS(lParam)),
1736 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1737 wParam & MK_CONTROL);
1738 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1739 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1740 wParam & MK_CONTROL);
1741 } else {
1742 /* trigger a scroll */
1743 term_scroll(0,
1744 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
1745 }
1746 }
1747 return 0;
1748 }
1749 case WM_LBUTTONDOWN:
1750 case WM_MBUTTONDOWN:
1751 case WM_RBUTTONDOWN:
1752 case WM_LBUTTONUP:
1753 case WM_MBUTTONUP:
1754 case WM_RBUTTONUP:
1755 {
1756 int button, press;
1757 switch (message) {
1758 case WM_LBUTTONDOWN:
1759 button = MBT_LEFT;
1760 press = 1;
1761 break;
1762 case WM_MBUTTONDOWN:
1763 button = MBT_MIDDLE;
1764 press = 1;
1765 break;
1766 case WM_RBUTTONDOWN:
1767 button = MBT_RIGHT;
1768 press = 1;
1769 break;
1770 case WM_LBUTTONUP:
1771 button = MBT_LEFT;
1772 press = 0;
1773 break;
1774 case WM_MBUTTONUP:
1775 button = MBT_MIDDLE;
1776 press = 0;
1777 break;
1778 case WM_RBUTTONUP:
1779 button = MBT_RIGHT;
1780 press = 0;
1781 break;
1782 default:
1783 button = press = 0; /* shouldn't happen */
1784 }
1785 show_mouseptr(1);
1786 if (press) {
1787 click(button,
1788 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1789 wParam & MK_SHIFT, wParam & MK_CONTROL);
1790 SetCapture(hwnd);
1791 } else {
1792 term_mouse(button, MA_RELEASE,
1793 TO_CHR_X(X_POS(lParam)),
1794 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1795 wParam & MK_CONTROL);
1796 ReleaseCapture();
1797 }
1798 }
1799 return 0;
1800 case WM_MOUSEMOVE:
1801 show_mouseptr(1);
1802 /*
1803 * Add the mouse position and message time to the random
1804 * number noise.
1805 */
1806 noise_ultralight(lParam);
1807
1808 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1809 Mouse_Button b;
1810 if (wParam & MK_LBUTTON)
1811 b = MBT_LEFT;
1812 else if (wParam & MK_MBUTTON)
1813 b = MBT_MIDDLE;
1814 else
1815 b = MBT_RIGHT;
1816 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1817 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1818 wParam & MK_CONTROL);
1819 }
1820 return 0;
1821 case WM_NCMOUSEMOVE:
1822 show_mouseptr(1);
1823 noise_ultralight(lParam);
1824 return 0;
1825 case WM_IGNORE_CLIP:
1826 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1827 break;
1828 case WM_DESTROYCLIPBOARD:
1829 if (!ignore_clip)
1830 term_deselect();
1831 ignore_clip = FALSE;
1832 return 0;
1833 case WM_PAINT:
1834 {
1835 PAINTSTRUCT p;
1836 HideCaret(hwnd);
1837 hdc = BeginPaint(hwnd, &p);
1838 if (pal) {
1839 SelectPalette(hdc, pal, TRUE);
1840 RealizePalette(hdc);
1841 }
1842 term_paint(hdc,
1843 (p.rcPaint.left-offset_width)/font_width,
1844 (p.rcPaint.top-offset_height)/font_height,
1845 (p.rcPaint.right-offset_width-1)/font_width,
1846 (p.rcPaint.bottom-offset_height-1)/font_height);
1847
1848 if (p.fErase ||
1849 p.rcPaint.left < offset_width ||
1850 p.rcPaint.top < offset_height ||
1851 p.rcPaint.right >= offset_width + font_width*cols ||
1852 p.rcPaint.bottom>= offset_height + font_height*rows)
1853 {
1854 HBRUSH fillcolour, oldbrush;
1855 HPEN edge, oldpen;
1856 fillcolour = CreateSolidBrush (
1857 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1858 oldbrush = SelectObject(hdc, fillcolour);
1859 edge = CreatePen(PS_SOLID, 0,
1860 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1861 oldpen = SelectObject(hdc, edge);
1862
1863 ExcludeClipRect(hdc,
1864 offset_width, offset_height,
1865 offset_width+font_width*cols,
1866 offset_height+font_height*rows);
1867
1868 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1869 p.rcPaint.right, p.rcPaint.bottom);
1870
1871 // SelectClipRgn(hdc, NULL);
1872
1873 SelectObject(hdc, oldbrush);
1874 DeleteObject(fillcolour);
1875 SelectObject(hdc, oldpen);
1876 DeleteObject(edge);
1877 }
1878 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1879 SelectObject(hdc, GetStockObject(WHITE_PEN));
1880 EndPaint(hwnd, &p);
1881 ShowCaret(hwnd);
1882 }
1883 return 0;
1884 case WM_NETEVENT:
1885 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1886 * but the only one that's likely to try to overload us is FD_READ.
1887 * This means buffering just one is fine.
1888 */
1889 if (pending_netevent)
1890 enact_pending_netevent();
1891
1892 pending_netevent = TRUE;
1893 pend_netevent_wParam = wParam;
1894 pend_netevent_lParam = lParam;
1895 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1896 enact_pending_netevent();
1897
1898 time(&last_movement);
1899 return 0;
1900 case WM_SETFOCUS:
1901 has_focus = TRUE;
1902 CreateCaret(hwnd, caretbm, font_width, font_height);
1903 ShowCaret(hwnd);
1904 flash_window(0); /* stop */
1905 compose_state = 0;
1906 term_out();
1907 term_update();
1908 break;
1909 case WM_KILLFOCUS:
1910 show_mouseptr(1);
1911 has_focus = FALSE;
1912 DestroyCaret();
1913 term_out();
1914 term_update();
1915 break;
1916 case WM_ENTERSIZEMOVE:
1917#ifdef RDB_DEBUG_PATCH
1918 debug((27, "WM_ENTERSIZEMOVE"));
1919#endif
1920 EnableSizeTip(1);
1921 resizing = TRUE;
1922 need_backend_resize = FALSE;
1923 break;
1924 case WM_EXITSIZEMOVE:
1925 EnableSizeTip(0);
1926 resizing = FALSE;
1927#ifdef RDB_DEBUG_PATCH
1928 debug((27, "WM_EXITSIZEMOVE"));
1929#endif
1930 if (need_backend_resize) {
1931 term_size(cfg.height, cfg.width, cfg.savelines);
1932 InvalidateRect(hwnd, NULL, TRUE);
1933 }
1934 break;
1935 case WM_SIZING:
1936 /*
1937 * This does two jobs:
1938 * 1) Keep the sizetip uptodate
1939 * 2) Make sure the window size is _stepped_ in units of the font size.
1940 */
1941 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
1942 int width, height, w, h, ew, eh;
1943 LPRECT r = (LPRECT) lParam;
1944
1945 if ( !need_backend_resize &&
1946 (cfg.height != rows || cfg.width != cols )) {
1947 /*
1948 * Great! It seems the host has been changing the terminal
1949 * size, well the user is now grabbing so this is probably
1950 * the least confusing solution in the long run even though
1951 * it a is suprise. Unfortunatly the only way to prevent
1952 * this seems to be to let the host change the window size
1953 * and as that's a user option we're still right back here.
1954 */
1955 term_size(cfg.height, cfg.width, cfg.savelines);
1956 reset_window(2);
1957 InvalidateRect(hwnd, NULL, TRUE);
1958 need_backend_resize = TRUE;
1959 }
1960
1961 width = r->right - r->left - extra_width;
1962 height = r->bottom - r->top - extra_height;
1963 w = (width + font_width / 2) / font_width;
1964 if (w < 1)
1965 w = 1;
1966 h = (height + font_height / 2) / font_height;
1967 if (h < 1)
1968 h = 1;
1969 UpdateSizeTip(hwnd, w, h);
1970 ew = width - w * font_width;
1971 eh = height - h * font_height;
1972 if (ew != 0) {
1973 if (wParam == WMSZ_LEFT ||
1974 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
1975 r->left += ew;
1976 else
1977 r->right -= ew;
1978 }
1979 if (eh != 0) {
1980 if (wParam == WMSZ_TOP ||
1981 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
1982 r->top += eh;
1983 else
1984 r->bottom -= eh;
1985 }
1986 if (ew || eh)
1987 return 1;
1988 else
1989 return 0;
1990 } else {
1991 int width, height, w, h, rv = 0;
1992 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1993 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1994 LPRECT r = (LPRECT) lParam;
1995
1996 width = r->right - r->left - ex_width;
1997 height = r->bottom - r->top - ex_height;
1998
1999 w = (width + cols/2)/cols;
2000 h = (height + rows/2)/rows;
2001 if ( r->right != r->left + w*cols + ex_width)
2002 rv = 1;
2003
2004 if (wParam == WMSZ_LEFT ||
2005 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2006 r->left = r->right - w*cols - ex_width;
2007 else
2008 r->right = r->left + w*cols + ex_width;
2009
2010 if (r->bottom != r->top + h*rows + ex_height)
2011 rv = 1;
2012
2013 if (wParam == WMSZ_TOP ||
2014 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2015 r->top = r->bottom - h*rows - ex_height;
2016 else
2017 r->bottom = r->top + h*rows + ex_height;
2018
2019 return rv;
2020 }
2021 /* break; (never reached) */
2022 case WM_SIZE:
2023#ifdef RDB_DEBUG_PATCH
2024 debug((27, "WM_SIZE %s (%d,%d)",
2025 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2026 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2027 (wParam == SIZE_RESTORED && resizing) ? "to":
2028 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2029 "...",
2030 LOWORD(lParam), HIWORD(lParam)));
2031#endif
2032 if (wParam == SIZE_MINIMIZED) {
2033 SetWindowText(hwnd,
2034 cfg.win_name_always ? window_name : icon_name);
2035 break;
2036 }
2037 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2038 SetWindowText(hwnd, window_name);
2039
2040 if (cfg.resize_action == RESIZE_DISABLED) {
2041 /* A resize, well it better be a minimize. */
2042 reset_window(-1);
2043 } else {
2044
2045 int width, height, w, h;
2046
2047 width = LOWORD(lParam);
2048 height = HIWORD(lParam);
2049
2050 if (!resizing) {
2051 if (wParam == SIZE_MAXIMIZED) {
2052 was_zoomed = 1;
2053 prev_rows = rows;
2054 prev_cols = cols;
2055 if (cfg.resize_action != RESIZE_FONT) {
2056 w = width / font_width;
2057 if (w < 1) w = 1;
2058 h = height / font_height;
2059 if (h < 1) h = 1;
2060
2061 term_size(h, w, cfg.savelines);
2062 }
2063 reset_window(0);
2064 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2065 was_zoomed = 0;
2066 if (cfg.resize_action != RESIZE_FONT)
2067 term_size(prev_rows, prev_cols, cfg.savelines);
2068 reset_window(0);
2069 } else if (was_full_screen) {
2070 was_full_screen = 0;
2071 if (cfg.resize_action != RESIZE_FONT)
2072 term_size(pre_fs_rows, pre_fs_cols, cfg.savelines);
2073 reset_window(0);
2074 }
2075 /* This is an unexpected resize, these will normally happen
2076 * if the window is too large. Probably either the user
2077 * selected a huge font or the screen size has changed.
2078 *
2079 * This is also called with minimize.
2080 */
2081 else reset_window(-1);
2082 }
2083
2084 /*
2085 * Don't call back->size in mid-resize. (To prevent
2086 * massive numbers of resize events getting sent
2087 * down the connection during an NT opaque drag.)
2088 */
2089 if (resizing) {
2090 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
2091 need_backend_resize = TRUE;
2092 w = (width-cfg.window_border*2) / font_width;
2093 if (w < 1) w = 1;
2094 h = (height-cfg.window_border*2) / font_height;
2095 if (h < 1) h = 1;
2096
2097 cfg.height = h;
2098 cfg.width = w;
2099 } else
2100 reset_window(0);
2101 }
2102 }
2103 return 0;
2104 case WM_VSCROLL:
2105 switch (LOWORD(wParam)) {
2106 case SB_BOTTOM:
2107 term_scroll(-1, 0);
2108 break;
2109 case SB_TOP:
2110 term_scroll(+1, 0);
2111 break;
2112 case SB_LINEDOWN:
2113 term_scroll(0, +1);
2114 break;
2115 case SB_LINEUP:
2116 term_scroll(0, -1);
2117 break;
2118 case SB_PAGEDOWN:
2119 term_scroll(0, +rows / 2);
2120 break;
2121 case SB_PAGEUP:
2122 term_scroll(0, -rows / 2);
2123 break;
2124 case SB_THUMBPOSITION:
2125 case SB_THUMBTRACK:
2126 term_scroll(1, HIWORD(wParam));
2127 break;
2128 }
2129 break;
2130 case WM_PALETTECHANGED:
2131 if ((HWND) wParam != hwnd && pal != NULL) {
2132 HDC hdc = get_ctx();
2133 if (hdc) {
2134 if (RealizePalette(hdc) > 0)
2135 UpdateColors(hdc);
2136 free_ctx(hdc);
2137 }
2138 }
2139 break;
2140 case WM_QUERYNEWPALETTE:
2141 if (pal != NULL) {
2142 HDC hdc = get_ctx();
2143 if (hdc) {
2144 if (RealizePalette(hdc) > 0)
2145 UpdateColors(hdc);
2146 free_ctx(hdc);
2147 return TRUE;
2148 }
2149 }
2150 return FALSE;
2151 case WM_KEYDOWN:
2152 case WM_SYSKEYDOWN:
2153 case WM_KEYUP:
2154 case WM_SYSKEYUP:
2155 /*
2156 * Add the scan code and keypress timing to the random
2157 * number noise.
2158 */
2159 noise_ultralight(lParam);
2160
2161 /*
2162 * We don't do TranslateMessage since it disassociates the
2163 * resulting CHAR message from the KEYDOWN that sparked it,
2164 * which we occasionally don't want. Instead, we process
2165 * KEYDOWN, and call the Win32 translator functions so that
2166 * we get the translations under _our_ control.
2167 */
2168 {
2169 unsigned char buf[20];
2170 int len;
2171
2172 if (wParam == VK_PROCESSKEY) {
2173 MSG m;
2174 m.hwnd = hwnd;
2175 m.message = WM_KEYDOWN;
2176 m.wParam = wParam;
2177 m.lParam = lParam & 0xdfff;
2178 TranslateMessage(&m);
2179 } else {
2180 len = TranslateKey(message, wParam, lParam, buf);
2181 if (len == -1)
2182 return DefWindowProc(hwnd, message, wParam, lParam);
2183
2184 if (len != 0) {
2185 /*
2186 * Interrupt an ongoing paste. I'm not sure
2187 * this is sensible, but for the moment it's
2188 * preferable to having to faff about buffering
2189 * things.
2190 */
2191 term_nopaste();
2192
2193 /*
2194 * We need not bother about stdin backlogs
2195 * here, because in GUI PuTTY we can't do
2196 * anything about it anyway; there's no means
2197 * of asking Windows to hold off on KEYDOWN
2198 * messages. We _have_ to buffer everything
2199 * we're sent.
2200 */
2201 ldisc_send(buf, len, 1);
2202 show_mouseptr(0);
2203 }
2204 }
2205 }
2206 net_pending_errors();
2207 return 0;
2208 case WM_INPUTLANGCHANGE:
2209 {
2210 /* wParam == Font number */
2211 /* lParam == Locale */
2212 char lbuf[20];
2213 HKL NewInputLocale = (HKL) lParam;
2214
2215 // lParam == GetKeyboardLayout(0);
2216
2217 GetLocaleInfo(LOWORD(NewInputLocale),
2218 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2219
2220 kbd_codepage = atoi(lbuf);
2221 }
2222 break;
2223 case WM_IME_COMPOSITION:
2224 {
2225 HIMC hIMC;
2226 int n;
2227 char *buff;
2228
2229 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2230 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2231
2232 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2233 break; /* fall back to DefWindowProc */
2234
2235 hIMC = ImmGetContext(hwnd);
2236 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2237
2238 if (n > 0) {
2239 buff = (char*) smalloc(n);
2240 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2241 luni_send((unsigned short *)buff, n / 2, 1);
2242 free(buff);
2243 }
2244 ImmReleaseContext(hwnd, hIMC);
2245 return 1;
2246 }
2247
2248 case WM_IME_CHAR:
2249 if (wParam & 0xFF00) {
2250 unsigned char buf[2];
2251
2252 buf[1] = wParam;
2253 buf[0] = wParam >> 8;
2254 lpage_send(kbd_codepage, buf, 2, 1);
2255 } else {
2256 char c = (unsigned char) wParam;
2257 lpage_send(kbd_codepage, &c, 1, 1);
2258 }
2259 return (0);
2260 case WM_CHAR:
2261 case WM_SYSCHAR:
2262 /*
2263 * Nevertheless, we are prepared to deal with WM_CHAR
2264 * messages, should they crop up. So if someone wants to
2265 * post the things to us as part of a macro manoeuvre,
2266 * we're ready to cope.
2267 */
2268 {
2269 char c = (unsigned char)wParam;
2270 lpage_send(CP_ACP, &c, 1, 1);
2271 }
2272 return 0;
2273 case WM_SETCURSOR:
2274 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2275 SetCursor(LoadCursor(NULL, IDC_ARROW));
2276 return TRUE;
2277 }
2278 }
2279
2280 return DefWindowProc(hwnd, message, wParam, lParam);
2281}
2282
2283/*
2284 * Move the system caret. (We maintain one, even though it's
2285 * invisible, for the benefit of blind people: apparently some
2286 * helper software tracks the system caret, so we should arrange to
2287 * have one.)
2288 */
2289void sys_cursor(int x, int y)
2290{
2291 COMPOSITIONFORM cf;
2292 HIMC hIMC;
2293
2294 if (!has_focus) return;
2295
2296 SetCaretPos(x * font_width + offset_width,
2297 y * font_height + offset_height);
2298
2299 /* IMM calls on Win98 and beyond only */
2300 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2301
2302 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2303 osVersion.dwMinorVersion == 0) return; /* 95 */
2304
2305 /* we should have the IMM functions */
2306 hIMC = ImmGetContext(hwnd);
2307 cf.dwStyle = CFS_POINT;
2308 cf.ptCurrentPos.x = x * font_width;
2309 cf.ptCurrentPos.y = y * font_height;
2310 ImmSetCompositionWindow(hIMC, &cf);
2311
2312 ImmReleaseContext(hwnd, hIMC);
2313}
2314
2315/*
2316 * Draw a line of text in the window, at given character
2317 * coordinates, in given attributes.
2318 *
2319 * We are allowed to fiddle with the contents of `text'.
2320 */
2321void do_text(Context ctx, int x, int y, char *text, int len,
2322 unsigned long attr, int lattr)
2323{
2324 COLORREF fg, bg, t;
2325 int nfg, nbg, nfont;
2326 HDC hdc = ctx;
2327 RECT line_box;
2328 int force_manual_underline = 0;
2329 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2330 int char_width = fnt_width;
2331 int text_adjust = 0;
2332 static int *IpDx = 0, IpDxLEN = 0;
2333
2334 if (attr & ATTR_WIDE)
2335 char_width *= 2;
2336
2337 if (len > IpDxLEN || IpDx[0] != char_width) {
2338 int i;
2339 if (len > IpDxLEN) {
2340 sfree(IpDx);
2341 IpDx = smalloc((len + 16) * sizeof(int));
2342 IpDxLEN = (len + 16);
2343 }
2344 for (i = 0; i < IpDxLEN; i++)
2345 IpDx[i] = char_width;
2346 }
2347
2348 /* Only want the left half of double width lines */
2349 if (lattr != LATTR_NORM && x*2 >= cols)
2350 return;
2351
2352 x *= fnt_width;
2353 y *= font_height;
2354 x += offset_width;
2355 y += offset_height;
2356
2357 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2358 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2359 attr ^= ATTR_CUR_XOR;
2360 }
2361
2362 nfont = 0;
2363 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2364 /* Assume a poorman font is borken in other ways too. */
2365 lattr = LATTR_WIDE;
2366 } else
2367 switch (lattr) {
2368 case LATTR_NORM:
2369 break;
2370 case LATTR_WIDE:
2371 nfont |= FONT_WIDE;
2372 break;
2373 default:
2374 nfont |= FONT_WIDE + FONT_HIGH;
2375 break;
2376 }
2377 if (attr & ATTR_NARROW)
2378 nfont |= FONT_NARROW;
2379
2380 /* Special hack for the VT100 linedraw glyphs. */
2381 if ((attr & CSET_MASK) == 0x2300) {
2382 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2383 switch ((unsigned char) (text[0])) {
2384 case 0xBA:
2385 text_adjust = -2 * font_height / 5;
2386 break;
2387 case 0xBB:
2388 text_adjust = -1 * font_height / 5;
2389 break;
2390 case 0xBC:
2391 text_adjust = font_height / 5;
2392 break;
2393 case 0xBD:
2394 text_adjust = 2 * font_height / 5;
2395 break;
2396 }
2397 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2398 text_adjust *= 2;
2399 attr &= ~CSET_MASK;
2400 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2401 attr |= (unitab_xterm['q'] & CSET_MASK);
2402 if (attr & ATTR_UNDER) {
2403 attr &= ~ATTR_UNDER;
2404 force_manual_underline = 1;
2405 }
2406 }
2407 }
2408
2409 /* Anything left as an original character set is unprintable. */
2410 if (DIRECT_CHAR(attr)) {
2411 attr &= ~CSET_MASK;
2412 attr |= 0xFF00;
2413 memset(text, 0xFD, len);
2414 }
2415
2416 /* OEM CP */
2417 if ((attr & CSET_MASK) == ATTR_OEMCP)
2418 nfont |= FONT_OEM;
2419
2420 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2421 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2422 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2423 nfont |= FONT_BOLD;
2424 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2425 nfont |= FONT_UNDERLINE;
2426 another_font(nfont);
2427 if (!fonts[nfont]) {
2428 if (nfont & FONT_UNDERLINE)
2429 force_manual_underline = 1;
2430 /* Don't do the same for manual bold, it could be bad news. */
2431
2432 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2433 }
2434 another_font(nfont);
2435 if (!fonts[nfont])
2436 nfont = FONT_NORMAL;
2437 if (attr & ATTR_REVERSE) {
2438 t = nfg;
2439 nfg = nbg;
2440 nbg = t;
2441 }
2442 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2443 nfg++;
2444 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2445 nbg++;
2446 fg = colours[nfg];
2447 bg = colours[nbg];
2448 SelectObject(hdc, fonts[nfont]);
2449 SetTextColor(hdc, fg);
2450 SetBkColor(hdc, bg);
2451 SetBkMode(hdc, OPAQUE);
2452 line_box.left = x;
2453 line_box.top = y;
2454 line_box.right = x + char_width * len;
2455 line_box.bottom = y + font_height;
2456
2457 /* Only want the left half of double width lines */
2458 if (line_box.right > font_width*cols+offset_width)
2459 line_box.right = font_width*cols+offset_width;
2460
2461 /* We're using a private area for direct to font. (512 chars.) */
2462 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2463 /* Ho Hum, dbcs fonts are a PITA! */
2464 /* To display on W9x I have to convert to UCS */
2465 static wchar_t *uni_buf = 0;
2466 static int uni_len = 0;
2467 int nlen, mptr;
2468 if (len > uni_len) {
2469 sfree(uni_buf);
2470 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2471 }
2472
2473 for(nlen = mptr = 0; mptr<len; mptr++) {
2474 uni_buf[nlen] = 0xFFFD;
2475 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2476 IpDx[nlen] += char_width;
2477 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2478 text+mptr, 2, uni_buf+nlen, 1);
2479 mptr++;
2480 }
2481 else
2482 {
2483 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2484 text+mptr, 1, uni_buf+nlen, 1);
2485 }
2486 nlen++;
2487 }
2488 if (nlen <= 0)
2489 return; /* Eeek! */
2490
2491 ExtTextOutW(hdc, x,
2492 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2493 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2494 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2495 SetBkMode(hdc, TRANSPARENT);
2496 ExtTextOutW(hdc, x - 1,
2497 y - font_height * (lattr ==
2498 LATTR_BOT) + text_adjust,
2499 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2500 }
2501
2502 IpDx[0] = -1;
2503 } else if (DIRECT_FONT(attr)) {
2504 ExtTextOut(hdc, x,
2505 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2506 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2507 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2508 SetBkMode(hdc, TRANSPARENT);
2509
2510 /* GRR: This draws the character outside it's box and can leave
2511 * 'droppings' even with the clip box! I suppose I could loop it
2512 * one character at a time ... yuk.
2513 *
2514 * Or ... I could do a test print with "W", and use +1 or -1 for this
2515 * shift depending on if the leftmost column is blank...
2516 */
2517 ExtTextOut(hdc, x - 1,
2518 y - font_height * (lattr ==
2519 LATTR_BOT) + text_adjust,
2520 ETO_CLIPPED, &line_box, text, len, IpDx);
2521 }
2522 } else {
2523 /* And 'normal' unicode characters */
2524 static WCHAR *wbuf = NULL;
2525 static int wlen = 0;
2526 int i;
2527 if (wlen < len) {
2528 sfree(wbuf);
2529 wlen = len;
2530 wbuf = smalloc(wlen * sizeof(WCHAR));
2531 }
2532 for (i = 0; i < len; i++)
2533 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2534
2535 ExtTextOutW(hdc, x,
2536 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2537 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2538
2539 /* And the shadow bold hack. */
2540 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2541 SetBkMode(hdc, TRANSPARENT);
2542 ExtTextOutW(hdc, x - 1,
2543 y - font_height * (lattr ==
2544 LATTR_BOT) + text_adjust,
2545 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2546 }
2547 }
2548 if (lattr != LATTR_TOP && (force_manual_underline ||
2549 (und_mode == UND_LINE
2550 && (attr & ATTR_UNDER)))) {
2551 HPEN oldpen;
2552 int dec = descent;
2553 if (lattr == LATTR_BOT)
2554 dec = dec * 2 - font_height;
2555
2556 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2557 MoveToEx(hdc, x, y + dec, NULL);
2558 LineTo(hdc, x + len * char_width, y + dec);
2559 oldpen = SelectObject(hdc, oldpen);
2560 DeleteObject(oldpen);
2561 }
2562}
2563
2564void do_cursor(Context ctx, int x, int y, char *text, int len,
2565 unsigned long attr, int lattr)
2566{
2567
2568 int fnt_width;
2569 int char_width;
2570 HDC hdc = ctx;
2571 int ctype = cfg.cursor_type;
2572
2573 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2574 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2575 do_text(ctx, x, y, text, len, attr, lattr);
2576 return;
2577 }
2578 ctype = 2;
2579 attr |= TATTR_RIGHTCURS;
2580 }
2581
2582 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2583 if (attr & ATTR_WIDE)
2584 char_width *= 2;
2585 x *= fnt_width;
2586 y *= font_height;
2587 x += offset_width;
2588 y += offset_height;
2589
2590 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2591 POINT pts[5];
2592 HPEN oldpen;
2593 pts[0].x = pts[1].x = pts[4].x = x;
2594 pts[2].x = pts[3].x = x + char_width - 1;
2595 pts[0].y = pts[3].y = pts[4].y = y;
2596 pts[1].y = pts[2].y = y + font_height - 1;
2597 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2598 Polyline(hdc, pts, 5);
2599 oldpen = SelectObject(hdc, oldpen);
2600 DeleteObject(oldpen);
2601 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2602 int startx, starty, dx, dy, length, i;
2603 if (ctype == 1) {
2604 startx = x;
2605 starty = y + descent;
2606 dx = 1;
2607 dy = 0;
2608 length = char_width;
2609 } else {
2610 int xadjust = 0;
2611 if (attr & TATTR_RIGHTCURS)
2612 xadjust = char_width - 1;
2613 startx = x + xadjust;
2614 starty = y;
2615 dx = 0;
2616 dy = 1;
2617 length = font_height;
2618 }
2619 if (attr & TATTR_ACTCURS) {
2620 HPEN oldpen;
2621 oldpen =
2622 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2623 MoveToEx(hdc, startx, starty, NULL);
2624 LineTo(hdc, startx + dx * length, starty + dy * length);
2625 oldpen = SelectObject(hdc, oldpen);
2626 DeleteObject(oldpen);
2627 } else {
2628 for (i = 0; i < length; i++) {
2629 if (i % 2 == 0) {
2630 SetPixel(hdc, startx, starty, colours[23]);
2631 }
2632 startx += dx;
2633 starty += dy;
2634 }
2635 }
2636 }
2637}
2638
2639/* This function gets the actual width of a character in the normal font.
2640 */
2641int CharWidth(Context ctx, int uc) {
2642 HDC hdc = ctx;
2643 int ibuf = 0;
2644
2645 /* If the font max is the same as the font ave width then this
2646 * function is a no-op.
2647 */
2648 if (!font_dualwidth) return 1;
2649
2650 switch (uc & CSET_MASK) {
2651 case ATTR_ASCII:
2652 uc = unitab_line[uc & 0xFF];
2653 break;
2654 case ATTR_LINEDRW:
2655 uc = unitab_xterm[uc & 0xFF];
2656 break;
2657 case ATTR_SCOACS:
2658 uc = unitab_scoacs[uc & 0xFF];
2659 break;
2660 }
2661 if (DIRECT_FONT(uc)) {
2662 if (dbcs_screenfont) return 1;
2663
2664 /* Speedup, I know of no font where ascii is the wrong width */
2665 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2666 return 1;
2667
2668 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2669 SelectObject(hdc, fonts[FONT_NORMAL]);
2670 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2671 another_font(FONT_OEM);
2672 if (!fonts[FONT_OEM]) return 0;
2673
2674 SelectObject(hdc, fonts[FONT_OEM]);
2675 } else
2676 return 0;
2677
2678 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2679 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2680 return 0;
2681 } else {
2682 /* Speedup, I know of no font where ascii is the wrong width */
2683 if (uc >= ' ' && uc <= '~') return 1;
2684
2685 SelectObject(hdc, fonts[FONT_NORMAL]);
2686 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2687 /* Okay that one worked */ ;
2688 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2689 /* This should work on 9x too, but it's "less accurate" */ ;
2690 else
2691 return 0;
2692 }
2693
2694 ibuf += font_width / 2 -1;
2695 ibuf /= font_width;
2696
2697 return ibuf;
2698}
2699
2700/*
2701 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2702 * codes. Returns number of bytes used or zero to drop the message
2703 * or -1 to forward the message to windows.
2704 */
2705static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2706 unsigned char *output)
2707{
2708 BYTE keystate[256];
2709 int scan, left_alt = 0, key_down, shift_state;
2710 int r, i, code;
2711 unsigned char *p = output;
2712 static int alt_sum = 0;
2713
2714 HKL kbd_layout = GetKeyboardLayout(0);
2715
2716 static WORD keys[3];
2717 static int compose_char = 0;
2718 static WPARAM compose_key = 0;
2719
2720 r = GetKeyboardState(keystate);
2721 if (!r)
2722 memset(keystate, 0, sizeof(keystate));
2723 else {
2724#if 0
2725#define SHOW_TOASCII_RESULT
2726 { /* Tell us all about key events */
2727 static BYTE oldstate[256];
2728 static int first = 1;
2729 static int scan;
2730 int ch;
2731 if (first)
2732 memcpy(oldstate, keystate, sizeof(oldstate));
2733 first = 0;
2734
2735 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2736 debug(("+"));
2737 } else if ((HIWORD(lParam) & KF_UP)
2738 && scan == (HIWORD(lParam) & 0xFF)) {
2739 debug((". U"));
2740 } else {
2741 debug((".\n"));
2742 if (wParam >= VK_F1 && wParam <= VK_F20)
2743 debug(("K_F%d", wParam + 1 - VK_F1));
2744 else
2745 switch (wParam) {
2746 case VK_SHIFT:
2747 debug(("SHIFT"));
2748 break;
2749 case VK_CONTROL:
2750 debug(("CTRL"));
2751 break;
2752 case VK_MENU:
2753 debug(("ALT"));
2754 break;
2755 default:
2756 debug(("VK_%02x", wParam));
2757 }
2758 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2759 debug(("*"));
2760 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2761
2762 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2763 if (ch >= ' ' && ch <= '~')
2764 debug((", '%c'", ch));
2765 else if (ch)
2766 debug((", $%02x", ch));
2767
2768 if (keys[0])
2769 debug((", KB0=%02x", keys[0]));
2770 if (keys[1])
2771 debug((", KB1=%02x", keys[1]));
2772 if (keys[2])
2773 debug((", KB2=%02x", keys[2]));
2774
2775 if ((keystate[VK_SHIFT] & 0x80) != 0)
2776 debug((", S"));
2777 if ((keystate[VK_CONTROL] & 0x80) != 0)
2778 debug((", C"));
2779 if ((HIWORD(lParam) & KF_EXTENDED))
2780 debug((", E"));
2781 if ((HIWORD(lParam) & KF_UP))
2782 debug((", U"));
2783 }
2784
2785 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2786 else if ((HIWORD(lParam) & KF_UP))
2787 oldstate[wParam & 0xFF] ^= 0x80;
2788 else
2789 oldstate[wParam & 0xFF] ^= 0x81;
2790
2791 for (ch = 0; ch < 256; ch++)
2792 if (oldstate[ch] != keystate[ch])
2793 debug((", M%02x=%02x", ch, keystate[ch]));
2794
2795 memcpy(oldstate, keystate, sizeof(oldstate));
2796 }
2797#endif
2798
2799 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2800 keystate[VK_RMENU] = keystate[VK_MENU];
2801 }
2802
2803
2804 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2805 if ((cfg.funky_type == 3 ||
2806 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2807 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2808
2809 wParam = VK_EXECUTE;
2810
2811 /* UnToggle NUMLock */
2812 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2813 keystate[VK_NUMLOCK] ^= 1;
2814 }
2815
2816 /* And write back the 'adjusted' state */
2817 SetKeyboardState(keystate);
2818 }
2819
2820 /* Disable Auto repeat if required */
2821 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2822 return 0;
2823
2824 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2825 left_alt = 1;
2826
2827 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2828
2829 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2830 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2831 if (cfg.ctrlaltkeys)
2832 keystate[VK_MENU] = 0;
2833 else {
2834 keystate[VK_RMENU] = 0x80;
2835 left_alt = 0;
2836 }
2837 }
2838
2839 alt_pressed = (left_alt && key_down);
2840
2841 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2842 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2843 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2844
2845 /* Note if AltGr was pressed and if it was used as a compose key */
2846 if (!compose_state) {
2847 compose_key = 0x100;
2848 if (cfg.compose_key) {
2849 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2850 compose_key = wParam;
2851 }
2852 if (wParam == VK_APPS)
2853 compose_key = wParam;
2854 }
2855
2856 if (wParam == compose_key) {
2857 if (compose_state == 0
2858 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2859 1;
2860 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2861 compose_state = 2;
2862 else
2863 compose_state = 0;
2864 } else if (compose_state == 1 && wParam != VK_CONTROL)
2865 compose_state = 0;
2866
2867 /*
2868 * Record that we pressed key so the scroll window can be reset, but
2869 * be careful to avoid Shift-UP/Down
2870 */
2871 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2872 seen_key_event = 1;
2873 }
2874
2875 if (compose_state > 1 && left_alt)
2876 compose_state = 0;
2877
2878 /* Sanitize the number pad if not using a PC NumPad */
2879 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2880 && cfg.funky_type != 2)
2881 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2882 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2883 int nParam = 0;
2884 switch (wParam) {
2885 case VK_INSERT:
2886 nParam = VK_NUMPAD0;
2887 break;
2888 case VK_END:
2889 nParam = VK_NUMPAD1;
2890 break;
2891 case VK_DOWN:
2892 nParam = VK_NUMPAD2;
2893 break;
2894 case VK_NEXT:
2895 nParam = VK_NUMPAD3;
2896 break;
2897 case VK_LEFT:
2898 nParam = VK_NUMPAD4;
2899 break;
2900 case VK_CLEAR:
2901 nParam = VK_NUMPAD5;
2902 break;
2903 case VK_RIGHT:
2904 nParam = VK_NUMPAD6;
2905 break;
2906 case VK_HOME:
2907 nParam = VK_NUMPAD7;
2908 break;
2909 case VK_UP:
2910 nParam = VK_NUMPAD8;
2911 break;
2912 case VK_PRIOR:
2913 nParam = VK_NUMPAD9;
2914 break;
2915 case VK_DELETE:
2916 nParam = VK_DECIMAL;
2917 break;
2918 }
2919 if (nParam) {
2920 if (keystate[VK_NUMLOCK] & 1)
2921 shift_state |= 1;
2922 wParam = nParam;
2923 }
2924 }
2925 }
2926
2927 /* If a key is pressed and AltGr is not active */
2928 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2929 /* Okay, prepare for most alts then ... */
2930 if (left_alt)
2931 *p++ = '\033';
2932
2933 /* Lets see if it's a pattern we know all about ... */
2934 if (wParam == VK_PRIOR && shift_state == 1) {
2935 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2936 return 0;
2937 }
2938 if (wParam == VK_NEXT && shift_state == 1) {
2939 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2940 return 0;
2941 }
2942 if (wParam == VK_INSERT && shift_state == 1) {
2943 term_do_paste();
2944 return 0;
2945 }
2946 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2947 return -1;
2948 }
2949 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2950 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2951 return -1;
2952 }
2953 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2954 flip_full_screen();
2955 return -1;
2956 }
2957 /* Control-Numlock for app-keypad mode switch */
2958 if (wParam == VK_PAUSE && shift_state == 2) {
2959 app_keypad_keys ^= 1;
2960 return 0;
2961 }
2962
2963 /* Nethack keypad */
2964 if (cfg.nethack_keypad && !left_alt) {
2965 switch (wParam) {
2966 case VK_NUMPAD1:
2967 *p++ = shift_state ? 'B' : 'b';
2968 return p - output;
2969 case VK_NUMPAD2:
2970 *p++ = shift_state ? 'J' : 'j';
2971 return p - output;
2972 case VK_NUMPAD3:
2973 *p++ = shift_state ? 'N' : 'n';
2974 return p - output;
2975 case VK_NUMPAD4:
2976 *p++ = shift_state ? 'H' : 'h';
2977 return p - output;
2978 case VK_NUMPAD5:
2979 *p++ = shift_state ? '.' : '.';
2980 return p - output;
2981 case VK_NUMPAD6:
2982 *p++ = shift_state ? 'L' : 'l';
2983 return p - output;
2984 case VK_NUMPAD7:
2985 *p++ = shift_state ? 'Y' : 'y';
2986 return p - output;
2987 case VK_NUMPAD8:
2988 *p++ = shift_state ? 'K' : 'k';
2989 return p - output;
2990 case VK_NUMPAD9:
2991 *p++ = shift_state ? 'U' : 'u';
2992 return p - output;
2993 }
2994 }
2995
2996 /* Application Keypad */
2997 if (!left_alt) {
2998 int xkey = 0;
2999
3000 if (cfg.funky_type == 3 ||
3001 (cfg.funky_type <= 1 &&
3002 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3003 case VK_EXECUTE:
3004 xkey = 'P';
3005 break;
3006 case VK_DIVIDE:
3007 xkey = 'Q';
3008 break;
3009 case VK_MULTIPLY:
3010 xkey = 'R';
3011 break;
3012 case VK_SUBTRACT:
3013 xkey = 'S';
3014 break;
3015 }
3016 if (app_keypad_keys && !cfg.no_applic_k)
3017 switch (wParam) {
3018 case VK_NUMPAD0:
3019 xkey = 'p';
3020 break;
3021 case VK_NUMPAD1:
3022 xkey = 'q';
3023 break;
3024 case VK_NUMPAD2:
3025 xkey = 'r';
3026 break;
3027 case VK_NUMPAD3:
3028 xkey = 's';
3029 break;
3030 case VK_NUMPAD4:
3031 xkey = 't';
3032 break;
3033 case VK_NUMPAD5:
3034 xkey = 'u';
3035 break;
3036 case VK_NUMPAD6:
3037 xkey = 'v';
3038 break;
3039 case VK_NUMPAD7:
3040 xkey = 'w';
3041 break;
3042 case VK_NUMPAD8:
3043 xkey = 'x';
3044 break;
3045 case VK_NUMPAD9:
3046 xkey = 'y';
3047 break;
3048
3049 case VK_DECIMAL:
3050 xkey = 'n';
3051 break;
3052 case VK_ADD:
3053 if (cfg.funky_type == 2) {
3054 if (shift_state)
3055 xkey = 'l';
3056 else
3057 xkey = 'k';
3058 } else if (shift_state)
3059 xkey = 'm';
3060 else
3061 xkey = 'l';
3062 break;
3063
3064 case VK_DIVIDE:
3065 if (cfg.funky_type == 2)
3066 xkey = 'o';
3067 break;
3068 case VK_MULTIPLY:
3069 if (cfg.funky_type == 2)
3070 xkey = 'j';
3071 break;
3072 case VK_SUBTRACT:
3073 if (cfg.funky_type == 2)
3074 xkey = 'm';
3075 break;
3076
3077 case VK_RETURN:
3078 if (HIWORD(lParam) & KF_EXTENDED)
3079 xkey = 'M';
3080 break;
3081 }
3082 if (xkey) {
3083 if (vt52_mode) {
3084 if (xkey >= 'P' && xkey <= 'S')
3085 p += sprintf((char *) p, "\x1B%c", xkey);
3086 else
3087 p += sprintf((char *) p, "\x1B?%c", xkey);
3088 } else
3089 p += sprintf((char *) p, "\x1BO%c", xkey);
3090 return p - output;
3091 }
3092 }
3093
3094 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3095 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3096 *p++ = 0;
3097 return -2;
3098 }
3099 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3100 *p++ = 0x1B;
3101 *p++ = '[';
3102 *p++ = 'Z';
3103 return p - output;
3104 }
3105 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3106 *p++ = 0;
3107 return p - output;
3108 }
3109 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3110 *p++ = 160;
3111 return p - output;
3112 }
3113 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3114 *p++ = 3;
3115 *p++ = 0;
3116 return -2;
3117 }
3118 if (wParam == VK_PAUSE) { /* Break/Pause */
3119 *p++ = 26;
3120 *p++ = 0;
3121 return -2;
3122 }
3123 /* Control-2 to Control-8 are special */
3124 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3125 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3126 return p - output;
3127 }
3128 if (shift_state == 2 && wParam == 0xBD) {
3129 *p++ = 0x1F;
3130 return p - output;
3131 }
3132 if (shift_state == 2 && wParam == 0xDF) {
3133 *p++ = 0x1C;
3134 return p - output;
3135 }
3136 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3137 *p++ = '\r';
3138 *p++ = '\n';
3139 return p - output;
3140 }
3141
3142 /*
3143 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3144 * for integer decimal nn.)
3145 *
3146 * We also deal with the weird ones here. Linux VCs replace F1
3147 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3148 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3149 * respectively.
3150 */
3151 code = 0;
3152 switch (wParam) {
3153 case VK_F1:
3154 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3155 break;
3156 case VK_F2:
3157 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3158 break;
3159 case VK_F3:
3160 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3161 break;
3162 case VK_F4:
3163 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3164 break;
3165 case VK_F5:
3166 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3167 break;
3168 case VK_F6:
3169 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3170 break;
3171 case VK_F7:
3172 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3173 break;
3174 case VK_F8:
3175 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3176 break;
3177 case VK_F9:
3178 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3179 break;
3180 case VK_F10:
3181 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3182 break;
3183 case VK_F11:
3184 code = 23;
3185 break;
3186 case VK_F12:
3187 code = 24;
3188 break;
3189 case VK_F13:
3190 code = 25;
3191 break;
3192 case VK_F14:
3193 code = 26;
3194 break;
3195 case VK_F15:
3196 code = 28;
3197 break;
3198 case VK_F16:
3199 code = 29;
3200 break;
3201 case VK_F17:
3202 code = 31;
3203 break;
3204 case VK_F18:
3205 code = 32;
3206 break;
3207 case VK_F19:
3208 code = 33;
3209 break;
3210 case VK_F20:
3211 code = 34;
3212 break;
3213 }
3214 if ((shift_state&2) == 0) switch (wParam) {
3215 case VK_HOME:
3216 code = 1;
3217 break;
3218 case VK_INSERT:
3219 code = 2;
3220 break;
3221 case VK_DELETE:
3222 code = 3;
3223 break;
3224 case VK_END:
3225 code = 4;
3226 break;
3227 case VK_PRIOR:
3228 code = 5;
3229 break;
3230 case VK_NEXT:
3231 code = 6;
3232 break;
3233 }
3234 /* Reorder edit keys to physical order */
3235 if (cfg.funky_type == 3 && code <= 6)
3236 code = "\0\2\1\4\5\3\6"[code];
3237
3238 if (vt52_mode && code > 0 && code <= 6) {
3239 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3240 return p - output;
3241 }
3242
3243 if (cfg.funky_type == 5 && /* SCO function keys */
3244 code >= 11 && code <= 34) {
3245 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3246 int index = 0;
3247 switch (wParam) {
3248 case VK_F1: index = 0; break;
3249 case VK_F2: index = 1; break;
3250 case VK_F3: index = 2; break;
3251 case VK_F4: index = 3; break;
3252 case VK_F5: index = 4; break;
3253 case VK_F6: index = 5; break;
3254 case VK_F7: index = 6; break;
3255 case VK_F8: index = 7; break;
3256 case VK_F9: index = 8; break;
3257 case VK_F10: index = 9; break;
3258 case VK_F11: index = 10; break;
3259 case VK_F12: index = 11; break;
3260 }
3261 if (keystate[VK_SHIFT] & 0x80) index += 12;
3262 if (keystate[VK_CONTROL] & 0x80) index += 24;
3263 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3264 return p - output;
3265 }
3266 if (cfg.funky_type == 5 && /* SCO small keypad */
3267 code >= 1 && code <= 6) {
3268 char codes[] = "HL.FIG";
3269 if (code == 3) {
3270 *p++ = '\x7F';
3271 } else {
3272 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3273 }
3274 return p - output;
3275 }
3276 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3277 int offt = 0;
3278 if (code > 15)
3279 offt++;
3280 if (code > 21)
3281 offt++;
3282 if (vt52_mode)
3283 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3284 else
3285 p +=
3286 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3287 return p - output;
3288 }
3289 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3290 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3291 return p - output;
3292 }
3293 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3294 if (vt52_mode)
3295 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3296 else
3297 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3298 return p - output;
3299 }
3300 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3301 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3302 return p - output;
3303 }
3304 if (code) {
3305 p += sprintf((char *) p, "\x1B[%d~", code);
3306 return p - output;
3307 }
3308
3309 /*
3310 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3311 * some reason seems to send VK_CLEAR to Windows...).
3312 */
3313 {
3314 char xkey = 0;
3315 switch (wParam) {
3316 case VK_UP:
3317 xkey = 'A';
3318 break;
3319 case VK_DOWN:
3320 xkey = 'B';
3321 break;
3322 case VK_RIGHT:
3323 xkey = 'C';
3324 break;
3325 case VK_LEFT:
3326 xkey = 'D';
3327 break;
3328 case VK_CLEAR:
3329 xkey = 'G';
3330 break;
3331 }
3332 if (xkey) {
3333 if (vt52_mode)
3334 p += sprintf((char *) p, "\x1B%c", xkey);
3335 else {
3336 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3337 /* VT100 & VT102 manuals both state the app cursor keys
3338 * only work if the app keypad is on.
3339 */
3340 if (!app_keypad_keys)
3341 app_flg = 0;
3342 /* Useful mapping of Ctrl-arrows */
3343 if (shift_state == 2)
3344 app_flg = !app_flg;
3345
3346 if (app_flg)
3347 p += sprintf((char *) p, "\x1BO%c", xkey);
3348 else
3349 p += sprintf((char *) p, "\x1B[%c", xkey);
3350 }
3351 return p - output;
3352 }
3353 }
3354
3355 /*
3356 * Finally, deal with Return ourselves. (Win95 seems to
3357 * foul it up when Alt is pressed, for some reason.)
3358 */
3359 if (wParam == VK_RETURN) { /* Return */
3360 *p++ = 0x0D;
3361 *p++ = 0;
3362 return -2;
3363 }
3364
3365 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3366 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3367 else
3368 alt_sum = 0;
3369 }
3370
3371 /* Okay we've done everything interesting; let windows deal with
3372 * the boring stuff */
3373 {
3374 BOOL capsOn=0;
3375
3376 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3377 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3378 capsOn= !left_alt;
3379 keystate[VK_CAPITAL] = 0;
3380 }
3381
3382 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3383#ifdef SHOW_TOASCII_RESULT
3384 if (r == 1 && !key_down) {
3385 if (alt_sum) {
3386 if (in_utf || dbcs_screenfont)
3387 debug((", (U+%04x)", alt_sum));
3388 else
3389 debug((", LCH(%d)", alt_sum));
3390 } else {
3391 debug((", ACH(%d)", keys[0]));
3392 }
3393 } else if (r > 0) {
3394 int r1;
3395 debug((", ASC("));
3396 for (r1 = 0; r1 < r; r1++) {
3397 debug(("%s%d", r1 ? "," : "", keys[r1]));
3398 }
3399 debug((")"));
3400 }
3401#endif
3402 if (r > 0) {
3403 WCHAR keybuf;
3404
3405 /*
3406 * Interrupt an ongoing paste. I'm not sure this is
3407 * sensible, but for the moment it's preferable to
3408 * having to faff about buffering things.
3409 */
3410 term_nopaste();
3411
3412 p = output;
3413 for (i = 0; i < r; i++) {
3414 unsigned char ch = (unsigned char) keys[i];
3415
3416 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3417 compose_char = ch;
3418 compose_state++;
3419 continue;
3420 }
3421 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3422 int nc;
3423 compose_state = 0;
3424
3425 if ((nc = check_compose(compose_char, ch)) == -1) {
3426 MessageBeep(MB_ICONHAND);
3427 return 0;
3428 }
3429 keybuf = nc;
3430 luni_send(&keybuf, 1, 1);
3431 continue;
3432 }
3433
3434 compose_state = 0;
3435
3436 if (!key_down) {
3437 if (alt_sum) {
3438 if (in_utf || dbcs_screenfont) {
3439 keybuf = alt_sum;
3440 luni_send(&keybuf, 1, 1);
3441 } else {
3442 ch = (char) alt_sum;
3443 /*
3444 * We need not bother about stdin
3445 * backlogs here, because in GUI PuTTY
3446 * we can't do anything about it
3447 * anyway; there's no means of asking
3448 * Windows to hold off on KEYDOWN
3449 * messages. We _have_ to buffer
3450 * everything we're sent.
3451 */
3452 ldisc_send(&ch, 1, 1);
3453 }
3454 alt_sum = 0;
3455 } else
3456 lpage_send(kbd_codepage, &ch, 1, 1);
3457 } else {
3458 if(capsOn && ch < 0x80) {
3459 WCHAR cbuf[2];
3460 cbuf[0] = 27;
3461 cbuf[1] = xlat_uskbd2cyrllic(ch);
3462 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3463 } else {
3464 char cbuf[2];
3465 cbuf[0] = '\033';
3466 cbuf[1] = ch;
3467 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3468 }
3469 }
3470 show_mouseptr(0);
3471 }
3472
3473 /* This is so the ALT-Numpad and dead keys work correctly. */
3474 keys[0] = 0;
3475
3476 return p - output;
3477 }
3478 /* If we're definitly not building up an ALT-54321 then clear it */
3479 if (!left_alt)
3480 keys[0] = 0;
3481 /* If we will be using alt_sum fix the 256s */
3482 else if (keys[0] && (in_utf || dbcs_screenfont))
3483 keys[0] = 10;
3484 }
3485
3486 /*
3487 * ALT alone may or may not want to bring up the System menu.
3488 * If it's not meant to, we return 0 on presses or releases of
3489 * ALT, to show that we've swallowed the keystroke. Otherwise
3490 * we return -1, which means Windows will give the keystroke
3491 * its default handling (i.e. bring up the System menu).
3492 */
3493 if (wParam == VK_MENU && !cfg.alt_only)
3494 return 0;
3495
3496 return -1;
3497}
3498
3499void set_title(char *title)
3500{
3501 sfree(window_name);
3502 window_name = smalloc(1 + strlen(title));
3503 strcpy(window_name, title);
3504 if (cfg.win_name_always || !IsIconic(hwnd))
3505 SetWindowText(hwnd, title);
3506}
3507
3508void set_icon(char *title)
3509{
3510 sfree(icon_name);
3511 icon_name = smalloc(1 + strlen(title));
3512 strcpy(icon_name, title);
3513 if (!cfg.win_name_always && IsIconic(hwnd))
3514 SetWindowText(hwnd, title);
3515}
3516
3517void set_sbar(int total, int start, int page)
3518{
3519 SCROLLINFO si;
3520
3521 if (!cfg.scrollbar)
3522 return;
3523
3524 si.cbSize = sizeof(si);
3525 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3526 si.nMin = 0;
3527 si.nMax = total - 1;
3528 si.nPage = page;
3529 si.nPos = start;
3530 if (hwnd)
3531 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3532}
3533
3534Context get_ctx(void)
3535{
3536 HDC hdc;
3537 if (hwnd) {
3538 hdc = GetDC(hwnd);
3539 if (hdc && pal)
3540 SelectPalette(hdc, pal, FALSE);
3541 return hdc;
3542 } else
3543 return NULL;
3544}
3545
3546void free_ctx(Context ctx)
3547{
3548 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3549 ReleaseDC(hwnd, ctx);
3550}
3551
3552static void real_palette_set(int n, int r, int g, int b)
3553{
3554 if (pal) {
3555 logpal->palPalEntry[n].peRed = r;
3556 logpal->palPalEntry[n].peGreen = g;
3557 logpal->palPalEntry[n].peBlue = b;
3558 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3559 colours[n] = PALETTERGB(r, g, b);
3560 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3561 } else
3562 colours[n] = RGB(r, g, b);
3563}
3564
3565void palette_set(int n, int r, int g, int b)
3566{
3567 static const int first[21] = {
3568 0, 2, 4, 6, 8, 10, 12, 14,
3569 1, 3, 5, 7, 9, 11, 13, 15,
3570 16, 17, 18, 20, 22
3571 };
3572 real_palette_set(first[n], r, g, b);
3573 if (first[n] >= 18)
3574 real_palette_set(first[n] + 1, r, g, b);
3575 if (pal) {
3576 HDC hdc = get_ctx();
3577 UnrealizeObject(pal);
3578 RealizePalette(hdc);
3579 free_ctx(hdc);
3580 }
3581}
3582
3583void palette_reset(void)
3584{
3585 int i;
3586
3587 for (i = 0; i < NCOLOURS; i++) {
3588 if (pal) {
3589 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3590 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3591 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3592 logpal->palPalEntry[i].peFlags = 0;
3593 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3594 defpal[i].rgbtGreen,
3595 defpal[i].rgbtBlue);
3596 } else
3597 colours[i] = RGB(defpal[i].rgbtRed,
3598 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3599 }
3600
3601 if (pal) {
3602 HDC hdc;
3603 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3604 hdc = get_ctx();
3605 RealizePalette(hdc);
3606 free_ctx(hdc);
3607 }
3608}
3609
3610void write_aclip(char *data, int len, int must_deselect)
3611{
3612 HGLOBAL clipdata;
3613 void *lock;
3614
3615 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3616 if (!clipdata)
3617 return;
3618 lock = GlobalLock(clipdata);
3619 if (!lock)
3620 return;
3621 memcpy(lock, data, len);
3622 ((unsigned char *) lock)[len] = 0;
3623 GlobalUnlock(clipdata);
3624
3625 if (!must_deselect)
3626 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3627
3628 if (OpenClipboard(hwnd)) {
3629 EmptyClipboard();
3630 SetClipboardData(CF_TEXT, clipdata);
3631 CloseClipboard();
3632 } else
3633 GlobalFree(clipdata);
3634
3635 if (!must_deselect)
3636 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3637}
3638
3639/*
3640 * Note: unlike write_aclip() this will not append a nul.
3641 */
3642void write_clip(wchar_t * data, int len, int must_deselect)
3643{
3644 HGLOBAL clipdata;
3645 HGLOBAL clipdata2;
3646 int len2;
3647 void *lock, *lock2;
3648
3649 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3650
3651 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3652 len * sizeof(wchar_t));
3653 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3654
3655 if (!clipdata || !clipdata2) {
3656 if (clipdata)
3657 GlobalFree(clipdata);
3658 if (clipdata2)
3659 GlobalFree(clipdata2);
3660 return;
3661 }
3662 if (!(lock = GlobalLock(clipdata)))
3663 return;
3664 if (!(lock2 = GlobalLock(clipdata2)))
3665 return;
3666
3667 memcpy(lock, data, len * sizeof(wchar_t));
3668 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3669
3670 GlobalUnlock(clipdata);
3671 GlobalUnlock(clipdata2);
3672
3673 if (!must_deselect)
3674 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3675
3676 if (OpenClipboard(hwnd)) {
3677 EmptyClipboard();
3678 SetClipboardData(CF_UNICODETEXT, clipdata);
3679 SetClipboardData(CF_TEXT, clipdata2);
3680 CloseClipboard();
3681 } else {
3682 GlobalFree(clipdata);
3683 GlobalFree(clipdata2);
3684 }
3685
3686 if (!must_deselect)
3687 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3688}
3689
3690void get_clip(wchar_t ** p, int *len)
3691{
3692 static HGLOBAL clipdata = NULL;
3693 static wchar_t *converted = 0;
3694 wchar_t *p2;
3695
3696 if (converted) {
3697 sfree(converted);
3698 converted = 0;
3699 }
3700 if (!p) {
3701 if (clipdata)
3702 GlobalUnlock(clipdata);
3703 clipdata = NULL;
3704 return;
3705 } else if (OpenClipboard(NULL)) {
3706 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3707 CloseClipboard();
3708 *p = GlobalLock(clipdata);
3709 if (*p) {
3710 for (p2 = *p; *p2; p2++);
3711 *len = p2 - *p;
3712 return;
3713 }
3714 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3715 char *s;
3716 int i;
3717 CloseClipboard();
3718 s = GlobalLock(clipdata);
3719 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3720 *p = converted = smalloc(i * sizeof(wchar_t));
3721 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3722 *len = i - 1;
3723 return;
3724 } else
3725 CloseClipboard();
3726 }
3727
3728 *p = NULL;
3729 *len = 0;
3730}
3731
3732#if 0
3733/*
3734 * Move `lines' lines from position `from' to position `to' in the
3735 * window.
3736 */
3737void optimised_move(int to, int from, int lines)
3738{
3739 RECT r;
3740 int min, max;
3741
3742 min = (to < from ? to : from);
3743 max = to + from - min;
3744
3745 r.left = offset_width;
3746 r.right = offset_width + cols * font_width;
3747 r.top = offset_height + min * font_height;
3748 r.bottom = offset_height + (max + lines) * font_height;
3749 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3750}
3751#endif
3752
3753/*
3754 * Print a message box and perform a fatal exit.
3755 */
3756void fatalbox(char *fmt, ...)
3757{
3758 va_list ap;
3759 char stuff[200];
3760
3761 va_start(ap, fmt);
3762 vsprintf(stuff, fmt, ap);
3763 va_end(ap);
3764 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3765 exit(1);
3766}
3767
3768/*
3769 * Manage window caption / taskbar flashing, if enabled.
3770 * 0 = stop, 1 = maintain, 2 = start
3771 */
3772static void flash_window(int mode)
3773{
3774 static long last_flash = 0;
3775 static int flashing = 0;
3776 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3777 /* stop */
3778 if (flashing) {
3779 FlashWindow(hwnd, FALSE);
3780 flashing = 0;
3781 }
3782
3783 } else if (mode == 2) {
3784 /* start */
3785 if (!flashing) {
3786 last_flash = GetTickCount();
3787 flashing = 1;
3788 FlashWindow(hwnd, TRUE);
3789 }
3790
3791 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3792 /* maintain */
3793 if (flashing) {
3794 long now = GetTickCount();
3795 long fdiff = now - last_flash;
3796 if (fdiff < 0 || fdiff > 450) {
3797 last_flash = now;
3798 FlashWindow(hwnd, TRUE); /* toggle */
3799 }
3800 }
3801 }
3802}
3803
3804/*
3805 * Beep.
3806 */
3807void beep(int mode)
3808{
3809 if (mode == BELL_DEFAULT) {
3810 /*
3811 * For MessageBeep style bells, we want to be careful of
3812 * timing, because they don't have the nice property of
3813 * PlaySound bells that each one cancels the previous
3814 * active one. So we limit the rate to one per 50ms or so.
3815 */
3816 static long lastbeep = 0;
3817 long beepdiff;
3818
3819 beepdiff = GetTickCount() - lastbeep;
3820 if (beepdiff >= 0 && beepdiff < 50)
3821 return;
3822 MessageBeep(MB_OK);
3823 /*
3824 * The above MessageBeep call takes time, so we record the
3825 * time _after_ it finishes rather than before it starts.
3826 */
3827 lastbeep = GetTickCount();
3828 } else if (mode == BELL_WAVEFILE) {
3829 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3830 char buf[sizeof(cfg.bell_wavefile) + 80];
3831 sprintf(buf, "Unable to play sound file\n%s\n"
3832 "Using default sound instead", cfg.bell_wavefile);
3833 MessageBox(hwnd, buf, "PuTTY Sound Error",
3834 MB_OK | MB_ICONEXCLAMATION);
3835 cfg.beep = BELL_DEFAULT;
3836 }
3837 }
3838 /* Otherwise, either visual bell or disabled; do nothing here */
3839 if (!has_focus) {
3840 flash_window(2); /* start */
3841 }
3842}
3843
3844/*
3845 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3846 * implementation.
3847 * Revised by <wez@thebrainroom.com>
3848 */
3849static void flip_full_screen(void)
3850{
3851 want_full_screen = !want_full_screen;
3852
3853 if (full_screen == want_full_screen)
3854 return;
3855
3856 full_screen = want_full_screen;
3857
3858 old_wind_placement.length = sizeof(old_wind_placement);
3859
3860 if (full_screen) {
3861 int x, y, cx, cy;
3862#if !defined(NO_MULTIMON) && defined(MONITOR_DEFAULTTONEAREST)
3863 /* The multi-monitor safe way of doing things */
3864 HMONITOR mon;
3865 MONITORINFO mi;
3866
3867 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
3868 mi.cbSize = sizeof(mi);
3869 GetMonitorInfo(mon, &mi);
3870 x = mi.rcMonitor.left;
3871 y = mi.rcMonitor.top;
3872 cx = mi.rcMonitor.right;
3873 cy = mi.rcMonitor.bottom;
3874#else
3875 /* good old fashioned way of doing it */
3876 x = 0;
3877 y = 0;
3878 cx = GetSystemMetrics(SM_CXSCREEN);
3879 cy = GetSystemMetrics(SM_CYSCREEN);
3880#endif
3881
3882 /* save rows for when we "restore" back down again */
3883 pre_fs_rows = rows;
3884 pre_fs_cols = cols;
3885
3886 GetWindowPlacement(hwnd, &old_wind_placement);
3887 SetWindowLong(hwnd, GWL_STYLE,
3888 GetWindowLong(hwnd, GWL_STYLE)
3889 & ~((cfg.scrollbar_in_fullscreen ? 0 : WS_VSCROLL)
3890 | WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3891 /* become topmost */
3892 SetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_FRAMECHANGED);
3893 } else {
3894 was_full_screen = 1;
3895 SetWindowLong(hwnd, GWL_STYLE,
3896 GetWindowLong(hwnd, GWL_STYLE)
3897 | (cfg.scrollbar ? WS_VSCROLL : 0)
3898 | WS_CAPTION | WS_BORDER | WS_THICKFRAME);
3899 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
3900 SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
3901 SetWindowPlacement(hwnd,&old_wind_placement);
3902 }
3903 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
3904 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
3905}