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