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