Fix pasting of newlines in local line editing mode. Possibly not a
[u/mdw/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 * We need not bother about stdin backlogs
2147 * here, because in GUI PuTTY we can't do
2148 * anything about it anyway; there's no means
2149 * of asking Windows to hold off on KEYDOWN
2150 * messages. We _have_ to buffer everything
2151 * we're sent.
2152 */
2153 ldisc_send(buf, len, 1);
2154 show_mouseptr(0);
2155 }
2156 }
2157 }
2158 return 0;
2159 case WM_INPUTLANGCHANGE:
2160 {
2161 /* wParam == Font number */
2162 /* lParam == Locale */
2163 char lbuf[20];
2164 HKL NewInputLocale = (HKL) lParam;
2165
2166 // lParam == GetKeyboardLayout(0);
2167
2168 GetLocaleInfo(LOWORD(NewInputLocale),
2169 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2170
2171 kbd_codepage = atoi(lbuf);
2172 }
2173 break;
2174 case WM_IME_COMPOSITION:
2175 {
2176 HIMC hIMC;
2177 int n;
2178 char *buff;
2179
2180 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2181 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2182
2183 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2184 break; /* fall back to DefWindowProc */
2185
2186 hIMC = ImmGetContext(hwnd);
2187 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2188
2189 if (n > 0) {
2190 buff = (char*) smalloc(n);
2191 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2192 luni_send((unsigned short *)buff, n / 2, 1);
2193 free(buff);
2194 }
2195 ImmReleaseContext(hwnd, hIMC);
2196 return 1;
2197 }
2198
2199 case WM_IME_CHAR:
2200 if (wParam & 0xFF00) {
2201 unsigned char buf[2];
2202
2203 buf[1] = wParam;
2204 buf[0] = wParam >> 8;
2205 lpage_send(kbd_codepage, buf, 2, 1);
2206 } else {
2207 char c = (unsigned char) wParam;
2208 lpage_send(kbd_codepage, &c, 1, 1);
2209 }
2210 return (0);
2211 case WM_CHAR:
2212 case WM_SYSCHAR:
2213 /*
2214 * Nevertheless, we are prepared to deal with WM_CHAR
2215 * messages, should they crop up. So if someone wants to
2216 * post the things to us as part of a macro manoeuvre,
2217 * we're ready to cope.
2218 */
2219 {
2220 char c = (unsigned char)wParam;
2221 lpage_send(CP_ACP, &c, 1, 1);
2222 }
2223 return 0;
2224 case WM_SETCURSOR:
2225 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2226 SetCursor(LoadCursor(NULL, IDC_ARROW));
2227 return TRUE;
2228 }
2229 }
2230
2231 return DefWindowProc(hwnd, message, wParam, lParam);
2232 }
2233
2234 /*
2235 * Move the system caret. (We maintain one, even though it's
2236 * invisible, for the benefit of blind people: apparently some
2237 * helper software tracks the system caret, so we should arrange to
2238 * have one.)
2239 */
2240 void sys_cursor(int x, int y)
2241 {
2242 COMPOSITIONFORM cf;
2243 HIMC hIMC;
2244
2245 if (!has_focus) return;
2246
2247 SetCaretPos(x * font_width + offset_width,
2248 y * font_height + offset_height);
2249
2250 /* IMM calls on Win98 and beyond only */
2251 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2252
2253 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2254 osVersion.dwMinorVersion == 0) return; /* 95 */
2255
2256 /* we should have the IMM functions */
2257 hIMC = ImmGetContext(hwnd);
2258 cf.dwStyle = CFS_POINT;
2259 cf.ptCurrentPos.x = x * font_width;
2260 cf.ptCurrentPos.y = y * font_height;
2261 ImmSetCompositionWindow(hIMC, &cf);
2262
2263 ImmReleaseContext(hwnd, hIMC);
2264 }
2265
2266 /*
2267 * Draw a line of text in the window, at given character
2268 * coordinates, in given attributes.
2269 *
2270 * We are allowed to fiddle with the contents of `text'.
2271 */
2272 void do_text(Context ctx, int x, int y, char *text, int len,
2273 unsigned long attr, int lattr)
2274 {
2275 COLORREF fg, bg, t;
2276 int nfg, nbg, nfont;
2277 HDC hdc = ctx;
2278 RECT line_box;
2279 int force_manual_underline = 0;
2280 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2281 int char_width = fnt_width;
2282 int text_adjust = 0;
2283 static int *IpDx = 0, IpDxLEN = 0;
2284
2285 if (attr & ATTR_WIDE)
2286 char_width *= 2;
2287
2288 if (len > IpDxLEN || IpDx[0] != char_width) {
2289 int i;
2290 if (len > IpDxLEN) {
2291 sfree(IpDx);
2292 IpDx = smalloc((len + 16) * sizeof(int));
2293 IpDxLEN = (len + 16);
2294 }
2295 for (i = 0; i < IpDxLEN; i++)
2296 IpDx[i] = char_width;
2297 }
2298
2299 /* Only want the left half of double width lines */
2300 if (lattr != LATTR_NORM && x*2 >= cols)
2301 return;
2302
2303 x *= fnt_width;
2304 y *= font_height;
2305 x += offset_width;
2306 y += offset_height;
2307
2308 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2309 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2310 attr ^= ATTR_CUR_XOR;
2311 }
2312
2313 nfont = 0;
2314 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2315 /* Assume a poorman font is borken in other ways too. */
2316 lattr = LATTR_WIDE;
2317 } else
2318 switch (lattr) {
2319 case LATTR_NORM:
2320 break;
2321 case LATTR_WIDE:
2322 nfont |= FONT_WIDE;
2323 break;
2324 default:
2325 nfont |= FONT_WIDE + FONT_HIGH;
2326 break;
2327 }
2328 if (attr & ATTR_NARROW)
2329 nfont |= FONT_NARROW;
2330
2331 /* Special hack for the VT100 linedraw glyphs. */
2332 if ((attr & CSET_MASK) == 0x2300) {
2333 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2334 switch ((unsigned char) (text[0])) {
2335 case 0xBA:
2336 text_adjust = -2 * font_height / 5;
2337 break;
2338 case 0xBB:
2339 text_adjust = -1 * font_height / 5;
2340 break;
2341 case 0xBC:
2342 text_adjust = font_height / 5;
2343 break;
2344 case 0xBD:
2345 text_adjust = 2 * font_height / 5;
2346 break;
2347 }
2348 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2349 text_adjust *= 2;
2350 attr &= ~CSET_MASK;
2351 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2352 attr |= (unitab_xterm['q'] & CSET_MASK);
2353 if (attr & ATTR_UNDER) {
2354 attr &= ~ATTR_UNDER;
2355 force_manual_underline = 1;
2356 }
2357 }
2358 }
2359
2360 /* Anything left as an original character set is unprintable. */
2361 if (DIRECT_CHAR(attr)) {
2362 attr &= ~CSET_MASK;
2363 attr |= 0xFF00;
2364 memset(text, 0xFD, len);
2365 }
2366
2367 /* OEM CP */
2368 if ((attr & CSET_MASK) == ATTR_OEMCP)
2369 nfont |= FONT_OEM;
2370
2371 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2372 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2373 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2374 nfont |= FONT_BOLD;
2375 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2376 nfont |= FONT_UNDERLINE;
2377 another_font(nfont);
2378 if (!fonts[nfont]) {
2379 if (nfont & FONT_UNDERLINE)
2380 force_manual_underline = 1;
2381 /* Don't do the same for manual bold, it could be bad news. */
2382
2383 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2384 }
2385 another_font(nfont);
2386 if (!fonts[nfont])
2387 nfont = FONT_NORMAL;
2388 if (attr & ATTR_REVERSE) {
2389 t = nfg;
2390 nfg = nbg;
2391 nbg = t;
2392 }
2393 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2394 nfg++;
2395 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2396 nbg++;
2397 fg = colours[nfg];
2398 bg = colours[nbg];
2399 SelectObject(hdc, fonts[nfont]);
2400 SetTextColor(hdc, fg);
2401 SetBkColor(hdc, bg);
2402 SetBkMode(hdc, OPAQUE);
2403 line_box.left = x;
2404 line_box.top = y;
2405 line_box.right = x + char_width * len;
2406 line_box.bottom = y + font_height;
2407
2408 /* Only want the left half of double width lines */
2409 if (line_box.right > font_width*cols+offset_width)
2410 line_box.right = font_width*cols+offset_width;
2411
2412 /* We're using a private area for direct to font. (512 chars.) */
2413 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2414 /* Ho Hum, dbcs fonts are a PITA! */
2415 /* To display on W9x I have to convert to UCS */
2416 static wchar_t *uni_buf = 0;
2417 static int uni_len = 0;
2418 int nlen, mptr;
2419 if (len > uni_len) {
2420 sfree(uni_buf);
2421 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2422 }
2423
2424 for(nlen = mptr = 0; mptr<len; mptr++) {
2425 uni_buf[nlen] = 0xFFFD;
2426 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2427 IpDx[nlen] += char_width;
2428 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2429 text+mptr, 2, uni_buf+nlen, 1);
2430 mptr++;
2431 }
2432 else
2433 {
2434 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2435 text+mptr, 1, uni_buf+nlen, 1);
2436 }
2437 nlen++;
2438 }
2439 if (nlen <= 0)
2440 return; /* Eeek! */
2441
2442 ExtTextOutW(hdc, x,
2443 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2444 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2445 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2446 SetBkMode(hdc, TRANSPARENT);
2447 ExtTextOutW(hdc, x - 1,
2448 y - font_height * (lattr ==
2449 LATTR_BOT) + text_adjust,
2450 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2451 }
2452
2453 IpDx[0] = -1;
2454 } else if (DIRECT_FONT(attr)) {
2455 ExtTextOut(hdc, x,
2456 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2457 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2458 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2459 SetBkMode(hdc, TRANSPARENT);
2460
2461 /* GRR: This draws the character outside it's box and can leave
2462 * 'droppings' even with the clip box! I suppose I could loop it
2463 * one character at a time ... yuk.
2464 *
2465 * Or ... I could do a test print with "W", and use +1 or -1 for this
2466 * shift depending on if the leftmost column is blank...
2467 */
2468 ExtTextOut(hdc, x - 1,
2469 y - font_height * (lattr ==
2470 LATTR_BOT) + text_adjust,
2471 ETO_CLIPPED, &line_box, text, len, IpDx);
2472 }
2473 } else {
2474 /* And 'normal' unicode characters */
2475 static WCHAR *wbuf = NULL;
2476 static int wlen = 0;
2477 int i;
2478 if (wlen < len) {
2479 sfree(wbuf);
2480 wlen = len;
2481 wbuf = smalloc(wlen * sizeof(WCHAR));
2482 }
2483 for (i = 0; i < len; i++)
2484 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2485
2486 ExtTextOutW(hdc, x,
2487 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2488 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2489
2490 /* And the shadow bold hack. */
2491 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2492 SetBkMode(hdc, TRANSPARENT);
2493 ExtTextOutW(hdc, x - 1,
2494 y - font_height * (lattr ==
2495 LATTR_BOT) + text_adjust,
2496 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2497 }
2498 }
2499 if (lattr != LATTR_TOP && (force_manual_underline ||
2500 (und_mode == UND_LINE
2501 && (attr & ATTR_UNDER)))) {
2502 HPEN oldpen;
2503 int dec = descent;
2504 if (lattr == LATTR_BOT)
2505 dec = dec * 2 - font_height;
2506
2507 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2508 MoveToEx(hdc, x, y + dec, NULL);
2509 LineTo(hdc, x + len * char_width, y + dec);
2510 oldpen = SelectObject(hdc, oldpen);
2511 DeleteObject(oldpen);
2512 }
2513 }
2514
2515 void do_cursor(Context ctx, int x, int y, char *text, int len,
2516 unsigned long attr, int lattr)
2517 {
2518
2519 int fnt_width;
2520 int char_width;
2521 HDC hdc = ctx;
2522 int ctype = cfg.cursor_type;
2523
2524 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2525 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2526 do_text(ctx, x, y, text, len, attr, lattr);
2527 return;
2528 }
2529 ctype = 2;
2530 attr |= TATTR_RIGHTCURS;
2531 }
2532
2533 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2534 if (attr & ATTR_WIDE)
2535 char_width *= 2;
2536 x *= fnt_width;
2537 y *= font_height;
2538 x += offset_width;
2539 y += offset_height;
2540
2541 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2542 POINT pts[5];
2543 HPEN oldpen;
2544 pts[0].x = pts[1].x = pts[4].x = x;
2545 pts[2].x = pts[3].x = x + char_width - 1;
2546 pts[0].y = pts[3].y = pts[4].y = y;
2547 pts[1].y = pts[2].y = y + font_height - 1;
2548 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2549 Polyline(hdc, pts, 5);
2550 oldpen = SelectObject(hdc, oldpen);
2551 DeleteObject(oldpen);
2552 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2553 int startx, starty, dx, dy, length, i;
2554 if (ctype == 1) {
2555 startx = x;
2556 starty = y + descent;
2557 dx = 1;
2558 dy = 0;
2559 length = char_width;
2560 } else {
2561 int xadjust = 0;
2562 if (attr & TATTR_RIGHTCURS)
2563 xadjust = char_width - 1;
2564 startx = x + xadjust;
2565 starty = y;
2566 dx = 0;
2567 dy = 1;
2568 length = font_height;
2569 }
2570 if (attr & TATTR_ACTCURS) {
2571 HPEN oldpen;
2572 oldpen =
2573 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2574 MoveToEx(hdc, startx, starty, NULL);
2575 LineTo(hdc, startx + dx * length, starty + dy * length);
2576 oldpen = SelectObject(hdc, oldpen);
2577 DeleteObject(oldpen);
2578 } else {
2579 for (i = 0; i < length; i++) {
2580 if (i % 2 == 0) {
2581 SetPixel(hdc, startx, starty, colours[23]);
2582 }
2583 startx += dx;
2584 starty += dy;
2585 }
2586 }
2587 }
2588 }
2589
2590 /* This function gets the actual width of a character in the normal font.
2591 */
2592 int CharWidth(Context ctx, int uc) {
2593 HDC hdc = ctx;
2594 int ibuf = 0;
2595
2596 /* If the font max is the same as the font ave width then this
2597 * function is a no-op.
2598 */
2599 if (!font_dualwidth) return 1;
2600
2601 switch (uc & CSET_MASK) {
2602 case ATTR_ASCII:
2603 uc = unitab_line[uc & 0xFF];
2604 break;
2605 case ATTR_LINEDRW:
2606 uc = unitab_xterm[uc & 0xFF];
2607 break;
2608 case ATTR_SCOACS:
2609 uc = unitab_scoacs[uc & 0xFF];
2610 break;
2611 }
2612 if (DIRECT_FONT(uc)) {
2613 if (dbcs_screenfont) return 1;
2614
2615 /* Speedup, I know of no font where ascii is the wrong width */
2616 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2617 return 1;
2618
2619 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2620 SelectObject(hdc, fonts[FONT_NORMAL]);
2621 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2622 another_font(FONT_OEM);
2623 if (!fonts[FONT_OEM]) return 0;
2624
2625 SelectObject(hdc, fonts[FONT_OEM]);
2626 } else
2627 return 0;
2628
2629 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2630 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2631 return 0;
2632 } else {
2633 /* Speedup, I know of no font where ascii is the wrong width */
2634 if (uc >= ' ' && uc <= '~') return 1;
2635
2636 SelectObject(hdc, fonts[FONT_NORMAL]);
2637 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2638 /* Okay that one worked */ ;
2639 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2640 /* This should work on 9x too, but it's "less accurate" */ ;
2641 else
2642 return 0;
2643 }
2644
2645 ibuf += font_width / 2 -1;
2646 ibuf /= font_width;
2647
2648 return ibuf;
2649 }
2650
2651 /*
2652 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2653 * codes. Returns number of bytes used or zero to drop the message
2654 * or -1 to forward the message to windows.
2655 */
2656 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2657 unsigned char *output)
2658 {
2659 BYTE keystate[256];
2660 int scan, left_alt = 0, key_down, shift_state;
2661 int r, i, code;
2662 unsigned char *p = output;
2663 static int alt_sum = 0;
2664
2665 HKL kbd_layout = GetKeyboardLayout(0);
2666
2667 static WORD keys[3];
2668 static int compose_char = 0;
2669 static WPARAM compose_key = 0;
2670
2671 r = GetKeyboardState(keystate);
2672 if (!r)
2673 memset(keystate, 0, sizeof(keystate));
2674 else {
2675 #if 0
2676 #define SHOW_TOASCII_RESULT
2677 { /* Tell us all about key events */
2678 static BYTE oldstate[256];
2679 static int first = 1;
2680 static int scan;
2681 int ch;
2682 if (first)
2683 memcpy(oldstate, keystate, sizeof(oldstate));
2684 first = 0;
2685
2686 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2687 debug(("+"));
2688 } else if ((HIWORD(lParam) & KF_UP)
2689 && scan == (HIWORD(lParam) & 0xFF)) {
2690 debug((". U"));
2691 } else {
2692 debug((".\n"));
2693 if (wParam >= VK_F1 && wParam <= VK_F20)
2694 debug(("K_F%d", wParam + 1 - VK_F1));
2695 else
2696 switch (wParam) {
2697 case VK_SHIFT:
2698 debug(("SHIFT"));
2699 break;
2700 case VK_CONTROL:
2701 debug(("CTRL"));
2702 break;
2703 case VK_MENU:
2704 debug(("ALT"));
2705 break;
2706 default:
2707 debug(("VK_%02x", wParam));
2708 }
2709 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2710 debug(("*"));
2711 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2712
2713 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2714 if (ch >= ' ' && ch <= '~')
2715 debug((", '%c'", ch));
2716 else if (ch)
2717 debug((", $%02x", ch));
2718
2719 if (keys[0])
2720 debug((", KB0=%02x", keys[0]));
2721 if (keys[1])
2722 debug((", KB1=%02x", keys[1]));
2723 if (keys[2])
2724 debug((", KB2=%02x", keys[2]));
2725
2726 if ((keystate[VK_SHIFT] & 0x80) != 0)
2727 debug((", S"));
2728 if ((keystate[VK_CONTROL] & 0x80) != 0)
2729 debug((", C"));
2730 if ((HIWORD(lParam) & KF_EXTENDED))
2731 debug((", E"));
2732 if ((HIWORD(lParam) & KF_UP))
2733 debug((", U"));
2734 }
2735
2736 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2737 else if ((HIWORD(lParam) & KF_UP))
2738 oldstate[wParam & 0xFF] ^= 0x80;
2739 else
2740 oldstate[wParam & 0xFF] ^= 0x81;
2741
2742 for (ch = 0; ch < 256; ch++)
2743 if (oldstate[ch] != keystate[ch])
2744 debug((", M%02x=%02x", ch, keystate[ch]));
2745
2746 memcpy(oldstate, keystate, sizeof(oldstate));
2747 }
2748 #endif
2749
2750 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2751 keystate[VK_RMENU] = keystate[VK_MENU];
2752 }
2753
2754
2755 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
2756 if ((cfg.funky_type == 3 ||
2757 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2758 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
2759
2760 wParam = VK_EXECUTE;
2761
2762 /* UnToggle NUMLock */
2763 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2764 keystate[VK_NUMLOCK] ^= 1;
2765 }
2766
2767 /* And write back the 'adjusted' state */
2768 SetKeyboardState(keystate);
2769 }
2770
2771 /* Disable Auto repeat if required */
2772 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2773 return 0;
2774
2775 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
2776 left_alt = 1;
2777
2778 key_down = ((HIWORD(lParam) & KF_UP) == 0);
2779
2780 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
2781 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
2782 if (cfg.ctrlaltkeys)
2783 keystate[VK_MENU] = 0;
2784 else {
2785 keystate[VK_RMENU] = 0x80;
2786 left_alt = 0;
2787 }
2788 }
2789
2790 alt_pressed = (left_alt && key_down);
2791
2792 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
2793 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2794 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
2795
2796 /* Note if AltGr was pressed and if it was used as a compose key */
2797 if (!compose_state) {
2798 compose_key = 0x100;
2799 if (cfg.compose_key) {
2800 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
2801 compose_key = wParam;
2802 }
2803 if (wParam == VK_APPS)
2804 compose_key = wParam;
2805 }
2806
2807 if (wParam == compose_key) {
2808 if (compose_state == 0
2809 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2810 1;
2811 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
2812 compose_state = 2;
2813 else
2814 compose_state = 0;
2815 } else if (compose_state == 1 && wParam != VK_CONTROL)
2816 compose_state = 0;
2817
2818 /*
2819 * Record that we pressed key so the scroll window can be reset, but
2820 * be careful to avoid Shift-UP/Down
2821 */
2822 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2823 seen_key_event = 1;
2824 }
2825
2826 /* Make sure we're not pasting */
2827 if (key_down)
2828 term_nopaste();
2829
2830 if (compose_state > 1 && left_alt)
2831 compose_state = 0;
2832
2833 /* Sanitize the number pad if not using a PC NumPad */
2834 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2835 && cfg.funky_type != 2)
2836 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2837 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
2838 int nParam = 0;
2839 switch (wParam) {
2840 case VK_INSERT:
2841 nParam = VK_NUMPAD0;
2842 break;
2843 case VK_END:
2844 nParam = VK_NUMPAD1;
2845 break;
2846 case VK_DOWN:
2847 nParam = VK_NUMPAD2;
2848 break;
2849 case VK_NEXT:
2850 nParam = VK_NUMPAD3;
2851 break;
2852 case VK_LEFT:
2853 nParam = VK_NUMPAD4;
2854 break;
2855 case VK_CLEAR:
2856 nParam = VK_NUMPAD5;
2857 break;
2858 case VK_RIGHT:
2859 nParam = VK_NUMPAD6;
2860 break;
2861 case VK_HOME:
2862 nParam = VK_NUMPAD7;
2863 break;
2864 case VK_UP:
2865 nParam = VK_NUMPAD8;
2866 break;
2867 case VK_PRIOR:
2868 nParam = VK_NUMPAD9;
2869 break;
2870 case VK_DELETE:
2871 nParam = VK_DECIMAL;
2872 break;
2873 }
2874 if (nParam) {
2875 if (keystate[VK_NUMLOCK] & 1)
2876 shift_state |= 1;
2877 wParam = nParam;
2878 }
2879 }
2880 }
2881
2882 /* If a key is pressed and AltGr is not active */
2883 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2884 /* Okay, prepare for most alts then ... */
2885 if (left_alt)
2886 *p++ = '\033';
2887
2888 /* Lets see if it's a pattern we know all about ... */
2889 if (wParam == VK_PRIOR && shift_state == 1) {
2890 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2891 return 0;
2892 }
2893 if (wParam == VK_NEXT && shift_state == 1) {
2894 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2895 return 0;
2896 }
2897 if (wParam == VK_INSERT && shift_state == 1) {
2898 term_do_paste();
2899 return 0;
2900 }
2901 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
2902 return -1;
2903 }
2904 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
2905 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2906 return -1;
2907 }
2908 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2909 flip_full_screen();
2910 return -1;
2911 }
2912 /* Control-Numlock for app-keypad mode switch */
2913 if (wParam == VK_PAUSE && shift_state == 2) {
2914 app_keypad_keys ^= 1;
2915 return 0;
2916 }
2917
2918 /* Nethack keypad */
2919 if (cfg.nethack_keypad && !left_alt) {
2920 switch (wParam) {
2921 case VK_NUMPAD1:
2922 *p++ = shift_state ? 'B' : 'b';
2923 return p - output;
2924 case VK_NUMPAD2:
2925 *p++ = shift_state ? 'J' : 'j';
2926 return p - output;
2927 case VK_NUMPAD3:
2928 *p++ = shift_state ? 'N' : 'n';
2929 return p - output;
2930 case VK_NUMPAD4:
2931 *p++ = shift_state ? 'H' : 'h';
2932 return p - output;
2933 case VK_NUMPAD5:
2934 *p++ = shift_state ? '.' : '.';
2935 return p - output;
2936 case VK_NUMPAD6:
2937 *p++ = shift_state ? 'L' : 'l';
2938 return p - output;
2939 case VK_NUMPAD7:
2940 *p++ = shift_state ? 'Y' : 'y';
2941 return p - output;
2942 case VK_NUMPAD8:
2943 *p++ = shift_state ? 'K' : 'k';
2944 return p - output;
2945 case VK_NUMPAD9:
2946 *p++ = shift_state ? 'U' : 'u';
2947 return p - output;
2948 }
2949 }
2950
2951 /* Application Keypad */
2952 if (!left_alt) {
2953 int xkey = 0;
2954
2955 if (cfg.funky_type == 3 ||
2956 (cfg.funky_type <= 1 &&
2957 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2958 case VK_EXECUTE:
2959 xkey = 'P';
2960 break;
2961 case VK_DIVIDE:
2962 xkey = 'Q';
2963 break;
2964 case VK_MULTIPLY:
2965 xkey = 'R';
2966 break;
2967 case VK_SUBTRACT:
2968 xkey = 'S';
2969 break;
2970 }
2971 if (app_keypad_keys && !cfg.no_applic_k)
2972 switch (wParam) {
2973 case VK_NUMPAD0:
2974 xkey = 'p';
2975 break;
2976 case VK_NUMPAD1:
2977 xkey = 'q';
2978 break;
2979 case VK_NUMPAD2:
2980 xkey = 'r';
2981 break;
2982 case VK_NUMPAD3:
2983 xkey = 's';
2984 break;
2985 case VK_NUMPAD4:
2986 xkey = 't';
2987 break;
2988 case VK_NUMPAD5:
2989 xkey = 'u';
2990 break;
2991 case VK_NUMPAD6:
2992 xkey = 'v';
2993 break;
2994 case VK_NUMPAD7:
2995 xkey = 'w';
2996 break;
2997 case VK_NUMPAD8:
2998 xkey = 'x';
2999 break;
3000 case VK_NUMPAD9:
3001 xkey = 'y';
3002 break;
3003
3004 case VK_DECIMAL:
3005 xkey = 'n';
3006 break;
3007 case VK_ADD:
3008 if (cfg.funky_type == 2) {
3009 if (shift_state)
3010 xkey = 'l';
3011 else
3012 xkey = 'k';
3013 } else if (shift_state)
3014 xkey = 'm';
3015 else
3016 xkey = 'l';
3017 break;
3018
3019 case VK_DIVIDE:
3020 if (cfg.funky_type == 2)
3021 xkey = 'o';
3022 break;
3023 case VK_MULTIPLY:
3024 if (cfg.funky_type == 2)
3025 xkey = 'j';
3026 break;
3027 case VK_SUBTRACT:
3028 if (cfg.funky_type == 2)
3029 xkey = 'm';
3030 break;
3031
3032 case VK_RETURN:
3033 if (HIWORD(lParam) & KF_EXTENDED)
3034 xkey = 'M';
3035 break;
3036 }
3037 if (xkey) {
3038 if (vt52_mode) {
3039 if (xkey >= 'P' && xkey <= 'S')
3040 p += sprintf((char *) p, "\x1B%c", xkey);
3041 else
3042 p += sprintf((char *) p, "\x1B?%c", xkey);
3043 } else
3044 p += sprintf((char *) p, "\x1BO%c", xkey);
3045 return p - output;
3046 }
3047 }
3048
3049 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3050 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3051 *p++ = 0;
3052 return -2;
3053 }
3054 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3055 *p++ = 0x1B;
3056 *p++ = '[';
3057 *p++ = 'Z';
3058 return p - output;
3059 }
3060 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3061 *p++ = 0;
3062 return p - output;
3063 }
3064 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3065 *p++ = 160;
3066 return p - output;
3067 }
3068 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3069 *p++ = 3;
3070 *p++ = 0;
3071 return -2;
3072 }
3073 if (wParam == VK_PAUSE) { /* Break/Pause */
3074 *p++ = 26;
3075 *p++ = 0;
3076 return -2;
3077 }
3078 /* Control-2 to Control-8 are special */
3079 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3080 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3081 return p - output;
3082 }
3083 if (shift_state == 2 && wParam == 0xBD) {
3084 *p++ = 0x1F;
3085 return p - output;
3086 }
3087 if (shift_state == 2 && wParam == 0xDF) {
3088 *p++ = 0x1C;
3089 return p - output;
3090 }
3091 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3092 *p++ = '\r';
3093 *p++ = '\n';
3094 return p - output;
3095 }
3096
3097 /*
3098 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3099 * for integer decimal nn.)
3100 *
3101 * We also deal with the weird ones here. Linux VCs replace F1
3102 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3103 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3104 * respectively.
3105 */
3106 code = 0;
3107 switch (wParam) {
3108 case VK_F1:
3109 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3110 break;
3111 case VK_F2:
3112 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3113 break;
3114 case VK_F3:
3115 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3116 break;
3117 case VK_F4:
3118 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3119 break;
3120 case VK_F5:
3121 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3122 break;
3123 case VK_F6:
3124 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3125 break;
3126 case VK_F7:
3127 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3128 break;
3129 case VK_F8:
3130 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3131 break;
3132 case VK_F9:
3133 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3134 break;
3135 case VK_F10:
3136 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3137 break;
3138 case VK_F11:
3139 code = 23;
3140 break;
3141 case VK_F12:
3142 code = 24;
3143 break;
3144 case VK_F13:
3145 code = 25;
3146 break;
3147 case VK_F14:
3148 code = 26;
3149 break;
3150 case VK_F15:
3151 code = 28;
3152 break;
3153 case VK_F16:
3154 code = 29;
3155 break;
3156 case VK_F17:
3157 code = 31;
3158 break;
3159 case VK_F18:
3160 code = 32;
3161 break;
3162 case VK_F19:
3163 code = 33;
3164 break;
3165 case VK_F20:
3166 code = 34;
3167 break;
3168 }
3169 if ((shift_state&2) == 0) switch (wParam) {
3170 case VK_HOME:
3171 code = 1;
3172 break;
3173 case VK_INSERT:
3174 code = 2;
3175 break;
3176 case VK_DELETE:
3177 code = 3;
3178 break;
3179 case VK_END:
3180 code = 4;
3181 break;
3182 case VK_PRIOR:
3183 code = 5;
3184 break;
3185 case VK_NEXT:
3186 code = 6;
3187 break;
3188 }
3189 /* Reorder edit keys to physical order */
3190 if (cfg.funky_type == 3 && code <= 6)
3191 code = "\0\2\1\4\5\3\6"[code];
3192
3193 if (vt52_mode && code > 0 && code <= 6) {
3194 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3195 return p - output;
3196 }
3197
3198 if (cfg.funky_type == 5 && /* SCO function keys */
3199 code >= 11 && code <= 34) {
3200 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3201 int index = 0;
3202 switch (wParam) {
3203 case VK_F1: index = 0; break;
3204 case VK_F2: index = 1; break;
3205 case VK_F3: index = 2; break;
3206 case VK_F4: index = 3; break;
3207 case VK_F5: index = 4; break;
3208 case VK_F6: index = 5; break;
3209 case VK_F7: index = 6; break;
3210 case VK_F8: index = 7; break;
3211 case VK_F9: index = 8; break;
3212 case VK_F10: index = 9; break;
3213 case VK_F11: index = 10; break;
3214 case VK_F12: index = 11; break;
3215 }
3216 if (keystate[VK_SHIFT] & 0x80) index += 12;
3217 if (keystate[VK_CONTROL] & 0x80) index += 24;
3218 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3219 return p - output;
3220 }
3221 if (cfg.funky_type == 5 && /* SCO small keypad */
3222 code >= 1 && code <= 6) {
3223 char codes[] = "HL.FIG";
3224 if (code == 3) {
3225 *p++ = '\x7F';
3226 } else {
3227 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3228 }
3229 return p - output;
3230 }
3231 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3232 int offt = 0;
3233 if (code > 15)
3234 offt++;
3235 if (code > 21)
3236 offt++;
3237 if (vt52_mode)
3238 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3239 else
3240 p +=
3241 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3242 return p - output;
3243 }
3244 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3245 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3246 return p - output;
3247 }
3248 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3249 if (vt52_mode)
3250 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3251 else
3252 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3253 return p - output;
3254 }
3255 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3256 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3257 return p - output;
3258 }
3259 if (code) {
3260 p += sprintf((char *) p, "\x1B[%d~", code);
3261 return p - output;
3262 }
3263
3264 /*
3265 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3266 * some reason seems to send VK_CLEAR to Windows...).
3267 */
3268 {
3269 char xkey = 0;
3270 switch (wParam) {
3271 case VK_UP:
3272 xkey = 'A';
3273 break;
3274 case VK_DOWN:
3275 xkey = 'B';
3276 break;
3277 case VK_RIGHT:
3278 xkey = 'C';
3279 break;
3280 case VK_LEFT:
3281 xkey = 'D';
3282 break;
3283 case VK_CLEAR:
3284 xkey = 'G';
3285 break;
3286 }
3287 if (xkey) {
3288 if (vt52_mode)
3289 p += sprintf((char *) p, "\x1B%c", xkey);
3290 else {
3291 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3292 /* VT100 & VT102 manuals both state the app cursor keys
3293 * only work if the app keypad is on.
3294 */
3295 if (!app_keypad_keys)
3296 app_flg = 0;
3297 /* Useful mapping of Ctrl-arrows */
3298 if (shift_state == 2)
3299 app_flg = !app_flg;
3300
3301 if (app_flg)
3302 p += sprintf((char *) p, "\x1BO%c", xkey);
3303 else
3304 p += sprintf((char *) p, "\x1B[%c", xkey);
3305 }
3306 return p - output;
3307 }
3308 }
3309
3310 /*
3311 * Finally, deal with Return ourselves. (Win95 seems to
3312 * foul it up when Alt is pressed, for some reason.)
3313 */
3314 if (wParam == VK_RETURN) { /* Return */
3315 *p++ = 0x0D;
3316 *p++ = 0;
3317 return -2;
3318 }
3319
3320 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3321 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3322 else
3323 alt_sum = 0;
3324 }
3325
3326 /* Okay we've done everything interesting; let windows deal with
3327 * the boring stuff */
3328 {
3329 BOOL capsOn=0;
3330
3331 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3332 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3333 capsOn= !left_alt;
3334 keystate[VK_CAPITAL] = 0;
3335 }
3336
3337 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3338 #ifdef SHOW_TOASCII_RESULT
3339 if (r == 1 && !key_down) {
3340 if (alt_sum) {
3341 if (in_utf || dbcs_screenfont)
3342 debug((", (U+%04x)", alt_sum));
3343 else
3344 debug((", LCH(%d)", alt_sum));
3345 } else {
3346 debug((", ACH(%d)", keys[0]));
3347 }
3348 } else if (r > 0) {
3349 int r1;
3350 debug((", ASC("));
3351 for (r1 = 0; r1 < r; r1++) {
3352 debug(("%s%d", r1 ? "," : "", keys[r1]));
3353 }
3354 debug((")"));
3355 }
3356 #endif
3357 if (r > 0) {
3358 WCHAR keybuf;
3359 p = output;
3360 for (i = 0; i < r; i++) {
3361 unsigned char ch = (unsigned char) keys[i];
3362
3363 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3364 compose_char = ch;
3365 compose_state++;
3366 continue;
3367 }
3368 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3369 int nc;
3370 compose_state = 0;
3371
3372 if ((nc = check_compose(compose_char, ch)) == -1) {
3373 MessageBeep(MB_ICONHAND);
3374 return 0;
3375 }
3376 keybuf = nc;
3377 luni_send(&keybuf, 1, 1);
3378 continue;
3379 }
3380
3381 compose_state = 0;
3382
3383 if (!key_down) {
3384 if (alt_sum) {
3385 if (in_utf || dbcs_screenfont) {
3386 keybuf = alt_sum;
3387 luni_send(&keybuf, 1, 1);
3388 } else {
3389 ch = (char) alt_sum;
3390 /*
3391 * We need not bother about stdin
3392 * backlogs here, because in GUI PuTTY
3393 * we can't do anything about it
3394 * anyway; there's no means of asking
3395 * Windows to hold off on KEYDOWN
3396 * messages. We _have_ to buffer
3397 * everything we're sent.
3398 */
3399 ldisc_send(&ch, 1, 1);
3400 }
3401 alt_sum = 0;
3402 } else
3403 lpage_send(kbd_codepage, &ch, 1, 1);
3404 } else {
3405 if(capsOn && ch < 0x80) {
3406 WCHAR cbuf[2];
3407 cbuf[0] = 27;
3408 cbuf[1] = xlat_uskbd2cyrllic(ch);
3409 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3410 } else {
3411 char cbuf[2];
3412 cbuf[0] = '\033';
3413 cbuf[1] = ch;
3414 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3415 }
3416 }
3417 show_mouseptr(0);
3418 }
3419
3420 /* This is so the ALT-Numpad and dead keys work correctly. */
3421 keys[0] = 0;
3422
3423 return p - output;
3424 }
3425 /* If we're definitly not building up an ALT-54321 then clear it */
3426 if (!left_alt)
3427 keys[0] = 0;
3428 /* If we will be using alt_sum fix the 256s */
3429 else if (keys[0] && (in_utf || dbcs_screenfont))
3430 keys[0] = 10;
3431 }
3432
3433 /*
3434 * ALT alone may or may not want to bring up the System menu.
3435 * If it's not meant to, we return 0 on presses or releases of
3436 * ALT, to show that we've swallowed the keystroke. Otherwise
3437 * we return -1, which means Windows will give the keystroke
3438 * its default handling (i.e. bring up the System menu).
3439 */
3440 if (wParam == VK_MENU && !cfg.alt_only)
3441 return 0;
3442
3443 return -1;
3444 }
3445
3446 void set_title(char *title)
3447 {
3448 sfree(window_name);
3449 window_name = smalloc(1 + strlen(title));
3450 strcpy(window_name, title);
3451 if (cfg.win_name_always || !IsIconic(hwnd))
3452 SetWindowText(hwnd, title);
3453 }
3454
3455 void set_icon(char *title)
3456 {
3457 sfree(icon_name);
3458 icon_name = smalloc(1 + strlen(title));
3459 strcpy(icon_name, title);
3460 if (!cfg.win_name_always && IsIconic(hwnd))
3461 SetWindowText(hwnd, title);
3462 }
3463
3464 void set_sbar(int total, int start, int page)
3465 {
3466 SCROLLINFO si;
3467
3468 if (!cfg.scrollbar)
3469 return;
3470
3471 si.cbSize = sizeof(si);
3472 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3473 si.nMin = 0;
3474 si.nMax = total - 1;
3475 si.nPage = page;
3476 si.nPos = start;
3477 if (hwnd)
3478 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3479 }
3480
3481 Context get_ctx(void)
3482 {
3483 HDC hdc;
3484 if (hwnd) {
3485 hdc = GetDC(hwnd);
3486 if (hdc && pal)
3487 SelectPalette(hdc, pal, FALSE);
3488 return hdc;
3489 } else
3490 return NULL;
3491 }
3492
3493 void free_ctx(Context ctx)
3494 {
3495 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3496 ReleaseDC(hwnd, ctx);
3497 }
3498
3499 static void real_palette_set(int n, int r, int g, int b)
3500 {
3501 if (pal) {
3502 logpal->palPalEntry[n].peRed = r;
3503 logpal->palPalEntry[n].peGreen = g;
3504 logpal->palPalEntry[n].peBlue = b;
3505 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3506 colours[n] = PALETTERGB(r, g, b);
3507 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3508 } else
3509 colours[n] = RGB(r, g, b);
3510 }
3511
3512 void palette_set(int n, int r, int g, int b)
3513 {
3514 static const int first[21] = {
3515 0, 2, 4, 6, 8, 10, 12, 14,
3516 1, 3, 5, 7, 9, 11, 13, 15,
3517 16, 17, 18, 20, 22
3518 };
3519 real_palette_set(first[n], r, g, b);
3520 if (first[n] >= 18)
3521 real_palette_set(first[n] + 1, r, g, b);
3522 if (pal) {
3523 HDC hdc = get_ctx();
3524 UnrealizeObject(pal);
3525 RealizePalette(hdc);
3526 free_ctx(hdc);
3527 }
3528 }
3529
3530 void palette_reset(void)
3531 {
3532 int i;
3533
3534 for (i = 0; i < NCOLOURS; i++) {
3535 if (pal) {
3536 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3537 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3538 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3539 logpal->palPalEntry[i].peFlags = 0;
3540 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3541 defpal[i].rgbtGreen,
3542 defpal[i].rgbtBlue);
3543 } else
3544 colours[i] = RGB(defpal[i].rgbtRed,
3545 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3546 }
3547
3548 if (pal) {
3549 HDC hdc;
3550 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3551 hdc = get_ctx();
3552 RealizePalette(hdc);
3553 free_ctx(hdc);
3554 }
3555 }
3556
3557 void write_aclip(char *data, int len, int must_deselect)
3558 {
3559 HGLOBAL clipdata;
3560 void *lock;
3561
3562 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3563 if (!clipdata)
3564 return;
3565 lock = GlobalLock(clipdata);
3566 if (!lock)
3567 return;
3568 memcpy(lock, data, len);
3569 ((unsigned char *) lock)[len] = 0;
3570 GlobalUnlock(clipdata);
3571
3572 if (!must_deselect)
3573 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3574
3575 if (OpenClipboard(hwnd)) {
3576 EmptyClipboard();
3577 SetClipboardData(CF_TEXT, clipdata);
3578 CloseClipboard();
3579 } else
3580 GlobalFree(clipdata);
3581
3582 if (!must_deselect)
3583 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3584 }
3585
3586 /*
3587 * Note: unlike write_aclip() this will not append a nul.
3588 */
3589 void write_clip(wchar_t * data, int len, int must_deselect)
3590 {
3591 HGLOBAL clipdata;
3592 HGLOBAL clipdata2;
3593 int len2;
3594 void *lock, *lock2;
3595
3596 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3597
3598 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3599 len * sizeof(wchar_t));
3600 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3601
3602 if (!clipdata || !clipdata2) {
3603 if (clipdata)
3604 GlobalFree(clipdata);
3605 if (clipdata2)
3606 GlobalFree(clipdata2);
3607 return;
3608 }
3609 if (!(lock = GlobalLock(clipdata)))
3610 return;
3611 if (!(lock2 = GlobalLock(clipdata2)))
3612 return;
3613
3614 memcpy(lock, data, len * sizeof(wchar_t));
3615 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3616
3617 GlobalUnlock(clipdata);
3618 GlobalUnlock(clipdata2);
3619
3620 if (!must_deselect)
3621 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3622
3623 if (OpenClipboard(hwnd)) {
3624 EmptyClipboard();
3625 SetClipboardData(CF_UNICODETEXT, clipdata);
3626 SetClipboardData(CF_TEXT, clipdata2);
3627 CloseClipboard();
3628 } else {
3629 GlobalFree(clipdata);
3630 GlobalFree(clipdata2);
3631 }
3632
3633 if (!must_deselect)
3634 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3635 }
3636
3637 void get_clip(wchar_t ** p, int *len)
3638 {
3639 static HGLOBAL clipdata = NULL;
3640 static wchar_t *converted = 0;
3641 wchar_t *p2;
3642
3643 if (converted) {
3644 sfree(converted);
3645 converted = 0;
3646 }
3647 if (!p) {
3648 if (clipdata)
3649 GlobalUnlock(clipdata);
3650 clipdata = NULL;
3651 return;
3652 } else if (OpenClipboard(NULL)) {
3653 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
3654 CloseClipboard();
3655 *p = GlobalLock(clipdata);
3656 if (*p) {
3657 for (p2 = *p; *p2; p2++);
3658 *len = p2 - *p;
3659 return;
3660 }
3661 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
3662 char *s;
3663 int i;
3664 CloseClipboard();
3665 s = GlobalLock(clipdata);
3666 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3667 *p = converted = smalloc(i * sizeof(wchar_t));
3668 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3669 *len = i - 1;
3670 return;
3671 } else
3672 CloseClipboard();
3673 }
3674
3675 *p = NULL;
3676 *len = 0;
3677 }
3678
3679 #if 0
3680 /*
3681 * Move `lines' lines from position `from' to position `to' in the
3682 * window.
3683 */
3684 void optimised_move(int to, int from, int lines)
3685 {
3686 RECT r;
3687 int min, max;
3688
3689 min = (to < from ? to : from);
3690 max = to + from - min;
3691
3692 r.left = offset_width;
3693 r.right = offset_width + cols * font_width;
3694 r.top = offset_height + min * font_height;
3695 r.bottom = offset_height + (max + lines) * font_height;
3696 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
3697 }
3698 #endif
3699
3700 /*
3701 * Print a message box and perform a fatal exit.
3702 */
3703 void fatalbox(char *fmt, ...)
3704 {
3705 va_list ap;
3706 char stuff[200];
3707
3708 va_start(ap, fmt);
3709 vsprintf(stuff, fmt, ap);
3710 va_end(ap);
3711 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3712 exit(1);
3713 }
3714
3715 /*
3716 * Manage window caption / taskbar flashing, if enabled.
3717 * 0 = stop, 1 = maintain, 2 = start
3718 */
3719 static void flash_window(int mode)
3720 {
3721 static long last_flash = 0;
3722 static int flashing = 0;
3723 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3724 /* stop */
3725 if (flashing) {
3726 FlashWindow(hwnd, FALSE);
3727 flashing = 0;
3728 }
3729
3730 } else if (mode == 2) {
3731 /* start */
3732 if (!flashing) {
3733 last_flash = GetTickCount();
3734 flashing = 1;
3735 FlashWindow(hwnd, TRUE);
3736 }
3737
3738 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3739 /* maintain */
3740 if (flashing) {
3741 long now = GetTickCount();
3742 long fdiff = now - last_flash;
3743 if (fdiff < 0 || fdiff > 450) {
3744 last_flash = now;
3745 FlashWindow(hwnd, TRUE); /* toggle */
3746 }
3747 }
3748 }
3749 }
3750
3751 /*
3752 * Beep.
3753 */
3754 void beep(int mode)
3755 {
3756 if (mode == BELL_DEFAULT) {
3757 /*
3758 * For MessageBeep style bells, we want to be careful of
3759 * timing, because they don't have the nice property of
3760 * PlaySound bells that each one cancels the previous
3761 * active one. So we limit the rate to one per 50ms or so.
3762 */
3763 static long lastbeep = 0;
3764 long beepdiff;
3765
3766 beepdiff = GetTickCount() - lastbeep;
3767 if (beepdiff >= 0 && beepdiff < 50)
3768 return;
3769 MessageBeep(MB_OK);
3770 /*
3771 * The above MessageBeep call takes time, so we record the
3772 * time _after_ it finishes rather than before it starts.
3773 */
3774 lastbeep = GetTickCount();
3775 } else if (mode == BELL_WAVEFILE) {
3776 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
3777 char buf[sizeof(cfg.bell_wavefile) + 80];
3778 sprintf(buf, "Unable to play sound file\n%s\n"
3779 "Using default sound instead", cfg.bell_wavefile);
3780 MessageBox(hwnd, buf, "PuTTY Sound Error",
3781 MB_OK | MB_ICONEXCLAMATION);
3782 cfg.beep = BELL_DEFAULT;
3783 }
3784 }
3785 /* Otherwise, either visual bell or disabled; do nothing here */
3786 if (!has_focus) {
3787 flash_window(2); /* start */
3788 }
3789 }
3790
3791 /*
3792 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3793 * implementation.
3794 */
3795 static void flip_full_screen(void)
3796 {
3797 if (!full_screen) {
3798 int cx, cy;
3799
3800 cx = GetSystemMetrics(SM_CXSCREEN);
3801 cy = GetSystemMetrics(SM_CYSCREEN);
3802 GetWindowPlacement(hwnd, &old_wind_placement);
3803 old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3804 SetWindowLong(hwnd, GWL_STYLE,
3805 old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3806 SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3807 full_screen = 1;
3808 } else {
3809 SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3810 SetWindowPlacement(hwnd,&old_wind_placement);
3811 full_screen = 0;
3812 }
3813 }