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