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