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