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