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