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