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