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