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