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