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