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