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