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