Add a couple of other sensible button defaults in MessageBox()s.
[u/mdw/putty] / windows / window.c
CommitLineData
374330e2 1#include <stdio.h>
2#include <stdlib.h>
1d470ad2 3#include <ctype.h>
ec55b220 4#include <time.h>
a7419ea4 5#include <assert.h>
374330e2 6
32874aea 7#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 8#include "putty.h"
887035a5 9#include "terminal.h"
d5859615 10#include "storage.h"
374330e2 11#include "win_res.h"
12
7440fd44 13#ifndef NO_MULTIMON
14#if WINVER < 0x0500
15#define COMPILE_MULTIMON_STUBS
16#include <multimon.h>
17#endif
18#endif
19
20#include <imm.h>
21#include <commctrl.h>
22#include <richedit.h>
23#include <mmsystem.h>
24
ce0b6f45 25/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of
26 * wParam are used by Windows, and should be masked off, so we shouldn't
27 * attempt to store information in them. Hence all these identifiers have
865e82ad 28 * the low 4 bits clear. Also, identifiers should < 0xF000. */
ce0b6f45 29
6833a413 30#define IDM_SHOWLOG 0x0010
31#define IDM_NEWSESS 0x0020
32#define IDM_DUPSESS 0x0030
e3be8de5 33#define IDM_RESTART 0x0040
34#define IDM_RECONF 0x0050
35#define IDM_CLRSB 0x0060
36#define IDM_RESET 0x0070
70133c0e 37#define IDM_HELP 0x0140
38#define IDM_ABOUT 0x0150
39#define IDM_SAVEDSESS 0x0160
40#define IDM_COPYALL 0x0170
41#define IDM_FULLSCREEN 0x0180
63be7767 42#define IDM_PASTE 0x0190
6833a413 43
125105d1 44#define IDM_SPECIAL_MIN 0x0400
45#define IDM_SPECIAL_MAX 0x0800
46
6833a413 47#define IDM_SAVED_MIN 0x1000
865e82ad 48#define IDM_SAVED_MAX 0x5000
49#define MENU_SAVED_STEP 16
1f1e3232 50/* Maximum number of sessions on saved-session submenu */
865e82ad 51#define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP)
374330e2 52
59ad2c03 53#define WM_IGNORE_CLIP (WM_XUSER + 2)
1ba99d2c 54#define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
c44bf5bd 55#define WM_AGENT_CALLBACK (WM_XUSER + 4)
374330e2 56
3cf144db 57/* Needed for Chinese support and apparently not always defined. */
58#ifndef VK_PROCESSKEY
59#define VK_PROCESSKEY 0xE5
60#endif
61
3f0f9388 62/* Mouse wheel support. */
5eaf1e06 63#ifndef WM_MOUSEWHEEL
3f0f9388 64#define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
65#endif
66#ifndef WHEEL_DELTA
67#define WHEEL_DELTA 120
5eaf1e06 68#endif
69
2c02ed02 70static Mouse_Button translate_button(Mouse_Button button);
32874aea 71static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
72static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
73 unsigned char *output);
374330e2 74static void cfgtopalette(void);
26d1da7b 75static void systopalette(void);
374330e2 76static void init_palette(void);
5a73255e 77static void init_fonts(int, int);
4eeb7d09 78static void another_font(int);
79static void deinit_fonts(void);
a5ce2e9f 80static void set_input_locale(HKL);
374330e2 81
1ba99d2c 82static int is_full_screen(void);
83static void make_full_screen(void);
84static void clear_full_screen(void);
85static void flip_full_screen(void);
86
5a73255e 87/* Window layout information */
88static void reset_window(int);
a401e5f3 89static int extra_width, extra_height;
5a73255e 90static int font_width, font_height, font_dualwidth;
91static int offset_width, offset_height;
92static int was_zoomed = 0;
93static int prev_rows, prev_cols;
94
39934deb 95static void enact_netevent(WPARAM, LPARAM);
f8a28d1f 96static void flash_window(int mode);
c1da5066 97static void sys_cursor_update(void);
b9d7bcad 98static int is_shift_pressed(void);
d0f1bcb4 99static int get_fullscreen_rect(RECT * ss);
59ad2c03 100
c1da5066 101static int caret_x = -1, caret_y = -1;
102
0b4f0bc0 103static int kbd_codepage;
104
b9d7bcad 105static void *ldisc;
6b78788a 106static Backend *back;
107static void *backhandle;
b9d7bcad 108
21d2b241 109static struct unicode_data ucsdata;
0b4f0bc0 110static int session_closed;
111
125105d1 112static const struct telnet_special *specials;
6f2d0cde 113static int n_specials;
125105d1 114
39934deb 115#define TIMING_TIMER_ID 1234
116static long timing_next_time;
117
63be7767 118static struct {
119 HMENU menu;
120 int specials_submenu_pos;
121} popup_menus[2];
122enum { SYSMENU, CTXMENU };
123
3ea863a3 124Config cfg; /* exported to windlg.c */
125
0b4f0bc0 126extern struct sesslist sesslist; /* imported from windlg.c */
127
c44bf5bd 128struct agent_callback {
129 void (*callback)(void *, void *, int);
130 void *callback_ctx;
131 void *data;
132 int len;
133};
134
374330e2 135#define FONT_NORMAL 0
136#define FONT_BOLD 1
137#define FONT_UNDERLINE 2
138#define FONT_BOLDUND 3
4eeb7d09 139#define FONT_WIDE 0x04
140#define FONT_HIGH 0x08
141#define FONT_NARROW 0x10
5a73255e 142
4eeb7d09 143#define FONT_OEM 0x20
144#define FONT_OEMBOLD 0x21
145#define FONT_OEMUND 0x22
146#define FONT_OEMBOLDUND 0x23
5a73255e 147
148#define FONT_MAXNO 0x2F
4eeb7d09 149#define FONT_SHIFT 5
150static HFONT fonts[FONT_MAXNO];
71f6951d 151static LOGFONT lfont;
4eeb7d09 152static int fontflag[FONT_MAXNO];
374330e2 153static enum {
154 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
155} bold_mode;
156static enum {
157 UND_LINE, UND_FONT
158} und_mode;
159static int descent;
160
04a8208b 161#define NCFGCOLOURS 22
cecb13f6 162#define NEXTCOLOURS 240
163#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
164static COLORREF colours[NALLCOLOURS];
374330e2 165static HPALETTE pal;
166static LPLOGPALETTE logpal;
cecb13f6 167static RGBTRIPLE defpal[NALLCOLOURS];
374330e2 168
169static HWND hwnd;
170
934c0b7a 171static HBITMAP caretbm;
172
374330e2 173static int dbltime, lasttime, lastact;
174static Mouse_Button lastbtn;
175
01c034ad 176/* this allows xterm-style mouse handling. */
177static int send_raw_mouse = 0;
178static int wheel_accumulator = 0;
179
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?",
76347f46 1865 str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2)
1866 == IDOK)
f6f450e2 1867 DestroyWindow(hwnd);
1868 sfree(str);
1869 }
68130d34 1870 return 0;
374330e2 1871 case WM_DESTROY:
32874aea 1872 show_mouseptr(1);
1873 PostQuitMessage(0);
374330e2 1874 return 0;
63be7767 1875 case WM_COMMAND:
6833a413 1876 case WM_SYSCOMMAND:
1877 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1878 case IDM_SHOWLOG:
c5e9c988 1879 showeventlog(hwnd);
374330e2 1880 break;
1881 case IDM_NEWSESS:
1882 case IDM_DUPSESS:
6833a413 1883 case IDM_SAVEDSESS:
374330e2 1884 {
1885 char b[2048];
1886 char c[30], *cl;
e4e4cc7e 1887 int freecl = FALSE;
374330e2 1888 STARTUPINFO si;
1889 PROCESS_INFORMATION pi;
1890 HANDLE filemap = NULL;
1891
1892 if (wParam == IDM_DUPSESS) {
1893 /*
1894 * Allocate a file-mapping memory chunk for the
1895 * config structure.
1896 */
1897 SECURITY_ATTRIBUTES sa;
1898 Config *p;
1899
1900 sa.nLength = sizeof(sa);
1901 sa.lpSecurityDescriptor = NULL;
1902 sa.bInheritHandle = TRUE;
32874aea 1903 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1904 &sa,
1905 PAGE_READWRITE,
32874aea 1906 0, sizeof(Config), NULL);
374330e2 1907 if (filemap) {
32874aea 1908 p = (Config *) MapViewOfFile(filemap,
1909 FILE_MAP_WRITE,
1910 0, 0, sizeof(Config));
374330e2 1911 if (p) {
1912 *p = cfg; /* structure copy */
1913 UnmapViewOfFile(p);
1914 }
1915 }
1d470ad2 1916 sprintf(c, "putty &%p", filemap);
374330e2 1917 cl = c;
0a4aa984 1918 } else if (wParam == IDM_SAVEDSESS) {
865e82ad 1919 unsigned int sessno = ((lParam - IDM_SAVED_MIN)
1920 / MENU_SAVED_STEP) + 1;
1f1e3232 1921 if (sessno < sesslist.nsessions) {
1922 char *session = sesslist.sessions[sessno];
1923 /* XXX spaces? quotes? "-load"? */
1924 cl = dupprintf("putty @%s", session);
1925 freecl = TRUE;
f8d7977b 1926 } else
1927 break;
374330e2 1928 } else
6833a413 1929 cl = NULL;
374330e2 1930
32874aea 1931 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1932 si.cb = sizeof(si);
1933 si.lpReserved = NULL;
1934 si.lpDesktop = NULL;
1935 si.lpTitle = NULL;
1936 si.dwFlags = 0;
1937 si.cbReserved2 = 0;
1938 si.lpReserved2 = NULL;
32874aea 1939 CreateProcess(b, cl, NULL, NULL, TRUE,
1940 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1941
1942 if (filemap)
1943 CloseHandle(filemap);
e4e4cc7e 1944 if (freecl)
dcbde236 1945 sfree(cl);
374330e2 1946 }
1947 break;
e3be8de5 1948 case IDM_RESTART:
1949 if (!back) {
1950 logevent(NULL, "----- Session restarted -----");
1951 start_backend();
1952 }
1953
1954 break;
32874aea 1955 case IDM_RECONF:
1956 {
5a73255e 1957 Config prev_cfg;
1958 int init_lvl = 1;
1959
32874aea 1960 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
5a73255e 1961 prev_cfg = cfg;
e1c8e0ed 1962
f89c3294 1963 if (!do_reconfig(hwnd, back ? back->cfg_info(backhandle) : 0))
32874aea 1964 break;
e1c8e0ed 1965
2cae8529 1966 {
1967 /* Disable full-screen if resizing forbidden */
1968 HMENU m = GetSystemMenu (hwnd, FALSE);
1969 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1970 (cfg.resize_action == RESIZE_DISABLED)
1971 ? MF_GRAYED : MF_ENABLED);
1972 /* Gracefully unzoom if necessary */
1ba99d2c 1973 if (IsZoomed(hwnd) &&
2cae8529 1974 (cfg.resize_action == RESIZE_DISABLED)) {
1ba99d2c 1975 ShowWindow(hwnd, SW_RESTORE);
2cae8529 1976 }
a401e5f3 1977 }
1978
c229ef97 1979 /* Pass new config data to the logging module */
1980 log_reconfig(logctx, &cfg);
e1c8e0ed 1981
32874aea 1982 sfree(logpal);
1983 /*
1984 * Flush the line discipline's edit buffer in the
1985 * case where local editing has just been disabled.
1986 */
e3be8de5 1987 if (ldisc)
1988 ldisc_send(ldisc, NULL, 0, 0);
32874aea 1989 if (pal)
1990 DeleteObject(pal);
1991 logpal = NULL;
1992 pal = NULL;
1993 cfgtopalette();
1994 init_palette();
1995
64734920 1996 /* Pass new config data to the terminal */
1997 term_reconfig(term, &cfg);
0d2086c5 1998
86916870 1999 /* Pass new config data to the back end */
e3be8de5 2000 if (back)
2001 back->reconfig(backhandle, &cfg);
86916870 2002
5a73255e 2003 /* Screen size changed ? */
2004 if (cfg.height != prev_cfg.height ||
2005 cfg.width != prev_cfg.width ||
2006 cfg.savelines != prev_cfg.savelines ||
0ed2f48e 2007 cfg.resize_action == RESIZE_FONT ||
811f10b3 2008 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
0ed2f48e 2009 cfg.resize_action == RESIZE_DISABLED)
887035a5 2010 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 2011
32874aea 2012 /* Enable or disable the scroll bar, etc */
2013 {
2014 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
2015 LONG nexflag, exflag =
2016 GetWindowLong(hwnd, GWL_EXSTYLE);
2017
2018 nexflag = exflag;
5a73255e 2019 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 2020 if (cfg.alwaysontop) {
2021 nexflag |= WS_EX_TOPMOST;
2022 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
2023 SWP_NOMOVE | SWP_NOSIZE);
2024 } else {
2025 nexflag &= ~(WS_EX_TOPMOST);
2026 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
2027 SWP_NOMOVE | SWP_NOSIZE);
2028 }
2029 }
2030 if (cfg.sunken_edge)
2031 nexflag |= WS_EX_CLIENTEDGE;
2032 else
2033 nexflag &= ~(WS_EX_CLIENTEDGE);
2034
2035 nflg = flag;
1ba99d2c 2036 if (is_full_screen() ?
2cae8529 2037 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
32874aea 2038 nflg |= WS_VSCROLL;
2039 else
2040 nflg &= ~WS_VSCROLL;
811f10b3 2041
2042 if (cfg.resize_action == RESIZE_DISABLED ||
2043 is_full_screen())
2044 nflg &= ~WS_THICKFRAME;
2045 else
2046 nflg |= WS_THICKFRAME;
2047
ad5c93cc 2048 if (cfg.resize_action == RESIZE_DISABLED)
811f10b3 2049 nflg &= ~WS_MAXIMIZEBOX;
32874aea 2050 else
811f10b3 2051 nflg |= WS_MAXIMIZEBOX;
32874aea 2052
2053 if (nflg != flag || nexflag != exflag) {
32874aea 2054 if (nflg != flag)
2055 SetWindowLong(hwnd, GWL_STYLE, nflg);
2056 if (nexflag != exflag)
2057 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
2058
32874aea 2059 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
2060 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 2061 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
2062 SWP_FRAMECHANGED);
c9def1b8 2063
5a73255e 2064 init_lvl = 2;
e44d78b6 2065 }
32874aea 2066 }
5a73255e 2067
e44d78b6 2068 /* Oops */
ad5c93cc 2069 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 2070 force_normal(hwnd);
5a73255e 2071 init_lvl = 2;
2072 }
2073
a8327734 2074 set_title(NULL, cfg.wintitle);
32874aea 2075 if (IsIconic(hwnd)) {
2076 SetWindowText(hwnd,
2077 cfg.win_name_always ? window_name :
2078 icon_name);
3da0b1d2 2079 }
5a73255e 2080
9a30e26b 2081 if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
5a73255e 2082 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
9a30e26b 2083 cfg.font.isbold != prev_cfg.font.isbold ||
2084 cfg.font.height != prev_cfg.font.height ||
2085 cfg.font.charset != prev_cfg.font.charset ||
5a73255e 2086 cfg.vtmode != prev_cfg.vtmode ||
2087 cfg.bold_colour != prev_cfg.bold_colour ||
0ed2f48e 2088 cfg.resize_action == RESIZE_DISABLED ||
2089 cfg.resize_action == RESIZE_EITHER ||
2090 (cfg.resize_action != prev_cfg.resize_action))
5a73255e 2091 init_lvl = 2;
2092
2093 InvalidateRect(hwnd, NULL, TRUE);
2094 reset_window(init_lvl);
7732d38a 2095 net_pending_errors();
32874aea 2096 }
2097 break;
bc1235d4 2098 case IDM_COPYALL:
887035a5 2099 term_copyall(term);
bc1235d4 2100 break;
63be7767 2101 case IDM_PASTE:
2102 term_do_paste(term);
2103 break;
32874aea 2104 case IDM_CLRSB:
887035a5 2105 term_clrsb(term);
32874aea 2106 break;
2107 case IDM_RESET:
887035a5 2108 term_pwron(term);
e3be8de5 2109 if (ldisc)
2110 ldisc_send(ldisc, NULL, 0, 0);
32874aea 2111 break;
374330e2 2112 case IDM_ABOUT:
32874aea 2113 showabout(hwnd);
374330e2 2114 break;
70133c0e 2115 case IDM_HELP:
2116 WinHelp(hwnd, help_path,
2117 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
2118 break;
0d6dcf38 2119 case SC_MOUSEMENU:
2120 /*
2121 * We get this if the System menu has been activated
2122 * using the mouse.
2123 */
2124 show_mouseptr(1);
2125 break;
dfca2656 2126 case SC_KEYMENU:
2127 /*
0d6dcf38 2128 * We get this if the System menu has been activated
2129 * using the keyboard. This might happen from within
2130 * TranslateKey, in which case it really wants to be
2131 * followed by a `space' character to actually _bring
2132 * the menu up_ rather than just sitting there in
2133 * `ready to appear' state.
dfca2656 2134 */
0d6dcf38 2135 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 2136 if( lParam == 0 )
2137 PostMessage(hwnd, WM_CHAR, ' ', 0);
2138 break;
a401e5f3 2139 case IDM_FULLSCREEN:
1ba99d2c 2140 flip_full_screen();
2141 break;
32874aea 2142 default:
1f1e3232 2143 if (wParam >= IDM_SAVED_MIN && wParam < IDM_SAVED_MAX) {
32874aea 2144 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
2145 }
125105d1 2146 if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
2147 int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
125105d1 2148 /*
2149 * Ensure we haven't been sent a bogus SYSCOMMAND
2150 * which would cause us to reference invalid memory
2151 * and crash. Perhaps I'm just too paranoid here.
2152 */
6f2d0cde 2153 if (i >= n_specials)
2154 break;
2155 if (back)
2156 back->special(backhandle, specials[i].code);
2157 net_pending_errors();
125105d1 2158 }
374330e2 2159 }
2160 break;
37508af4 2161
2162#define X_POS(l) ((int)(short)LOWORD(l))
2163#define Y_POS(l) ((int)(short)HIWORD(l))
2164
5a73255e 2165#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2166#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
374330e2 2167 case WM_LBUTTONDOWN:
374330e2 2168 case WM_MBUTTONDOWN:
374330e2 2169 case WM_RBUTTONDOWN:
01c034ad 2170 case WM_LBUTTONUP:
2171 case WM_MBUTTONUP:
374330e2 2172 case WM_RBUTTONUP:
ebc0310d 2173 if (message == WM_RBUTTONDOWN &&
2174 ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) {
63be7767 2175 POINT cursorpos;
2176
ebc0310d 2177 show_mouseptr(1); /* make sure pointer is visible */
63be7767 2178 GetCursorPos(&cursorpos);
2179 TrackPopupMenu(popup_menus[CTXMENU].menu,
2180 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
2181 cursorpos.x, cursorpos.y,
2182 0, hwnd, NULL);
2183 break;
2184 }
01c034ad 2185 {
2186 int button, press;
6908fed7 2187
01c034ad 2188 switch (message) {
32874aea 2189 case WM_LBUTTONDOWN:
2190 button = MBT_LEFT;
2191 press = 1;
2192 break;
2193 case WM_MBUTTONDOWN:
2194 button = MBT_MIDDLE;
2195 press = 1;
2196 break;
2197 case WM_RBUTTONDOWN:
2198 button = MBT_RIGHT;
2199 press = 1;
2200 break;
2201 case WM_LBUTTONUP:
2202 button = MBT_LEFT;
2203 press = 0;
2204 break;
2205 case WM_MBUTTONUP:
2206 button = MBT_MIDDLE;
2207 press = 0;
2208 break;
2209 case WM_RBUTTONUP:
2210 button = MBT_RIGHT;
2211 press = 0;
2212 break;
2d466ffd 2213 default:
2214 button = press = 0; /* shouldn't happen */
01c034ad 2215 }
2216 show_mouseptr(1);
2cae8529 2217 /*
2218 * Special case: in full-screen mode, if the left
2219 * button is clicked in the very top left corner of the
2220 * window, we put up the System menu instead of doing
2221 * selection.
2222 */
52a4d159 2223 {
2224 char mouse_on_hotspot = 0;
2225 POINT pt;
2226
2227 GetCursorPos(&pt);
2228#ifndef NO_MULTIMON
2229 {
2230 HMONITOR mon;
2231 MONITORINFO mi;
2232
2233 mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
2234
2235 if (mon != NULL) {
2236 mi.cbSize = sizeof(MONITORINFO);
2237 GetMonitorInfo(mon, &mi);
2238
2239 if (mi.rcMonitor.left == pt.x &&
2240 mi.rcMonitor.top == pt.y) {
2241 mouse_on_hotspot = 1;
2242 }
52a4d159 2243 }
2244 }
2245#else
2246 if (pt.x == 0 && pt.y == 0) {
2247 mouse_on_hotspot = 1;
2248 }
2249#endif
2250 if (is_full_screen() && press &&
2251 button == MBT_LEFT && mouse_on_hotspot) {
2252 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU,
2253 MAKELPARAM(pt.x, pt.y));
2254 return 0;
2255 }
2cae8529 2256 }
52a4d159 2257
01c034ad 2258 if (press) {
32874aea 2259 click(button,
2260 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
6908fed7 2261 wParam & MK_SHIFT, wParam & MK_CONTROL,
2262 is_alt_pressed());
01c034ad 2263 SetCapture(hwnd);
2264 } else {
fc5b0934 2265 term_mouse(term, button, translate_button(button), MA_RELEASE,
32874aea 2266 TO_CHR_X(X_POS(lParam)),
2267 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 2268 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 2269 ReleaseCapture();
2270 }
2271 }
374330e2 2272 return 0;
2273 case WM_MOUSEMOVE:
d8a13f62 2274 {
2275 /*
2276 * Windows seems to like to occasionally send MOUSEMOVE
2277 * events even if the mouse hasn't moved. Don't unhide
2278 * the mouse pointer in this case.
2279 */
2280 static WPARAM wp = 0;
2281 static LPARAM lp = 0;
2282 if (wParam != wp || lParam != lp ||
2283 last_mousemove != WM_MOUSEMOVE) {
2284 show_mouseptr(1);
2285 wp = wParam; lp = lParam;
2286 last_mousemove = WM_MOUSEMOVE;
2287 }
2288 }
374330e2 2289 /*
2290 * Add the mouse position and message time to the random
7d6ee6ff 2291 * number noise.
374330e2 2292 */
32874aea 2293 noise_ultralight(lParam);
374330e2 2294
319bfe5a 2295 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2296 GetCapture() == hwnd) {
374330e2 2297 Mouse_Button b;
2298 if (wParam & MK_LBUTTON)
6c6e7711 2299 b = MBT_LEFT;
374330e2 2300 else if (wParam & MK_MBUTTON)
6c6e7711 2301 b = MBT_MIDDLE;
374330e2 2302 else
6c6e7711 2303 b = MBT_RIGHT;
fc5b0934 2304 term_mouse(term, b, translate_button(b), MA_DRAG,
2305 TO_CHR_X(X_POS(lParam)),
32874aea 2306 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 2307 wParam & MK_CONTROL, is_alt_pressed());
374330e2 2308 }
374330e2 2309 return 0;
d318ef8e 2310 case WM_NCMOUSEMOVE:
d8a13f62 2311 {
2312 static WPARAM wp = 0;
2313 static LPARAM lp = 0;
2314 if (wParam != wp || lParam != lp ||
2315 last_mousemove != WM_NCMOUSEMOVE) {
2316 show_mouseptr(1);
2317 wp = wParam; lp = lParam;
2318 last_mousemove = WM_NCMOUSEMOVE;
2319 }
2320 }
32874aea 2321 noise_ultralight(lParam);
f9cbc48c 2322 break;
374330e2 2323 case WM_IGNORE_CLIP:
2324 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2325 break;
2326 case WM_DESTROYCLIPBOARD:
2327 if (!ignore_clip)
887035a5 2328 term_deselect(term);
374330e2 2329 ignore_clip = FALSE;
2330 return 0;
2331 case WM_PAINT:
2332 {
2333 PAINTSTRUCT p;
39934deb 2334
32874aea 2335 HideCaret(hwnd);
2336 hdc = BeginPaint(hwnd, &p);
374330e2 2337 if (pal) {
32874aea 2338 SelectPalette(hdc, pal, TRUE);
2339 RealizePalette(hdc);
374330e2 2340 }
39934deb 2341
887035a5 2342 term_paint(term, hdc,
5a73255e 2343 (p.rcPaint.left-offset_width)/font_width,
2344 (p.rcPaint.top-offset_height)/font_height,
2345 (p.rcPaint.right-offset_width-1)/font_width,
c1b55581 2346 (p.rcPaint.bottom-offset_height-1)/font_height,
39934deb 2347 TRUE);
5a73255e 2348
2349 if (p.fErase ||
2350 p.rcPaint.left < offset_width ||
2351 p.rcPaint.top < offset_height ||
887035a5 2352 p.rcPaint.right >= offset_width + font_width*term->cols ||
2353 p.rcPaint.bottom>= offset_height + font_height*term->rows)
5a73255e 2354 {
2355 HBRUSH fillcolour, oldbrush;
2356 HPEN edge, oldpen;
2357 fillcolour = CreateSolidBrush (
c50f9fde 2358 colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
5a73255e 2359 oldbrush = SelectObject(hdc, fillcolour);
2360 edge = CreatePen(PS_SOLID, 0,
c50f9fde 2361 colours[ATTR_DEFBG>>ATTR_BGSHIFT]);
5a73255e 2362 oldpen = SelectObject(hdc, edge);
2363
bfa7a5db 2364 /*
2365 * Jordan Russell reports that this apparently
2366 * ineffectual IntersectClipRect() call masks a
2367 * Windows NT/2K bug causing strange display
2368 * problems when the PuTTY window is taller than
2369 * the primary monitor. It seems harmless enough...
2370 */
2371 IntersectClipRect(hdc,
2372 p.rcPaint.left, p.rcPaint.top,
2373 p.rcPaint.right, p.rcPaint.bottom);
2374
5a73255e 2375 ExcludeClipRect(hdc,
2376 offset_width, offset_height,
887035a5 2377 offset_width+font_width*term->cols,
2378 offset_height+font_height*term->rows);
5a73255e 2379
2380 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2381 p.rcPaint.right, p.rcPaint.bottom);
2382
31fb1866 2383 /* SelectClipRgn(hdc, NULL); */
5a73255e 2384
2385 SelectObject(hdc, oldbrush);
2386 DeleteObject(fillcolour);
2387 SelectObject(hdc, oldpen);
2388 DeleteObject(edge);
2389 }
32874aea 2390 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2391 SelectObject(hdc, GetStockObject(WHITE_PEN));
2392 EndPaint(hwnd, &p);
2393 ShowCaret(hwnd);
374330e2 2394 }
2395 return 0;
2396 case WM_NETEVENT:
39934deb 2397 enact_netevent(wParam, lParam);
2398 net_pending_errors();
374330e2 2399 return 0;
2400 case WM_SETFOCUS:
1cff1320 2401 term_set_focus(term, TRUE);
32874aea 2402 CreateCaret(hwnd, caretbm, font_width, font_height);
2403 ShowCaret(hwnd);
f8a28d1f 2404 flash_window(0); /* stop */
32874aea 2405 compose_state = 0;
887035a5 2406 term_update(term);
374330e2 2407 break;
2408 case WM_KILLFOCUS:
32874aea 2409 show_mouseptr(1);
1cff1320 2410 term_set_focus(term, FALSE);
32874aea 2411 DestroyCaret();
c1da5066 2412 caret_x = caret_y = -1; /* ensure caret is replaced next time */
887035a5 2413 term_update(term);
374330e2 2414 break;
73251d5d 2415 case WM_ENTERSIZEMOVE:
5a73255e 2416#ifdef RDB_DEBUG_PATCH
2417 debug((27, "WM_ENTERSIZEMOVE"));
2418#endif
32874aea 2419 EnableSizeTip(1);
2420 resizing = TRUE;
3ad8c6db 2421 need_backend_resize = FALSE;
32874aea 2422 break;
73251d5d 2423 case WM_EXITSIZEMOVE:
32874aea 2424 EnableSizeTip(0);
2425 resizing = FALSE;
5a73255e 2426#ifdef RDB_DEBUG_PATCH
2427 debug((27, "WM_EXITSIZEMOVE"));
2428#endif
2429 if (need_backend_resize) {
887035a5 2430 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 2431 InvalidateRect(hwnd, NULL, TRUE);
2432 }
32874aea 2433 break;
374330e2 2434 case WM_SIZING:
5a73255e 2435 /*
2436 * This does two jobs:
2437 * 1) Keep the sizetip uptodate
2438 * 2) Make sure the window size is _stepped_ in units of the font size.
2439 */
c1b55581 2440 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
374330e2 2441 int width, height, w, h, ew, eh;
32874aea 2442 LPRECT r = (LPRECT) lParam;
374330e2 2443
0ed2f48e 2444 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
887035a5 2445 (cfg.height != term->rows || cfg.width != term->cols )) {
5a73255e 2446 /*
0ed2f48e 2447 * Great! It seems that both the terminal size and the
2448 * font size have been changed and the user is now dragging.
2449 *
2450 * It will now be difficult to get back to the configured
2451 * font size!
2452 *
2453 * This would be easier but it seems to be too confusing.
2454
887035a5 2455 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 2456 reset_window(2);
0ed2f48e 2457 */
887035a5 2458 cfg.height=term->rows; cfg.width=term->cols;
0ed2f48e 2459
5a73255e 2460 InvalidateRect(hwnd, NULL, TRUE);
2461 need_backend_resize = TRUE;
2462 }
2463
374330e2 2464 width = r->right - r->left - extra_width;
2465 height = r->bottom - r->top - extra_height;
32874aea 2466 w = (width + font_width / 2) / font_width;
2467 if (w < 1)
2468 w = 1;
2469 h = (height + font_height / 2) / font_height;
2470 if (h < 1)
2471 h = 1;
2472 UpdateSizeTip(hwnd, w, h);
374330e2 2473 ew = width - w * font_width;
2474 eh = height - h * font_height;
2475 if (ew != 0) {
2476 if (wParam == WMSZ_LEFT ||
32874aea 2477 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 2478 r->left += ew;
2479 else
2480 r->right -= ew;
2481 }
2482 if (eh != 0) {
2483 if (wParam == WMSZ_TOP ||
32874aea 2484 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 2485 r->top += eh;
2486 else
2487 r->bottom -= eh;
2488 }
2489 if (ew || eh)
2490 return 1;
2491 else
2492 return 0;
5a73255e 2493 } else {
2494 int width, height, w, h, rv = 0;
2495 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2496 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2497 LPRECT r = (LPRECT) lParam;
2498
2499 width = r->right - r->left - ex_width;
2500 height = r->bottom - r->top - ex_height;
2501
887035a5 2502 w = (width + term->cols/2)/term->cols;
2503 h = (height + term->rows/2)/term->rows;
2504 if ( r->right != r->left + w*term->cols + ex_width)
5a73255e 2505 rv = 1;
2506
2507 if (wParam == WMSZ_LEFT ||
2508 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
887035a5 2509 r->left = r->right - w*term->cols - ex_width;
5a73255e 2510 else
887035a5 2511 r->right = r->left + w*term->cols + ex_width;
5a73255e 2512
887035a5 2513 if (r->bottom != r->top + h*term->rows + ex_height)
5a73255e 2514 rv = 1;
2515
2516 if (wParam == WMSZ_TOP ||
2517 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
887035a5 2518 r->top = r->bottom - h*term->rows - ex_height;
5a73255e 2519 else
887035a5 2520 r->bottom = r->top + h*term->rows + ex_height;
5a73255e 2521
2522 return rv;
374330e2 2523 }
32874aea 2524 /* break; (never reached) */
1ba99d2c 2525 case WM_FULLSCR_ON_MAX:
2526 fullscr_on_max = TRUE;
2527 break;
c1da5066 2528 case WM_MOVE:
2529 sys_cursor_update();
2530 break;
374330e2 2531 case WM_SIZE:
5a73255e 2532#ifdef RDB_DEBUG_PATCH
2533 debug((27, "WM_SIZE %s (%d,%d)",
2534 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2535 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2536 (wParam == SIZE_RESTORED && resizing) ? "to":
2537 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2538 "...",
2539 LOWORD(lParam), HIWORD(lParam)));
2540#endif
0ed2f48e 2541 if (wParam == SIZE_MINIMIZED)
32874aea 2542 SetWindowText(hwnd,
2543 cfg.win_name_always ? window_name : icon_name);
374330e2 2544 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2545 SetWindowText(hwnd, window_name);
811f10b3 2546 if (wParam == SIZE_RESTORED)
2547 clear_full_screen();
2548 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
811f10b3 2549 fullscr_on_max = FALSE;
d0f1bcb4 2550 make_full_screen();
811f10b3 2551 }
5a73255e 2552
ad5c93cc 2553 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2554 /* A resize, well it better be a minimize. */
2555 reset_window(-1);
2556 } else {
2557
1a6f78fe 2558 int width, height, w, h;
374330e2 2559
2560 width = LOWORD(lParam);
2561 height = HIWORD(lParam);
5a73255e 2562
2563 if (!resizing) {
0ed2f48e 2564 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
5a73255e 2565 was_zoomed = 1;
887035a5 2566 prev_rows = term->rows;
2567 prev_cols = term->cols;
0ed2f48e 2568 if (cfg.resize_action == RESIZE_TERM) {
5a73255e 2569 w = width / font_width;
2570 if (w < 1) w = 1;
2571 h = height / font_height;
2572 if (h < 1) h = 1;
2573
887035a5 2574 term_size(term, h, w, cfg.savelines);
5a73255e 2575 }
2576 reset_window(0);
2577 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2578 was_zoomed = 0;
0ed2f48e 2579 if (cfg.resize_action == RESIZE_TERM)
887035a5 2580 term_size(term, prev_rows, prev_cols, cfg.savelines);
0ed2f48e 2581 if (cfg.resize_action != RESIZE_FONT)
2582 reset_window(2);
2583 else
2584 reset_window(0);
5a73255e 2585 }
2586 /* This is an unexpected resize, these will normally happen
2587 * if the window is too large. Probably either the user
2588 * selected a huge font or the screen size has changed.
2589 *
2590 * This is also called with minimize.
32874aea 2591 */
5a73255e 2592 else reset_window(-1);
2593 }
2594
2595 /*
2596 * Don't call back->size in mid-resize. (To prevent
2597 * massive numbers of resize events getting sent
2598 * down the connection during an NT opaque drag.)
2599 */
2600 if (resizing) {
c1b55581 2601 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
3ad8c6db 2602 need_backend_resize = TRUE;
5a73255e 2603 w = (width-cfg.window_border*2) / font_width;
2604 if (w < 1) w = 1;
2605 h = (height-cfg.window_border*2) / font_height;
2606 if (h < 1) h = 1;
2607
e44d78b6 2608 cfg.height = h;
2609 cfg.width = w;
5a73255e 2610 } else
2611 reset_window(0);
374330e2 2612 }
2613 }
c1da5066 2614 sys_cursor_update();
374330e2 2615 return 0;
2616 case WM_VSCROLL:
2617 switch (LOWORD(wParam)) {
32874aea 2618 case SB_BOTTOM:
887035a5 2619 term_scroll(term, -1, 0);
32874aea 2620 break;
2621 case SB_TOP:
887035a5 2622 term_scroll(term, +1, 0);
32874aea 2623 break;
2624 case SB_LINEDOWN:
887035a5 2625 term_scroll(term, 0, +1);
32874aea 2626 break;
2627 case SB_LINEUP:
887035a5 2628 term_scroll(term, 0, -1);
32874aea 2629 break;
2630 case SB_PAGEDOWN:
887035a5 2631 term_scroll(term, 0, +term->rows / 2);
32874aea 2632 break;
2633 case SB_PAGEUP:
887035a5 2634 term_scroll(term, 0, -term->rows / 2);
32874aea 2635 break;
2636 case SB_THUMBPOSITION:
2637 case SB_THUMBTRACK:
887035a5 2638 term_scroll(term, 1, HIWORD(wParam));
32874aea 2639 break;
2640 }
2641 break;
2642 case WM_PALETTECHANGED:
374330e2 2643 if ((HWND) wParam != hwnd && pal != NULL) {
a8327734 2644 HDC hdc = get_ctx(NULL);
374330e2 2645 if (hdc) {
32874aea 2646 if (RealizePalette(hdc) > 0)
2647 UpdateColors(hdc);
2648 free_ctx(hdc);
374330e2 2649 }
2650 }
2651 break;
2652 case WM_QUERYNEWPALETTE:
2653 if (pal != NULL) {
a8327734 2654 HDC hdc = get_ctx(NULL);
374330e2 2655 if (hdc) {
32874aea 2656 if (RealizePalette(hdc) > 0)
2657 UpdateColors(hdc);
2658 free_ctx(hdc);
374330e2 2659 return TRUE;
2660 }
2661 }
2662 return FALSE;
2663 case WM_KEYDOWN:
2664 case WM_SYSKEYDOWN:
c9def1b8 2665 case WM_KEYUP:
2666 case WM_SYSKEYUP:
374330e2 2667 /*
2668 * Add the scan code and keypress timing to the random
7d6ee6ff 2669 * number noise.
374330e2 2670 */
32874aea 2671 noise_ultralight(lParam);
374330e2 2672
2673 /*
2674 * We don't do TranslateMessage since it disassociates the
2675 * resulting CHAR message from the KEYDOWN that sparked it,
2676 * which we occasionally don't want. Instead, we process
2677 * KEYDOWN, and call the Win32 translator functions so that
2678 * we get the translations under _our_ control.
2679 */
2680 {
2681 unsigned char buf[20];
2682 int len;
2683
32874aea 2684 if (wParam == VK_PROCESSKEY) {
3cf144db 2685 MSG m;
32874aea 2686 m.hwnd = hwnd;
2687 m.message = WM_KEYDOWN;
2688 m.wParam = wParam;
2689 m.lParam = lParam & 0xdfff;
2690 TranslateMessage(&m);
2691 } else {
2692 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2693 if (len == -1)
32874aea 2694 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2695
b2a1eade 2696 if (len != 0) {
bca9517a 2697 /*
256cb87c 2698 * Interrupt an ongoing paste. I'm not sure
2699 * this is sensible, but for the moment it's
2700 * preferable to having to faff about buffering
2701 * things.
2702 */
887035a5 2703 term_nopaste(term);
256cb87c 2704
2705 /*
bca9517a 2706 * We need not bother about stdin backlogs
2707 * here, because in GUI PuTTY we can't do
2708 * anything about it anyway; there's no means
2709 * of asking Windows to hold off on KEYDOWN
2710 * messages. We _have_ to buffer everything
2711 * we're sent.
2712 */
887035a5 2713 term_seen_key_event(term);
e3be8de5 2714 if (ldisc)
2715 ldisc_send(ldisc, buf, len, 1);
32874aea 2716 show_mouseptr(0);
bca9517a 2717 }
3cf144db 2718 }
374330e2 2719 }
7732d38a 2720 net_pending_errors();
374330e2 2721 return 0;
4eeb7d09 2722 case WM_INPUTLANGCHANGE:
a5ce2e9f 2723 /* wParam == Font number */
2724 /* lParam == Locale */
2725 set_input_locale((HKL)lParam);
c1da5066 2726 sys_cursor_update();
4eeb7d09 2727 break;
71f6951d 2728 case WM_IME_NOTIFY:
2729 if(wParam == IMN_SETOPENSTATUS) {
2730 HIMC hImc = ImmGetContext(hwnd);
2731 ImmSetCompositionFont(hImc, &lfont);
2732 ImmReleaseContext(hwnd, hImc);
2733 return 0;
2734 }
2735 break;
88485e4d 2736 case WM_IME_COMPOSITION:
2737 {
2738 HIMC hIMC;
2739 int n;
2740 char *buff;
a3cfd0c8 2741
88485e4d 2742 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2743 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2744
2745 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2746 break; /* fall back to DefWindowProc */
2747
2748 hIMC = ImmGetContext(hwnd);
2749 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2750
2751 if (n > 0) {
a3cfd0c8 2752 int i;
3d88e64d 2753 buff = snewn(n, char);
88485e4d 2754 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
a3cfd0c8 2755 /*
2756 * Jaeyoun Chung reports that Korean character
2757 * input doesn't work correctly if we do a single
2758 * luni_send() covering the whole of buff. So
2759 * instead we luni_send the characters one by one.
2760 */
887035a5 2761 term_seen_key_event(term);
2cb50250 2762 for (i = 0; i < n; i += 2) {
e3be8de5 2763 if (ldisc)
2764 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2cb50250 2765 }
88485e4d 2766 free(buff);
2767 }
2768 ImmReleaseContext(hwnd, hIMC);
2769 return 1;
2770 }
2771
4eeb7d09 2772 case WM_IME_CHAR:
2773 if (wParam & 0xFF00) {
3cf144db 2774 unsigned char buf[2];
2775
2776 buf[1] = wParam;
2777 buf[0] = wParam >> 8;
887035a5 2778 term_seen_key_event(term);
e3be8de5 2779 if (ldisc)
2780 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
4eeb7d09 2781 } else {
2782 char c = (unsigned char) wParam;
887035a5 2783 term_seen_key_event(term);
e3be8de5 2784 if (ldisc)
2785 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
3cf144db 2786 }
4eeb7d09 2787 return (0);
374330e2 2788 case WM_CHAR:
2789 case WM_SYSCHAR:
2790 /*
2791 * Nevertheless, we are prepared to deal with WM_CHAR
2792 * messages, should they crop up. So if someone wants to
2793 * post the things to us as part of a macro manoeuvre,
2794 * we're ready to cope.
2795 */
32874aea 2796 {
4eeb7d09 2797 char c = (unsigned char)wParam;
887035a5 2798 term_seen_key_event(term);
e3be8de5 2799 if (ldisc)
2800 lpage_send(ldisc, CP_ACP, &c, 1, 1);
374330e2 2801 }
2802 return 0;
49a332ff 2803 case WM_SYSCOLORCHANGE:
2804 if (cfg.system_colour) {
2805 /* Refresh palette from system colours. */
2806 /* XXX actually this zaps the entire palette. */
2807 systopalette();
2808 init_palette();
2809 /* Force a repaint of the terminal window. */
2810 term_invalidate(term);
2811 }
2812 break;
c44bf5bd 2813 case WM_AGENT_CALLBACK:
2814 {
2815 struct agent_callback *c = (struct agent_callback *)lParam;
2816 c->callback(c->callback_ctx, c->data, c->len);
2817 sfree(c);
2818 }
2819 return 0;
011b0e33 2820 default:
16837214 2821 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
c1b55581 2822 int shift_pressed=0, control_pressed=0;
011b0e33 2823
2824 if (message == WM_MOUSEWHEEL) {
2825 wheel_accumulator += (short)HIWORD(wParam);
2826 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2827 control_pressed=LOWORD(wParam) & MK_CONTROL;
2828 } else {
2829 BYTE keys[256];
2830 wheel_accumulator += (int)wParam;
2831 if (GetKeyboardState(keys)!=0) {
2832 shift_pressed=keys[VK_SHIFT]&0x80;
2833 control_pressed=keys[VK_CONTROL]&0x80;
2834 }
2835 }
2836
2837 /* process events when the threshold is reached */
2838 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2839 int b;
2840
2841 /* reduce amount for next time */
2842 if (wheel_accumulator > 0) {
2843 b = MBT_WHEEL_UP;
2844 wheel_accumulator -= WHEEL_DELTA;
2845 } else if (wheel_accumulator < 0) {
2846 b = MBT_WHEEL_DOWN;
2847 wheel_accumulator += WHEEL_DELTA;
2848 } else
2849 break;
2850
2851 if (send_raw_mouse &&
2852 !(cfg.mouse_override && shift_pressed)) {
2853 /* send a mouse-down followed by a mouse up */
fc5b0934 2854 term_mouse(term, b, translate_button(b),
011b0e33 2855 MA_CLICK,
2856 TO_CHR_X(X_POS(lParam)),
2857 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2858 control_pressed, is_alt_pressed());
fc5b0934 2859 term_mouse(term, b, translate_button(b),
2860 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
011b0e33 2861 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2862 control_pressed, is_alt_pressed());
2863 } else {
2864 /* trigger a scroll */
887035a5 2865 term_scroll(term, 0,
2866 b == MBT_WHEEL_UP ?
2867 -term->rows / 2 : term->rows / 2);
011b0e33 2868 }
2869 }
2870 return 0;
2871 }
374330e2 2872 }
2873
32874aea 2874 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2875}
2876
2877/*
ec8679e9 2878 * Move the system caret. (We maintain one, even though it's
2879 * invisible, for the benefit of blind people: apparently some
2880 * helper software tracks the system caret, so we should arrange to
2881 * have one.)
2882 */
a8327734 2883void sys_cursor(void *frontend, int x, int y)
32874aea 2884{
c1da5066 2885 int cx, cy;
2886
887035a5 2887 if (!term->has_focus) return;
c1da5066 2888
2889 /*
2890 * Avoid gratuitously re-updating the cursor position and IMM
2891 * window if there's no actual change required.
2892 */
2893 cx = x * font_width + offset_width;
2894 cy = y * font_height + offset_height;
2895 if (cx == caret_x && cy == caret_y)
2896 return;
2897 caret_x = cx;
2898 caret_y = cy;
2899
2900 sys_cursor_update();
2901}
2902
2903static void sys_cursor_update(void)
2904{
88485e4d 2905 COMPOSITIONFORM cf;
2906 HIMC hIMC;
2907
887035a5 2908 if (!term->has_focus) return;
c1da5066 2909
2910 if (caret_x < 0 || caret_y < 0)
2911 return;
2912
2913 SetCaretPos(caret_x, caret_y);
88485e4d 2914
2915 /* IMM calls on Win98 and beyond only */
2916 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2917
2918 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2919 osVersion.dwMinorVersion == 0) return; /* 95 */
2920
2921 /* we should have the IMM functions */
2922 hIMC = ImmGetContext(hwnd);
2923 cf.dwStyle = CFS_POINT;
c1da5066 2924 cf.ptCurrentPos.x = caret_x;
2925 cf.ptCurrentPos.y = caret_y;
88485e4d 2926 ImmSetCompositionWindow(hIMC, &cf);
2927
2928 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2929}
2930
2931/*
374330e2 2932 * Draw a line of text in the window, at given character
2933 * coordinates, in given attributes.
2934 *
2935 * We are allowed to fiddle with the contents of `text'.
2936 */
c6958dfe 2937void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
2938 unsigned long attr, int lattr)
32874aea 2939{
374330e2 2940 COLORREF fg, bg, t;
2941 int nfg, nbg, nfont;
2942 HDC hdc = ctx;
59ad2c03 2943 RECT line_box;
2944 int force_manual_underline = 0;
36566009 2945 int fnt_width, char_width;
4eeb7d09 2946 int text_adjust = 0;
2947 static int *IpDx = 0, IpDxLEN = 0;
2948
36566009 2949 lattr &= LATTR_MODE;
2950
2951 char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2952
4eeb7d09 2953 if (attr & ATTR_WIDE)
2954 char_width *= 2;
59ad2c03 2955
4eeb7d09 2956 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2957 int i;
32874aea 2958 if (len > IpDxLEN) {
59ad2c03 2959 sfree(IpDx);
3d88e64d 2960 IpDx = snewn(len + 16, int);
32874aea 2961 IpDxLEN = (len + 16);
59ad2c03 2962 }
32874aea 2963 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2964 IpDx[i] = char_width;
59ad2c03 2965 }
374330e2 2966
5a73255e 2967 /* Only want the left half of double width lines */
887035a5 2968 if (lattr != LATTR_NORM && x*2 >= term->cols)
5a73255e 2969 return;
2970
c9def1b8 2971 x *= fnt_width;
374330e2 2972 y *= font_height;
5a73255e 2973 x += offset_width;
2974 y += offset_height;
374330e2 2975
887035a5 2976 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
cecb13f6 2977 attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS);
2978 if (bold_mode == BOLD_COLOURS)
2979 attr &= ~ATTR_BOLD;
2980
2981 /* cursor fg and bg */
2982 attr |= (260 << ATTR_FGSHIFT) | (261 << ATTR_BGSHIFT);
374330e2 2983 }
2984
2985 nfont = 0;
4eeb7d09 2986 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2987 /* Assume a poorman font is borken in other ways too. */
2988 lattr = LATTR_WIDE;
2989 } else
2990 switch (lattr) {
2991 case LATTR_NORM:
2992 break;
2993 case LATTR_WIDE:
2994 nfont |= FONT_WIDE;
374330e2 2995 break;
4eeb7d09 2996 default:
2997 nfont |= FONT_WIDE + FONT_HIGH;
2998 break;
2999 }
5a73255e 3000 if (attr & ATTR_NARROW)
3001 nfont |= FONT_NARROW;
4eeb7d09 3002
3003 /* Special hack for the VT100 linedraw glyphs. */
36566009 3004 if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
3005 switch ((unsigned char) (text[0])) {
3006 case 0xBA:
3007 text_adjust = -2 * font_height / 5;
3008 break;
3009 case 0xBB:
3010 text_adjust = -1 * font_height / 5;
3011 break;
3012 case 0xBC:
3013 text_adjust = font_height / 5;
3014 break;
3015 case 0xBD:
3016 text_adjust = 2 * font_height / 5;
3017 break;
3018 }
3019 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
3020 text_adjust *= 2;
3021 text[0] = ucsdata.unitab_xterm['q'];
3022 if (attr & ATTR_UNDER) {
3023 attr &= ~ATTR_UNDER;
3024 force_manual_underline = 1;
374330e2 3025 }
3026 }
3027
4eeb7d09 3028 /* Anything left as an original character set is unprintable. */
36566009 3029 if (DIRECT_CHAR(text[0])) {
3030 int i;
3031 for (i = 0; i < len; i++)
3032 text[i] = 0xFFFD;
4eeb7d09 3033 }
3034
3035 /* OEM CP */
36566009 3036 if ((text[0] & CSET_MASK) == CSET_OEMCP)
4eeb7d09 3037 nfont |= FONT_OEM;
3038
37ca32ed 3039 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
37ca32ed 3040 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
374330e2 3041 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
3042 nfont |= FONT_BOLD;
3043 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
3044 nfont |= FONT_UNDERLINE;
4eeb7d09 3045 another_font(nfont);
32874aea 3046 if (!fonts[nfont]) {
3047 if (nfont & FONT_UNDERLINE)
59ad2c03 3048 force_manual_underline = 1;
3049 /* Don't do the same for manual bold, it could be bad news. */
3050
32874aea 3051 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 3052 }
4eeb7d09 3053 another_font(nfont);
3054 if (!fonts[nfont])
3055 nfont = FONT_NORMAL;
374330e2 3056 if (attr & ATTR_REVERSE) {
32874aea 3057 t = nfg;
3058 nfg = nbg;
3059 nbg = t;
374330e2 3060 }
cecb13f6 3061 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD)) {
3062 if (nfg < 16) nfg |= 8;
3063 else if (nfg >= 256) nfg |= 1;
3064 }
3065 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK)) {
3066 if (nbg < 16) nbg |= 8;
3067 else if (nbg >= 256) nbg |= 1;
3068 }
374330e2 3069 fg = colours[nfg];
3070 bg = colours[nbg];
32874aea 3071 SelectObject(hdc, fonts[nfont]);
3072 SetTextColor(hdc, fg);
3073 SetBkColor(hdc, bg);
c6958dfe 3074 if (attr & TATTR_COMBINING)
3075 SetBkMode(hdc, TRANSPARENT);
3076 else
3077 SetBkMode(hdc, OPAQUE);
32874aea 3078 line_box.left = x;
3079 line_box.top = y;
4eeb7d09 3080 line_box.right = x + char_width * len;
32874aea 3081 line_box.bottom = y + font_height;
4eeb7d09 3082
5a73255e 3083 /* Only want the left half of double width lines */
887035a5 3084 if (line_box.right > font_width*term->cols+offset_width)
3085 line_box.right = font_width*term->cols+offset_width;
5a73255e 3086
4eeb7d09 3087 /* We're using a private area for direct to font. (512 chars.) */
36566009 3088 if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {
4eeb7d09 3089 /* Ho Hum, dbcs fonts are a PITA! */
3090 /* To display on W9x I have to convert to UCS */
3091 static wchar_t *uni_buf = 0;
3092 static int uni_len = 0;
5a73255e 3093 int nlen, mptr;
4eeb7d09 3094 if (len > uni_len) {
3095 sfree(uni_buf);
3d88e64d 3096 uni_len = len;
3097 uni_buf = snewn(uni_len, wchar_t);
4eeb7d09 3098 }
4eeb7d09 3099
5a73255e 3100 for(nlen = mptr = 0; mptr<len; mptr++) {
3101 uni_buf[nlen] = 0xFFFD;
21d2b241 3102 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
36566009 3103 char dbcstext[2];
3104 dbcstext[0] = text[mptr] & 0xFF;
3105 dbcstext[1] = text[mptr+1] & 0xFF;
5a73255e 3106 IpDx[nlen] += char_width;
21d2b241 3107 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
36566009 3108 dbcstext, 2, uni_buf+nlen, 1);
5a73255e 3109 mptr++;
3110 }
3111 else
3112 {
36566009 3113 char dbcstext[1];
3114 dbcstext[0] = text[mptr] & 0xFF;
21d2b241 3115 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
36566009 3116 dbcstext, 1, uni_buf+nlen, 1);
5a73255e 3117 }
3118 nlen++;
3119 }
4eeb7d09 3120 if (nlen <= 0)
3121 return; /* Eeek! */
3122
3123 ExtTextOutW(hdc, x,
3124 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 3125 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3126 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3127 SetBkMode(hdc, TRANSPARENT);
3128 ExtTextOutW(hdc, x - 1,
3129 y - font_height * (lattr ==
3130 LATTR_BOT) + text_adjust,
5a73255e 3131 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3132 }
5a73255e 3133
3134 IpDx[0] = -1;
36566009 3135 } else if (DIRECT_FONT(text[0])) {
3136 static char *directbuf = NULL;
3137 static int directlen = 0;
3138 int i;
3139 if (len > directlen) {
3140 directlen = len;
3141 directbuf = sresize(directbuf, directlen, char);
3142 }
3143
3144 for (i = 0; i < len; i++)
3145 directbuf[i] = text[i] & 0xFF;
3146
4eeb7d09 3147 ExtTextOut(hdc, x,
3148 y - font_height * (lattr == LATTR_BOT) + text_adjust,
36566009 3149 ETO_CLIPPED | ETO_OPAQUE, &line_box, directbuf, len, IpDx);
4eeb7d09 3150 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3151 SetBkMode(hdc, TRANSPARENT);
3152
3153 /* GRR: This draws the character outside it's box and can leave
3154 * 'droppings' even with the clip box! I suppose I could loop it
3155 * one character at a time ... yuk.
3156 *
3157 * Or ... I could do a test print with "W", and use +1 or -1 for this
3158 * shift depending on if the leftmost column is blank...
3159 */
3160 ExtTextOut(hdc, x - 1,
3161 y - font_height * (lattr ==
3162 LATTR_BOT) + text_adjust,
36566009 3163 ETO_CLIPPED, &line_box, directbuf, len, IpDx);
4eeb7d09 3164 }
3165 } else {
3166 /* And 'normal' unicode characters */
3167 static WCHAR *wbuf = NULL;
3168 static int wlen = 0;
3169 int i;
c6958dfe 3170
4eeb7d09 3171 if (wlen < len) {
3172 sfree(wbuf);
3173 wlen = len;
3d88e64d 3174 wbuf = snewn(wlen, WCHAR);
4eeb7d09 3175 }
c6958dfe 3176
4eeb7d09 3177 for (i = 0; i < len; i++)
36566009 3178 wbuf[i] = text[i];
4eeb7d09 3179
f0fccd51 3180 /* print Glyphs as they are, without Windows' Shaping*/
3181 exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
c6958dfe 3182 &line_box, wbuf, len, IpDx, !(attr & TATTR_COMBINING));
f0fccd51 3183/* ExtTextOutW(hdc, x,
4eeb7d09 3184 y - font_height * (lattr == LATTR_BOT) + text_adjust,
3185 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
f0fccd51 3186 */
4eeb7d09 3187
3188 /* And the shadow bold hack. */
5a73255e 3189 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 3190 SetBkMode(hdc, TRANSPARENT);
3191 ExtTextOutW(hdc, x - 1,
3192 y - font_height * (lattr ==
3193 LATTR_BOT) + text_adjust,
3194 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
3195 }
374330e2 3196 }
4eeb7d09 3197 if (lattr != LATTR_TOP && (force_manual_underline ||
3198 (und_mode == UND_LINE
3199 && (attr & ATTR_UNDER)))) {
32874aea 3200 HPEN oldpen;
4eeb7d09 3201 int dec = descent;
3202 if (lattr == LATTR_BOT)
3203 dec = dec * 2 - font_height;
3204
32874aea 3205 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 3206 MoveToEx(hdc, x, y + dec, NULL);
3207 LineTo(hdc, x + len * char_width, y + dec);
32874aea 3208 oldpen = SelectObject(hdc, oldpen);
3209 DeleteObject(oldpen);
374330e2 3210 }
4eeb7d09 3211}
3212
c6958dfe 3213/*
3214 * Wrapper that handles combining characters.
3215 */
3216void do_text(Context ctx, int x, int y, wchar_t *text, int len,
3217 unsigned long attr, int lattr)
3218{
3219 if (attr & TATTR_COMBINING) {
3220 unsigned long a = 0;
3221 attr &= ~TATTR_COMBINING;
3222 while (len--) {
3223 do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
3224 text++;
3225 a = TATTR_COMBINING;
3226 }
3227 } else
3228 do_text_internal(ctx, x, y, text, len, attr, lattr);
3229}
3230
36566009 3231void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
4eeb7d09 3232 unsigned long attr, int lattr)
3233{
3234
3235 int fnt_width;
3236 int char_width;
3237 HDC hdc = ctx;
3238 int ctype = cfg.cursor_type;
3239
8af95aa3 3240 lattr &= LATTR_MODE;
3241
887035a5 3242 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
36566009 3243 if (*text != UCSWIDE) {
4eeb7d09 3244 do_text(ctx, x, y, text, len, attr, lattr);
3245 return;
3246 }
3247 ctype = 2;
3248 attr |= TATTR_RIGHTCURS;
3249 }
3250
3251 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
3252 if (attr & ATTR_WIDE)
3253 char_width *= 2;
3254 x *= fnt_width;
3255 y *= font_height;
5a73255e 3256 x += offset_width;
3257 y += offset_height;
4eeb7d09 3258
887035a5 3259 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
374330e2 3260 POINT pts[5];
32874aea 3261 HPEN oldpen;
374330e2 3262 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 3263 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 3264 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 3265 pts[1].y = pts[2].y = y + font_height - 1;
cecb13f6 3266 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
32874aea 3267 Polyline(hdc, pts, 5);
3268 oldpen = SelectObject(hdc, oldpen);
3269 DeleteObject(oldpen);
4eeb7d09 3270 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 3271 int startx, starty, dx, dy, length, i;
4eeb7d09 3272 if (ctype == 1) {
32874aea 3273 startx = x;
3274 starty = y + descent;
3275 dx = 1;
3276 dy = 0;
4eeb7d09 3277 length = char_width;
32874aea 3278 } else {
4e30ff69 3279 int xadjust = 0;
4eeb7d09 3280 if (attr & TATTR_RIGHTCURS)
3281 xadjust = char_width - 1;
32874aea 3282 startx = x + xadjust;
3283 starty = y;
3284 dx = 0;
3285 dy = 1;
3286 length = font_height;
3287 }
4eeb7d09 3288 if (attr & TATTR_ACTCURS) {
32874aea 3289 HPEN oldpen;
3290 oldpen =
fdc94dd1 3291 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[261]));
32874aea 3292 MoveToEx(hdc, startx, starty, NULL);
3293 LineTo(hdc, startx + dx * length, starty + dy * length);
3294 oldpen = SelectObject(hdc, oldpen);
3295 DeleteObject(oldpen);
3296 } else {
3297 for (i = 0; i < length; i++) {
3298 if (i % 2 == 0) {
fdc94dd1 3299 SetPixel(hdc, startx, starty, colours[261]);
32874aea 3300 }
3301 startx += dx;
3302 starty += dy;
3303 }
3304 }
4e30ff69 3305 }
374330e2 3306}
3307
5a73255e 3308/* This function gets the actual width of a character in the normal font.
3309 */
2102eb8a 3310int char_width(Context ctx, int uc) {
5a73255e 3311 HDC hdc = ctx;
3312 int ibuf = 0;
3313
3314 /* If the font max is the same as the font ave width then this
3315 * function is a no-op.
3316 */
3317 if (!font_dualwidth) return 1;
3318
3319 switch (uc & CSET_MASK) {
36566009 3320 case CSET_ASCII:
21d2b241 3321 uc = ucsdata.unitab_line[uc & 0xFF];
5a73255e 3322 break;
36566009 3323 case CSET_LINEDRW:
21d2b241 3324 uc = ucsdata.unitab_xterm[uc & 0xFF];
5a73255e 3325 break;
36566009 3326 case CSET_SCOACS:
21d2b241 3327 uc = ucsdata.unitab_scoacs[uc & 0xFF];
5a73255e 3328 break;
3329 }
3330 if (DIRECT_FONT(uc)) {
21d2b241 3331 if (ucsdata.dbcs_screenfont) return 1;
5a73255e 3332
3333 /* Speedup, I know of no font where ascii is the wrong width */
36566009 3334 if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')
5a73255e 3335 return 1;
3336
36566009 3337 if ( (uc & CSET_MASK) == CSET_ACP ) {
5a73255e 3338 SelectObject(hdc, fonts[FONT_NORMAL]);
36566009 3339 } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {
5a73255e 3340 another_font(FONT_OEM);
3341 if (!fonts[FONT_OEM]) return 0;
3342
3343 SelectObject(hdc, fonts[FONT_OEM]);
3344 } else
3345 return 0;
3346
36566009 3347 if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&
3348 GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)
5a73255e 3349 return 0;
3350 } else {
3351 /* Speedup, I know of no font where ascii is the wrong width */
3352 if (uc >= ' ' && uc <= '~') return 1;
3353
3354 SelectObject(hdc, fonts[FONT_NORMAL]);
3355 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3356 /* Okay that one worked */ ;
3357 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3358 /* This should work on 9x too, but it's "less accurate" */ ;
3359 else
3360 return 0;
3361 }
3362
3363 ibuf += font_width / 2 -1;
3364 ibuf /= font_width;
3365
3366 return ibuf;
3367}
3368
374330e2 3369/*
c9def1b8 3370 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3371 * codes. Returns number of bytes used or zero to drop the message
3372 * or -1 to forward the message to windows.
374330e2 3373 */
3cf144db 3374static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 3375 unsigned char *output)
3376{
374330e2 3377 BYTE keystate[256];
32874aea 3378 int scan, left_alt = 0, key_down, shift_state;
3379 int r, i, code;
3380 unsigned char *p = output;
4eeb7d09 3381 static int alt_sum = 0;
374330e2 3382
00e3ba0f 3383 HKL kbd_layout = GetKeyboardLayout(0);
3384
c264be08 3385 /* keys is for ToAsciiEx. There's some ick here, see below. */
3386 static WORD keys[3];
0c50ef57 3387 static int compose_char = 0;
3388 static WPARAM compose_key = 0;
32874aea 3389
c9def1b8 3390 r = GetKeyboardState(keystate);
32874aea 3391 if (!r)
3392 memset(keystate, 0, sizeof(keystate));
3393 else {
ec55b220 3394#if 0
4eeb7d09 3395#define SHOW_TOASCII_RESULT
32874aea 3396 { /* Tell us all about key events */
3397 static BYTE oldstate[256];
3398 static int first = 1;
3399 static int scan;
3400 int ch;
3401 if (first)
3402 memcpy(oldstate, keystate, sizeof(oldstate));
3403 first = 0;
3404
3405 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3406 debug(("+"));
3407 } else if ((HIWORD(lParam) & KF_UP)
3408 && scan == (HIWORD(lParam) & 0xFF)) {
3409 debug((". U"));
3410 } else {
3411 debug((".\n"));
3412 if (wParam >= VK_F1 && wParam <= VK_F20)
3413 debug(("K_F%d", wParam + 1 - VK_F1));
3414 else
3415 switch (wParam) {
3416 case VK_SHIFT:
3417 debug(("SHIFT"));
3418 break;
3419 case VK_CONTROL:
3420 debug(("CTRL"));
3421 break;
3422 case VK_MENU:
3423 debug(("ALT"));
3424 break;
3425 default:
3426 debug(("VK_%02x", wParam));
3427 }
3428 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3429 debug(("*"));
3430 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3431
3432 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3433 if (ch >= ' ' && ch <= '~')
3434 debug((", '%c'", ch));
3435 else if (ch)
3436 debug((", $%02x", ch));
3437
3438 if (keys[0])
3439 debug((", KB0=%02x", keys[0]));
3440 if (keys[1])
3441 debug((", KB1=%02x", keys[1]));
3442 if (keys[2])
3443 debug((", KB2=%02x", keys[2]));
3444
3445 if ((keystate[VK_SHIFT] & 0x80) != 0)
3446 debug((", S"));
3447 if ((keystate[VK_CONTROL] & 0x80) != 0)
3448 debug((", C"));
3449 if ((HIWORD(lParam) & KF_EXTENDED))
3450 debug((", E"));
3451 if ((HIWORD(lParam) & KF_UP))
3452 debug((", U"));
3453 }
3454
3455 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3456 else if ((HIWORD(lParam) & KF_UP))
3457 oldstate[wParam & 0xFF] ^= 0x80;
3458 else
3459 oldstate[wParam & 0xFF] ^= 0x81;
3460
3461 for (ch = 0; ch < 256; ch++)
3462 if (oldstate[ch] != keystate[ch])
3463 debug((", M%02x=%02x", ch, keystate[ch]));
3464
3465 memcpy(oldstate, keystate, sizeof(oldstate));
3466 }
ec55b220 3467#endif
3468
32874aea 3469 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 3470 keystate[VK_RMENU] = keystate[VK_MENU];
3471 }
3472
c9def1b8 3473
3474 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
5789bc94 3475 if ((cfg.funky_type == FUNKY_VT400 ||
3476 (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
887035a5 3477 !cfg.no_applic_k))
32874aea 3478 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 3479
3480 wParam = VK_EXECUTE;
3481
3482 /* UnToggle NUMLock */
32874aea 3483 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3484 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 3485 }
3486
3487 /* And write back the 'adjusted' state */
32874aea 3488 SetKeyboardState(keystate);
c9def1b8 3489 }
3490
3491 /* Disable Auto repeat if required */
887035a5 3492 if (term->repeat_off &&
3493 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
32874aea 3494 return 0;
c9def1b8 3495
32874aea 3496 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 3497 left_alt = 1;
3498
32874aea 3499 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 3500
95bbe1ae 3501 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 3502 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 3503 if (cfg.ctrlaltkeys)
3504 keystate[VK_MENU] = 0;
3505 else {
3506 keystate[VK_RMENU] = 0x80;
3507 left_alt = 0;
3508 }
3509 }
c9def1b8 3510
3511 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3512 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3513 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3514
95bbe1ae 3515 /* Note if AltGr was pressed and if it was used as a compose key */
3516 if (!compose_state) {
159eba53 3517 compose_key = 0x100;
95bbe1ae 3518 if (cfg.compose_key) {
32874aea 3519 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3520 compose_key = wParam;
3521 }
3522 if (wParam == VK_APPS)
3523 compose_key = wParam;
3524 }
3525
32874aea 3526 if (wParam == compose_key) {
3527 if (compose_state == 0
3528 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3529 1;
3530 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3531 compose_state = 2;
3532 else
3533 compose_state = 0;
32874aea 3534 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3535 compose_state = 0;
3536
32874aea 3537 if (compose_state > 1 && left_alt)
3538 compose_state = 0;
67c339f7 3539
c9def1b8 3540 /* Sanitize the number pad if not using a PC NumPad */
887035a5 3541 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
5789bc94 3542 && cfg.funky_type != FUNKY_XTERM)
3543 || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
32874aea 3544 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3545 int nParam = 0;
32874aea 3546 switch (wParam) {
3547 case VK_INSERT:
3548 nParam = VK_NUMPAD0;
3549 break;
3550 case VK_END:
3551 nParam = VK_NUMPAD1;
3552 break;
3553 case VK_DOWN:
3554 nParam = VK_NUMPAD2;
3555 break;
3556 case VK_NEXT:
3557 nParam = VK_NUMPAD3;
3558 break;
3559 case VK_LEFT:
3560 nParam = VK_NUMPAD4;
3561 break;
3562 case VK_CLEAR:
3563 nParam = VK_NUMPAD5;
3564 break;
3565 case VK_RIGHT:
3566 nParam = VK_NUMPAD6;
3567 break;
3568 case VK_HOME:
3569 nParam = VK_NUMPAD7;
3570 break;
3571 case VK_UP:
3572 nParam = VK_NUMPAD8;
3573 break;
3574 case VK_PRIOR:
3575 nParam = VK_NUMPAD9;
3576 break;
3577 case VK_DELETE:
3578 nParam = VK_DECIMAL;
3579 break;
c9def1b8 3580 }
32874aea 3581 if (nParam) {
3582 if (keystate[VK_NUMLOCK] & 1)
3583 shift_state |= 1;
c9def1b8 3584 wParam = nParam;
3585 }
25d39ef6 3586 }
3587 }
3588
c9def1b8 3589 /* If a key is pressed and AltGr is not active */
32874aea 3590 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3591 /* Okay, prepare for most alts then ... */
3592 if (left_alt)
3593 *p++ = '\033';
374330e2 3594
c9def1b8 3595 /* Lets see if it's a pattern we know all about ... */
3596 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3597 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3598 return 0;
c9def1b8 3599 }
153580da 3600 if (wParam == VK_PRIOR && shift_state == 2) {
3601 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3602 return 0;
3603 }
c9def1b8 3604 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3605 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3606 return 0;
3607 }
153580da 3608 if (wParam == VK_NEXT && shift_state == 2) {
3609 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3610 return 0;
3611 }
32874aea 3612 if (wParam == VK_INSERT && shift_state == 1) {
887035a5 3613 term_do_paste(term);
32874aea 3614 return 0;
3615 }
c9def1b8 3616 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3617 return -1;
c9def1b8 3618 }
3619 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3620 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3621 return -1;
c9def1b8 3622 }
2cae8529 3623 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3624 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3625 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3626 flip_full_screen();
8f57d753 3627 return -1;
3628 }
ec55b220 3629 /* Control-Numlock for app-keypad mode switch */
3630 if (wParam == VK_PAUSE && shift_state == 2) {
887035a5 3631 term->app_keypad_keys ^= 1;
ec55b220 3632 return 0;
3633 }
374330e2 3634
c9def1b8 3635 /* Nethack keypad */
3636 if (cfg.nethack_keypad && !left_alt) {
32874aea 3637 switch (wParam) {
3638 case VK_NUMPAD1:
3639 *p++ = shift_state ? 'B' : 'b';
3640 return p - output;
3641 case VK_NUMPAD2:
3642 *p++ = shift_state ? 'J' : 'j';
3643 return p - output;
3644 case VK_NUMPAD3:
3645 *p++ = shift_state ? 'N' : 'n';
3646 return p - output;
3647 case VK_NUMPAD4:
3648 *p++ = shift_state ? 'H' : 'h';
3649 return p - output;
3650 case VK_NUMPAD5:
3651 *p++ = shift_state ? '.' : '.';
3652 return p - output;
3653 case VK_NUMPAD6:
3654 *p++ = shift_state ? 'L' : 'l';
3655 return p - output;
3656 case VK_NUMPAD7:
3657 *p++ = shift_state ? 'Y' : 'y';
3658 return p - output;
3659 case VK_NUMPAD8:
3660 *p++ = shift_state ? 'K' : 'k';
3661 return p - output;
3662 case VK_NUMPAD9:
3663 *p++ = shift_state ? 'U' : 'u';
3664 return p - output;
3665 }
c9def1b8 3666 }
3667
3668 /* Application Keypad */
3669 if (!left_alt) {
32874aea 3670 int xkey = 0;
3671
5789bc94 3672 if (cfg.funky_type == FUNKY_VT400 ||
3673 (cfg.funky_type <= FUNKY_LINUX &&
887035a5 3674 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
32874aea 3675 case VK_EXECUTE:
3676 xkey = 'P';
3677 break;
3678 case VK_DIVIDE:
3679 xkey = 'Q';
3680 break;
3681 case VK_MULTIPLY:
3682 xkey = 'R';
3683 break;
3684 case VK_SUBTRACT:
3685 xkey = 'S';
3686 break;
3687 }
887035a5 3688 if (term->app_keypad_keys && !cfg.no_applic_k)
32874aea 3689 switch (wParam) {
3690 case VK_NUMPAD0:
3691 xkey = 'p';
3692 break;
3693 case VK_NUMPAD1:
3694 xkey = 'q';
3695 break;
3696 case VK_NUMPAD2:
3697 xkey = 'r';
3698 break;
3699 case VK_NUMPAD3:
3700 xkey = 's';
3701 break;
3702 case VK_NUMPAD4:
3703 xkey = 't';
3704 break;
3705 case VK_NUMPAD5:
3706 xkey = 'u';
3707 break;
3708 case VK_NUMPAD6:
3709 xkey = 'v';
3710 break;
3711 case VK_NUMPAD7:
3712 xkey = 'w';
3713 break;
3714 case VK_NUMPAD8:
3715 xkey = 'x';
3716 break;
3717 case VK_NUMPAD9:
3718 xkey = 'y';
3719 break;
3720
3721 case VK_DECIMAL:
3722 xkey = 'n';
3723 break;
3724 case VK_ADD:
5789bc94 3725 if (cfg.funky_type == FUNKY_XTERM) {
32874aea 3726 if (shift_state)
3727 xkey = 'l';
3728 else
3729 xkey = 'k';
3730 } else if (shift_state)
3731 xkey = 'm';
c9def1b8 3732 else
32874aea 3733 xkey = 'l';
3734 break;
3735
3736 case VK_DIVIDE:
5789bc94 3737 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3738 xkey = 'o';
3739 break;
3740 case VK_MULTIPLY:
5789bc94 3741 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3742 xkey = 'j';
3743 break;
3744 case VK_SUBTRACT:
5789bc94 3745 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3746 xkey = 'm';
3747 break;
3748
3749 case VK_RETURN:
3750 if (HIWORD(lParam) & KF_EXTENDED)
3751 xkey = 'M';
3752 break;
c9def1b8 3753 }
32874aea 3754 if (xkey) {
887035a5 3755 if (term->vt52_mode) {
32874aea 3756 if (xkey >= 'P' && xkey <= 'S')
3757 p += sprintf((char *) p, "\x1B%c", xkey);
3758 else
3759 p += sprintf((char *) p, "\x1B?%c", xkey);
3760 } else
3761 p += sprintf((char *) p, "\x1BO%c", xkey);
3762 return p - output;
c9def1b8 3763 }
3764 }
3765
32874aea 3766 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3767 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3768 *p++ = 0;
3769 return -2;
c9def1b8 3770 }
e8e8d6e2 3771 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3772 /* We do the opposite of what is configured */
3773 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3774 *p++ = 0;
3775 return -2;
3776 }
32874aea 3777 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3778 *p++ = 0x1B;
3779 *p++ = '[';
3780 *p++ = 'Z';
3781 return p - output;
c9def1b8 3782 }
32874aea 3783 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3784 *p++ = 0;
3785 return p - output;
c9def1b8 3786 }
32874aea 3787 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3788 *p++ = 160;
3789 return p - output;
c9def1b8 3790 }
32874aea 3791 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3792 *p++ = 3;
a5f3e637 3793 *p++ = 0;
3794 return -2;
c9def1b8 3795 }
32874aea 3796 if (wParam == VK_PAUSE) { /* Break/Pause */
3797 *p++ = 26;
3798 *p++ = 0;
3799 return -2;
95bbe1ae 3800 }
c9def1b8 3801 /* Control-2 to Control-8 are special */
32874aea 3802 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3803 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3804 return p - output;
3805 }
237f2b6e 3806 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
c9def1b8 3807 *p++ = 0x1F;
3808 return p - output;
3809 }
3810 if (shift_state == 2 && wParam == 0xDF) {
3811 *p++ = 0x1C;
3812 return p - output;
3813 }
9aa461e4 3814 if (shift_state == 3 && wParam == 0xDE) {
3815 *p++ = 0x1E; /* Ctrl-~ == Ctrl-^ in xterm at least */
3816 return p - output;
3817 }
887035a5 3818 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
32874aea 3819 *p++ = '\r';
3820 *p++ = '\n';
c9def1b8 3821 return p - output;
3822 }
374330e2 3823
c5e9c988 3824 /*
c9def1b8 3825 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3826 * for integer decimal nn.)
3827 *
3828 * We also deal with the weird ones here. Linux VCs replace F1
3829 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3830 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3831 * respectively.
c5e9c988 3832 */
c9def1b8 3833 code = 0;
3834 switch (wParam) {
32874aea 3835 case VK_F1:
3836 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3837 break;
3838 case VK_F2:
3839 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3840 break;
3841 case VK_F3:
3842 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3843 break;
3844 case VK_F4:
3845 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3846 break;
3847 case VK_F5:
3848 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3849 break;
3850 case VK_F6:
3851 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3852 break;
3853 case VK_F7:
3854 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3855 break;
3856 case VK_F8:
3857 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3858 break;
3859 case VK_F9:
3860 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3861 break;
3862 case VK_F10:
3863 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3864 break;
3865 case VK_F11:
3866 code = 23;
3867 break;
3868 case VK_F12:
3869 code = 24;
3870 break;
3871 case VK_F13:
3872 code = 25;
3873 break;
3874 case VK_F14:
3875 code = 26;
3876 break;
3877 case VK_F15:
3878 code = 28;
3879 break;
3880 case VK_F16:
3881 code = 29;
3882 break;
3883 case VK_F17:
3884 code = 31;
3885 break;
3886 case VK_F18:
3887 code = 32;
3888 break;
3889 case VK_F19:
3890 code = 33;
3891 break;
3892 case VK_F20:
3893 code = 34;
3894 break;
dfca2656 3895 }
3896 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3897 case VK_HOME:
3898 code = 1;
3899 break;
3900 case VK_INSERT:
3901 code = 2;
3902 break;
3903 case VK_DELETE:
3904 code = 3;
3905 break;
3906 case VK_END:
3907 code = 4;
3908 break;
3909 case VK_PRIOR:
3910 code = 5;
3911 break;
3912 case VK_NEXT:
3913 code = 6;
3914 break;
374330e2 3915 }
ec55b220 3916 /* Reorder edit keys to physical order */
5789bc94 3917 if (cfg.funky_type == FUNKY_VT400 && code <= 6)
32874aea 3918 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3919
887035a5 3920 if (term->vt52_mode && code > 0 && code <= 6) {
32874aea 3921 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3922 return p - output;
3923 }
3924
5789bc94 3925 if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */
9bc81a2c 3926 code >= 11 && code <= 34) {
e24b1972 3927 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3928 int index = 0;
3929 switch (wParam) {
3930 case VK_F1: index = 0; break;
3931 case VK_F2: index = 1; break;
3932 case VK_F3: index = 2; break;
3933 case VK_F4: index = 3; break;
3934 case VK_F5: index = 4; break;
3935 case VK_F6: index = 5; break;
3936 case VK_F7: index = 6; break;
3937 case VK_F8: index = 7; break;
3938 case VK_F9: index = 8; break;
3939 case VK_F10: index = 9; break;
3940 case VK_F11: index = 10; break;
3941 case VK_F12: index = 11; break;
3942 }
3943 if (keystate[VK_SHIFT] & 0x80) index += 12;
3944 if (keystate[VK_CONTROL] & 0x80) index += 24;
3945 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3946 return p - output;
3947 }
5789bc94 3948 if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */
9bc81a2c 3949 code >= 1 && code <= 6) {
3950 char codes[] = "HL.FIG";
3951 if (code == 3) {
3952 *p++ = '\x7F';
3953 } else {
3954 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3955 }
3956 return p - output;
3957 }
5789bc94 3958 if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
f37caa11 3959 int offt = 0;
32874aea 3960 if (code > 15)
3961 offt++;
3962 if (code > 21)
3963 offt++;
887035a5 3964 if (term->vt52_mode)
32874aea 3965 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3966 else
32874aea 3967 p +=
3968 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3969 return p - output;
3970 }
5789bc94 3971 if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
32874aea 3972 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3973 return p - output;
3974 }
5789bc94 3975 if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
887035a5 3976 if (term->vt52_mode)
32874aea 3977 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3978 else
32874aea 3979 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3980 return p - output;
3981 }
3982 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3983 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3984 return p - output;
3985 }
3986 if (code) {
32874aea 3987 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3988 return p - output;
374330e2 3989 }
45dabbc5 3990
c9def1b8 3991 /*
3992 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3993 * some reason seems to send VK_CLEAR to Windows...).
3994 */
3995 {
3996 char xkey = 0;
3997 switch (wParam) {
32874aea 3998 case VK_UP:
3999 xkey = 'A';
4000 break;
4001 case VK_DOWN:
4002 xkey = 'B';
4003 break;
4004 case VK_RIGHT:
4005 xkey = 'C';
4006 break;
4007 case VK_LEFT:
4008 xkey = 'D';
4009 break;
4010 case VK_CLEAR:
4011 xkey = 'G';
4012 break;
c9def1b8 4013 }
32874aea 4014 if (xkey) {
887035a5 4015 if (term->vt52_mode)
32874aea 4016 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 4017 else {
887035a5 4018 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3a953121 4019#if 0
4020 /*
4021 * RDB: VT100 & VT102 manuals both state the
4022 * app cursor keys only work if the app keypad
4023 * is on.
4024 *
4025 * SGT: That may well be true, but xterm
4026 * disagrees and so does at least one
4027 * application, so I've #if'ed this out and the
4028 * behaviour is back to PuTTY's original: app
4029 * cursor and app keypad are independently
4030 * switchable modes. If anyone complains about
4031 * _this_ I'll have to put in a configurable
4032 * option.
e864f84f 4033 */
887035a5 4034 if (!term->app_keypad_keys)
e864f84f 4035 app_flg = 0;
3a953121 4036#endif
e864f84f 4037 /* Useful mapping of Ctrl-arrows */
4038 if (shift_state == 2)
4039 app_flg = !app_flg;
4040
4041 if (app_flg)
4042 p += sprintf((char *) p, "\x1BO%c", xkey);
4043 else
4044 p += sprintf((char *) p, "\x1B[%c", xkey);
4045 }
c9def1b8 4046 return p - output;
4047 }
4048 }
0c50ef57 4049
4050 /*
4051 * Finally, deal with Return ourselves. (Win95 seems to
4052 * foul it up when Alt is pressed, for some reason.)
4053 */
32874aea 4054 if (wParam == VK_RETURN) { /* Return */
0c50ef57 4055 *p++ = 0x0D;
a5f3e637 4056 *p++ = 0;
4057 return -2;
0c50ef57 4058 }
4eeb7d09 4059
4060 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
4061 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
4062 else
4063 alt_sum = 0;
67c339f7 4064 }
374330e2 4065
c9def1b8 4066 /* Okay we've done everything interesting; let windows deal with
4067 * the boring stuff */
4068 {
a9c02454 4069 BOOL capsOn=0;
4070
4071 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
4072 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
4073 capsOn= !left_alt;
4074 keystate[VK_CAPITAL] = 0;
4075 }
4076
c264be08 4077 /* XXX how do we know what the max size of the keys array should
4078 * be is? There's indication on MS' website of an Inquire/InquireEx
4079 * functioning returning a KBINFO structure which tells us. */
4080 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
4081 /* XXX 'keys' parameter is declared in MSDN documentation as
4082 * 'LPWORD lpChar'.
4083 * The experience of a French user indicates that on
4084 * Win98, WORD[] should be passed in, but on Win2K, it should
4085 * be BYTE[]. German WinXP and my Win2K with "US International"
4086 * driver corroborate this.
4087 * Experimentally I've conditionalised the behaviour on the
4088 * Win9x/NT split, but I suspect it's worse than that.
4089 * See wishlist item `win-dead-keys' for more horrible detail
4090 * and speculations. */
4091 BYTE keybs[3];
4092 int i;
4093 r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
4094 for (i=0; i<3; i++) keys[i] = keybs[i];
4095 } else {
4096 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4097 }
4eeb7d09 4098#ifdef SHOW_TOASCII_RESULT
4099 if (r == 1 && !key_down) {
4100 if (alt_sum) {
21d2b241 4101 if (in_utf(term) || ucsdata.dbcs_screenfont)
4eeb7d09 4102 debug((", (U+%04x)", alt_sum));
4103 else
4104 debug((", LCH(%d)", alt_sum));
4105 } else {
4106 debug((", ACH(%d)", keys[0]));
4107 }
4108 } else if (r > 0) {
4109 int r1;
4110 debug((", ASC("));
4111 for (r1 = 0; r1 < r; r1++) {
4112 debug(("%s%d", r1 ? "," : "", keys[r1]));
4113 }
4114 debug((")"));
4115 }
4116#endif
32874aea 4117 if (r > 0) {
4eeb7d09 4118 WCHAR keybuf;
256cb87c 4119
4120 /*
4121 * Interrupt an ongoing paste. I'm not sure this is
4122 * sensible, but for the moment it's preferable to
4123 * having to faff about buffering things.
4124 */
887035a5 4125 term_nopaste(term);
256cb87c 4126
c9def1b8 4127 p = output;
32874aea 4128 for (i = 0; i < r; i++) {
4129 unsigned char ch = (unsigned char) keys[i];
14963b8f 4130
32874aea 4131 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4132 compose_char = ch;
32874aea 4133 compose_state++;
c9def1b8 4134 continue;
4135 }
32874aea 4136 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4137 int nc;
4138 compose_state = 0;
4139
32874aea 4140 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 4141 MessageBeep(MB_ICONHAND);
c9def1b8 4142 return 0;
4143 }
4eeb7d09 4144 keybuf = nc;
887035a5 4145 term_seen_key_event(term);
e3be8de5 4146 if (ldisc)
4147 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4148 continue;
c9def1b8 4149 }
374330e2 4150
c9def1b8 4151 compose_state = 0;
374330e2 4152
4eeb7d09 4153 if (!key_down) {
4154 if (alt_sum) {
21d2b241 4155 if (in_utf(term) || ucsdata.dbcs_screenfont) {
4eeb7d09 4156 keybuf = alt_sum;
887035a5 4157 term_seen_key_event(term);
e3be8de5 4158 if (ldisc)
4159 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4160 } else {
4161 ch = (char) alt_sum;
5471d09a 4162 /*
4163 * We need not bother about stdin
4164 * backlogs here, because in GUI PuTTY
4165 * we can't do anything about it
4166 * anyway; there's no means of asking
4167 * Windows to hold off on KEYDOWN
4168 * messages. We _have_ to buffer
4169 * everything we're sent.
4170 */
887035a5 4171 term_seen_key_event(term);
e3be8de5 4172 if (ldisc)
4173 ldisc_send(ldisc, &ch, 1, 1);
4eeb7d09 4174 }
4175 alt_sum = 0;
405a0c29 4176 } else {
887035a5 4177 term_seen_key_event(term);
e3be8de5 4178 if (ldisc)
4179 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
405a0c29 4180 }
4eeb7d09 4181 } else {
a9c02454 4182 if(capsOn && ch < 0x80) {
4183 WCHAR cbuf[2];
4184 cbuf[0] = 27;
4185 cbuf[1] = xlat_uskbd2cyrllic(ch);
887035a5 4186 term_seen_key_event(term);
e3be8de5 4187 if (ldisc)
4188 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4189 } else {
4190 char cbuf[2];
4191 cbuf[0] = '\033';
4192 cbuf[1] = ch;
887035a5 4193 term_seen_key_event(term);
e3be8de5 4194 if (ldisc)
4195 lpage_send(ldisc, kbd_codepage,
4196 cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4197 }
c9def1b8 4198 }
bca9517a 4199 show_mouseptr(0);
c9def1b8 4200 }
374330e2 4201
c9def1b8 4202 /* This is so the ALT-Numpad and dead keys work correctly. */
4203 keys[0] = 0;
4204
32874aea 4205 return p - output;
c9def1b8 4206 }
159eba53 4207 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 4208 if (!left_alt)
4209 keys[0] = 0;
4eeb7d09 4210 /* If we will be using alt_sum fix the 256s */
21d2b241 4211 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
4eeb7d09 4212 keys[0] = 10;
374330e2 4213 }
4214
dfca2656 4215 /*
4216 * ALT alone may or may not want to bring up the System menu.
4217 * If it's not meant to, we return 0 on presses or releases of
4218 * ALT, to show that we've swallowed the keystroke. Otherwise
4219 * we return -1, which means Windows will give the keystroke
4220 * its default handling (i.e. bring up the System menu).
4221 */
4222 if (wParam == VK_MENU && !cfg.alt_only)
4223 return 0;
374330e2 4224
c9def1b8 4225 return -1;
374330e2 4226}
4227
a8327734 4228void request_paste(void *frontend)
e6346999 4229{
4230 /*
4231 * In Windows, pasting is synchronous: we can read the
4232 * clipboard with no difficulty, so request_paste() can just go
4233 * ahead and paste.
4234 */
887035a5 4235 term_do_paste(term);
e6346999 4236}
4237
a8327734 4238void set_title(void *frontend, char *title)
32874aea 4239{
4240 sfree(window_name);
3d88e64d 4241 window_name = snewn(1 + strlen(title), char);
32874aea 4242 strcpy(window_name, title);
37508af4 4243 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 4244 SetWindowText(hwnd, title);
374330e2 4245}
4246
a8327734 4247void set_icon(void *frontend, char *title)
32874aea 4248{
4249 sfree(icon_name);
3d88e64d 4250 icon_name = snewn(1 + strlen(title), char);
32874aea 4251 strcpy(icon_name, title);
37508af4 4252 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 4253 SetWindowText(hwnd, title);
374330e2 4254}
4255
a8327734 4256void set_sbar(void *frontend, int total, int start, int page)
32874aea 4257{
374330e2 4258 SCROLLINFO si;
c9def1b8 4259
1ba99d2c 4260 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 4261 return;
c9def1b8 4262
374330e2 4263 si.cbSize = sizeof(si);
c9def1b8 4264 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 4265 si.nMin = 0;
4266 si.nMax = total - 1;
4267 si.nPage = page;
4268 si.nPos = start;
c1f5f956 4269 if (hwnd)
32874aea 4270 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 4271}
4272
a8327734 4273Context get_ctx(void *frontend)
32874aea 4274{
374330e2 4275 HDC hdc;
4276 if (hwnd) {
32874aea 4277 hdc = GetDC(hwnd);
374330e2 4278 if (hdc && pal)
32874aea 4279 SelectPalette(hdc, pal, FALSE);
374330e2 4280 return hdc;
4281 } else
4282 return NULL;
4283}
4284
32874aea 4285void free_ctx(Context ctx)
4286{
4287 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
4288 ReleaseDC(hwnd, ctx);
374330e2 4289}
4290
32874aea 4291static void real_palette_set(int n, int r, int g, int b)
4292{
374330e2 4293 if (pal) {
4294 logpal->palPalEntry[n].peRed = r;
4295 logpal->palPalEntry[n].peGreen = g;
4296 logpal->palPalEntry[n].peBlue = b;
4297 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
4298 colours[n] = PALETTERGB(r, g, b);
cecb13f6 4299 SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
374330e2 4300 } else
4301 colours[n] = RGB(r, g, b);
4302}
4303
a8327734 4304void palette_set(void *frontend, int n, int r, int g, int b)
32874aea 4305{
cecb13f6 4306 if (n >= 16)
4307 n += 256 - 16;
4308 if (n > NALLCOLOURS)
4309 return;
4310 real_palette_set(n, r, g, b);
374330e2 4311 if (pal) {
a8327734 4312 HDC hdc = get_ctx(frontend);
32874aea 4313 UnrealizeObject(pal);
4314 RealizePalette(hdc);
4315 free_ctx(hdc);
374330e2 4316 }
4317}
4318
a8327734 4319void palette_reset(void *frontend)
32874aea 4320{
374330e2 4321 int i;
4322
cecb13f6 4323 /* And this */
4324 for (i = 0; i < NALLCOLOURS; i++) {
374330e2 4325 if (pal) {
4326 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4327 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4328 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4329 logpal->palPalEntry[i].peFlags = 0;
4330 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4331 defpal[i].rgbtGreen,
4332 defpal[i].rgbtBlue);
4333 } else
4334 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 4335 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 4336 }
4337
4338 if (pal) {
4339 HDC hdc;
cecb13f6 4340 SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry);
a8327734 4341 hdc = get_ctx(frontend);
32874aea 4342 RealizePalette(hdc);
4343 free_ctx(hdc);
374330e2 4344 }
4345}
4346
a8327734 4347void write_aclip(void *frontend, char *data, int len, int must_deselect)
32874aea 4348{
374330e2 4349 HGLOBAL clipdata;
4350 void *lock;
4351
32874aea 4352 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 4353 if (!clipdata)
4354 return;
32874aea 4355 lock = GlobalLock(clipdata);
374330e2 4356 if (!lock)
4357 return;
32874aea 4358 memcpy(lock, data, len);
4359 ((unsigned char *) lock)[len] = 0;
4360 GlobalUnlock(clipdata);
374330e2 4361
f0df44da 4362 if (!must_deselect)
32874aea 4363 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 4364
32874aea 4365 if (OpenClipboard(hwnd)) {
374330e2 4366 EmptyClipboard();
32874aea 4367 SetClipboardData(CF_TEXT, clipdata);
374330e2 4368 CloseClipboard();
4369 } else
32874aea 4370 GlobalFree(clipdata);
f0df44da 4371
4372 if (!must_deselect)
32874aea 4373 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 4374}
4375
4eeb7d09 4376/*
4377 * Note: unlike write_aclip() this will not append a nul.
4378 */
a8327734 4379void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4eeb7d09 4380{
a7419ea4 4381 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 4382 int len2;
a7419ea4 4383 void *lock, *lock2, *lock3;
4eeb7d09 4384
4385 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4386
4387 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4388 len * sizeof(wchar_t));
4389 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4390
0e5e7f46 4391 if (!clipdata || !clipdata2) {
4eeb7d09 4392 if (clipdata)
4393 GlobalFree(clipdata);
4394 if (clipdata2)
4395 GlobalFree(clipdata2);
4396 return;
4397 }
4398 if (!(lock = GlobalLock(clipdata)))
4399 return;
4400 if (!(lock2 = GlobalLock(clipdata2)))
4401 return;
4402
4403 memcpy(lock, data, len * sizeof(wchar_t));
4404 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4405
a7419ea4 4406 if (cfg.rtf_paste) {
4407 wchar_t unitab[256];
4408 char *rtf = NULL;
4409 unsigned char *tdata = (unsigned char *)lock2;
4410 wchar_t *udata = (wchar_t *)lock;
4411 int rtflen = 0, uindex = 0, tindex = 0;
4412 int rtfsize = 0;
4413 int multilen, blen, alen, totallen, i;
4414 char before[16], after[4];
4415
4416 get_unitab(CP_ACP, unitab, 0);
4417
9a30e26b 4418 rtfsize = 100 + strlen(cfg.font.name);
3d88e64d 4419 rtf = snewn(rtfsize, char);
a7419ea4 4420 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
9a30e26b 4421 GetACP(), cfg.font.name);
a7419ea4 4422 rtflen = strlen(rtf);
4423
4424 /*
4425 * We want to construct a piece of RTF that specifies the
4426 * same Unicode text. To do this we will read back in
4427 * parallel from the Unicode data in `udata' and the
4428 * non-Unicode data in `tdata'. For each character in
4429 * `tdata' which becomes the right thing in `udata' when
4430 * looked up in `unitab', we just copy straight over from
4431 * tdata. For each one that doesn't, we must WCToMB it
4432 * individually and produce a \u escape sequence.
4433 *
4434 * It would probably be more robust to just bite the bullet
4435 * and WCToMB each individual Unicode character one by one,
4436 * then MBToWC each one back to see if it was an accurate
4437 * translation; but that strikes me as a horrifying number
4438 * of Windows API calls so I want to see if this faster way
4439 * will work. If it screws up badly we can always revert to
4440 * the simple and slow way.
4441 */
4442 while (tindex < len2 && uindex < len &&
4443 tdata[tindex] && udata[uindex]) {
4444 if (tindex + 1 < len2 &&
4445 tdata[tindex] == '\r' &&
4446 tdata[tindex+1] == '\n') {
4447 tindex++;
4448 uindex++;
4449 }
4450 if (unitab[tdata[tindex]] == udata[uindex]) {
4451 multilen = 1;
4452 before[0] = '\0';
4453 after[0] = '\0';
4454 blen = alen = 0;
4455 } else {
4456 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4457 NULL, 0, NULL, NULL);
4458 if (multilen != 1) {
c3b032a6 4459 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4460 udata[uindex]);
a7419ea4 4461 alen = 1; strcpy(after, "}");
4462 } else {
4463 blen = sprintf(before, "\\u%d", udata[uindex]);
4464 alen = 0; after[0] = '\0';
4465 }
4466 }
4467 assert(tindex + multilen <= len2);
4468 totallen = blen + alen;
4469 for (i = 0; i < multilen; i++) {
4470 if (tdata[tindex+i] == '\\' ||
4471 tdata[tindex+i] == '{' ||
4472 tdata[tindex+i] == '}')
4473 totallen += 2;
4474 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4475 totallen += 6; /* \par\r\n */
4476 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4477 totallen += 4;
4478 else
4479 totallen++;
4480 }
4481
4482 if (rtfsize < rtflen + totallen + 3) {
4483 rtfsize = rtflen + totallen + 512;
3d88e64d 4484 rtf = sresize(rtf, rtfsize, char);
a7419ea4 4485 }
4486
4487 strcpy(rtf + rtflen, before); rtflen += blen;
4488 for (i = 0; i < multilen; i++) {
4489 if (tdata[tindex+i] == '\\' ||
4490 tdata[tindex+i] == '{' ||
4491 tdata[tindex+i] == '}') {
4492 rtf[rtflen++] = '\\';
4493 rtf[rtflen++] = tdata[tindex+i];
4494 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4495 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4496 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4497 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4498 } else {
4499 rtf[rtflen++] = tdata[tindex+i];
4500 }
4501 }
4502 strcpy(rtf + rtflen, after); rtflen += alen;
4503
4504 tindex += multilen;
4505 uindex++;
4506 }
4507
4508 strcpy(rtf + rtflen, "}");
4509 rtflen += 2;
4510
4511 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4512 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4513 strcpy(lock3, rtf);
4514 GlobalUnlock(clipdata3);
4515 }
4516 sfree(rtf);
4517 } else
4518 clipdata3 = NULL;
4519
4eeb7d09 4520 GlobalUnlock(clipdata);
4521 GlobalUnlock(clipdata2);
4522
4523 if (!must_deselect)
4524 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4525
4526 if (OpenClipboard(hwnd)) {
4527 EmptyClipboard();
4528 SetClipboardData(CF_UNICODETEXT, clipdata);
4529 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4530 if (clipdata3)
4531 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4532 CloseClipboard();
4533 } else {
4534 GlobalFree(clipdata);
4535 GlobalFree(clipdata2);
4536 }
4537
4538 if (!must_deselect)
4539 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4540}
4541
a8327734 4542void get_clip(void *frontend, wchar_t ** p, int *len)
32874aea 4543{
374330e2 4544 static HGLOBAL clipdata = NULL;
4eeb7d09 4545 static wchar_t *converted = 0;
4546 wchar_t *p2;
374330e2 4547
4eeb7d09 4548 if (converted) {
4549 sfree(converted);
4550 converted = 0;
4551 }
374330e2 4552 if (!p) {
4553 if (clipdata)
32874aea 4554 GlobalUnlock(clipdata);
374330e2 4555 clipdata = NULL;
4556 return;
4eeb7d09 4557 } else if (OpenClipboard(NULL)) {
2d466ffd 4558 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4559 CloseClipboard();
4eeb7d09 4560 *p = GlobalLock(clipdata);
4561 if (*p) {
4562 for (p2 = *p; *p2; p2++);
4563 *len = p2 - *p;
4564 return;
374330e2 4565 }
2d466ffd 4566 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4567 char *s;
4568 int i;
4569 CloseClipboard();
4570 s = GlobalLock(clipdata);
4571 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3d88e64d 4572 *p = converted = snewn(i, wchar_t);
4eeb7d09 4573 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4574 *len = i - 1;
4575 return;
4576 } else
4577 CloseClipboard();
374330e2 4578 }
4579
4580 *p = NULL;
4581 *len = 0;
4582}
4583
4eeb7d09 4584#if 0
374330e2 4585/*
4586 * Move `lines' lines from position `from' to position `to' in the
4587 * window.
4588 */
a8327734 4589void optimised_move(void *frontend, int to, int from, int lines)
32874aea 4590{
374330e2 4591 RECT r;
f67b4e85 4592 int min, max;
374330e2 4593
4594 min = (to < from ? to : from);
4595 max = to + from - min;
374330e2 4596
5a73255e 4597 r.left = offset_width;
887035a5 4598 r.right = offset_width + term->cols * font_width;
5a73255e 4599 r.top = offset_height + min * font_height;
4600 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4601 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4602}
4eeb7d09 4603#endif
374330e2 4604
4605/*
4606 * Print a message box and perform a fatal exit.
4607 */
32874aea 4608void fatalbox(char *fmt, ...)
4609{
374330e2 4610 va_list ap;
971bcc0a 4611 char *stuff, morestuff[100];
374330e2 4612
4613 va_start(ap, fmt);
971bcc0a 4614 stuff = dupvprintf(fmt, ap);
374330e2 4615 va_end(ap);
f6f450e2 4616 sprintf(morestuff, "%.70s Fatal Error", appname);
4617 MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
971bcc0a 4618 sfree(stuff);
93b581bd 4619 cleanup_exit(1);
374330e2 4620}
4621
4622/*
1709795f 4623 * Print a modal (Really Bad) message box and perform a fatal exit.
4624 */
4625void modalfatalbox(char *fmt, ...)
4626{
4627 va_list ap;
971bcc0a 4628 char *stuff, morestuff[100];
1709795f 4629
4630 va_start(ap, fmt);
971bcc0a 4631 stuff = dupvprintf(fmt, ap);
1709795f 4632 va_end(ap);
f6f450e2 4633 sprintf(morestuff, "%.70s Fatal Error", appname);
4634 MessageBox(hwnd, stuff, morestuff,
1709795f 4635 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
971bcc0a 4636 sfree(stuff);
1709795f 4637 cleanup_exit(1);
4638}
4639
39934deb 4640static void flash_window(int mode);
4641static long next_flash;
4642static int flashing = 0;
4643
4644static void flash_window_timer(void *ctx, long now)
4645{
4646 if (flashing && now - next_flash >= 0) {
4647 flash_window(1);
4648 }
4649}
4650
1709795f 4651/*
f8a28d1f 4652 * Manage window caption / taskbar flashing, if enabled.
4653 * 0 = stop, 1 = maintain, 2 = start
4654 */
4655static void flash_window(int mode)
4656{
f8a28d1f 4657 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4658 /* stop */
4659 if (flashing) {
4660 FlashWindow(hwnd, FALSE);
4661 flashing = 0;
4662 }
4663
4664 } else if (mode == 2) {
4665 /* start */
4666 if (!flashing) {
f8a28d1f 4667 flashing = 1;
4668 FlashWindow(hwnd, TRUE);
39934deb 4669 next_flash = schedule_timer(450, flash_window_timer, hwnd);
f8a28d1f 4670 }
4671
4672 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4673 /* maintain */
4674 if (flashing) {
39934deb 4675 FlashWindow(hwnd, TRUE); /* toggle */
4676 next_flash = schedule_timer(450, flash_window_timer, hwnd);
f8a28d1f 4677 }
4678 }
4679}
4680
4681/*
374330e2 4682 * Beep.
4683 */
a8327734 4684void beep(void *frontend, int mode)
32874aea 4685{
03169ad0 4686 if (mode == BELL_DEFAULT) {
eb04402e 4687 /*
4688 * For MessageBeep style bells, we want to be careful of
4689 * timing, because they don't have the nice property of
4690 * PlaySound bells that each one cancels the previous
4691 * active one. So we limit the rate to one per 50ms or so.
4692 */
4693 static long lastbeep = 0;
d51cdf1e 4694 long beepdiff;
eb04402e 4695
d51cdf1e 4696 beepdiff = GetTickCount() - lastbeep;
eb04402e 4697 if (beepdiff >= 0 && beepdiff < 50)
4698 return;
156686ef 4699 MessageBeep(MB_OK);
d51cdf1e 4700 /*
4701 * The above MessageBeep call takes time, so we record the
4702 * time _after_ it finishes rather than before it starts.
4703 */
4704 lastbeep = GetTickCount();
03169ad0 4705 } else if (mode == BELL_WAVEFILE) {
9a30e26b 4706 if (!PlaySound(cfg.bell_wavefile.path, NULL,
4707 SND_ASYNC | SND_FILENAME)) {
850323f9 4708 char buf[sizeof(cfg.bell_wavefile.path) + 80];
f6f450e2 4709 char otherbuf[100];
03169ad0 4710 sprintf(buf, "Unable to play sound file\n%s\n"
debe102c 4711 "Using default sound instead", cfg.bell_wavefile.path);
f6f450e2 4712 sprintf(otherbuf, "%.70s Sound Error", appname);
4713 MessageBox(hwnd, buf, otherbuf,
32874aea 4714 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4715 cfg.beep = BELL_DEFAULT;
4716 }
85f6b361 4717 } else if (mode == BELL_PCSPEAKER) {
4718 static long lastbeep = 0;
4719 long beepdiff;
4720
4721 beepdiff = GetTickCount() - lastbeep;
4722 if (beepdiff >= 0 && beepdiff < 50)
4723 return;
4724
4725 /*
4726 * We must beep in different ways depending on whether this
4727 * is a 95-series or NT-series OS.
4728 */
4729 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
4730 Beep(800, 100);
4731 else
4732 MessageBeep(-1);
4733 lastbeep = GetTickCount();
03169ad0 4734 }
f8a28d1f 4735 /* Otherwise, either visual bell or disabled; do nothing here */
887035a5 4736 if (!term->has_focus) {
f8a28d1f 4737 flash_window(2); /* start */
4738 }
374330e2 4739}
8f57d753 4740
4741/*
68f9b3d9 4742 * Minimise or restore the window in response to a server-side
4743 * request.
4744 */
a8327734 4745void set_iconic(void *frontend, int iconic)
68f9b3d9 4746{
4747 if (IsIconic(hwnd)) {
4748 if (!iconic)
4749 ShowWindow(hwnd, SW_RESTORE);
4750 } else {
4751 if (iconic)
4752 ShowWindow(hwnd, SW_MINIMIZE);
4753 }
4754}
4755
4756/*
4757 * Move the window in response to a server-side request.
4758 */
a8327734 4759void move_window(void *frontend, int x, int y)
68f9b3d9 4760{
d1e0a352 4761 if (cfg.resize_action == RESIZE_DISABLED ||
4762 cfg.resize_action == RESIZE_FONT ||
4763 IsZoomed(hwnd))
4764 return;
4765
68f9b3d9 4766 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4767}
4768
4769/*
4770 * Move the window to the top or bottom of the z-order in response
4771 * to a server-side request.
4772 */
a8327734 4773void set_zorder(void *frontend, int top)
68f9b3d9 4774{
1ba99d2c 4775 if (cfg.alwaysontop)
68f9b3d9 4776 return; /* ignore */
4777 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4778 SWP_NOMOVE | SWP_NOSIZE);
4779}
4780
4781/*
4782 * Refresh the window in response to a server-side request.
4783 */
a8327734 4784void refresh_window(void *frontend)
68f9b3d9 4785{
4786 InvalidateRect(hwnd, NULL, TRUE);
4787}
4788
4789/*
4790 * Maximise or restore the window in response to a server-side
4791 * request.
4792 */
a8327734 4793void set_zoomed(void *frontend, int zoomed)
68f9b3d9 4794{
1ba99d2c 4795 if (IsZoomed(hwnd)) {
4796 if (!zoomed)
4797 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4798 } else {
4799 if (zoomed)
4800 ShowWindow(hwnd, SW_MAXIMIZE);
4801 }
4802}
4803
4804/*
4805 * Report whether the window is iconic, for terminal reports.
4806 */
a8327734 4807int is_iconic(void *frontend)
68f9b3d9 4808{
4809 return IsIconic(hwnd);
4810}
4811
4812/*
4813 * Report the window's position, for terminal reports.
4814 */
a8327734 4815void get_window_pos(void *frontend, int *x, int *y)
68f9b3d9 4816{
4817 RECT r;
4818 GetWindowRect(hwnd, &r);
4819 *x = r.left;
4820 *y = r.top;
4821}
4822
4823/*
4824 * Report the window's pixel size, for terminal reports.
4825 */
a8327734 4826void get_window_pixels(void *frontend, int *x, int *y)
68f9b3d9 4827{
4828 RECT r;
4829 GetWindowRect(hwnd, &r);
4830 *x = r.right - r.left;
4831 *y = r.bottom - r.top;
4832}
4833
4834/*
4835 * Return the window or icon title.
4836 */
a8327734 4837char *get_window_title(void *frontend, int icon)
68f9b3d9 4838{
4839 return icon ? icon_name : window_name;
4840}
1ba99d2c 4841
4842/*
4843 * See if we're in full-screen mode.
4844 */
4e95095a 4845static int is_full_screen()
1ba99d2c 4846{
4847 if (!IsZoomed(hwnd))
4848 return FALSE;
4849 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4850 return FALSE;
4851 return TRUE;
4852}
4853
d0f1bcb4 4854/* Get the rect/size of a full screen window using the nearest available
4855 * monitor in multimon systems; default to something sensible if only
4856 * one monitor is present. */
4857static int get_fullscreen_rect(RECT * ss)
4858{
5d145a14 4859#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
d0f1bcb4 4860 HMONITOR mon;
4861 MONITORINFO mi;
4862 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4863 mi.cbSize = sizeof(mi);
4864 GetMonitorInfo(mon, &mi);
4865
4866 /* structure copy */
4867 *ss = mi.rcMonitor;
4868 return TRUE;
4869#else
4870/* could also use code like this:
4871 ss->left = ss->top = 0;
4872 ss->right = GetSystemMetrics(SM_CXSCREEN);
4873 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4874*/
e6f8202f 4875 return GetClientRect(GetDesktopWindow(), ss);
d0f1bcb4 4876#endif
4877}
4878
4879
1ba99d2c 4880/*
4881 * Go full-screen. This should only be called when we are already
4882 * maximised.
4883 */
4e95095a 4884static void make_full_screen()
1ba99d2c 4885{
4886 DWORD style;
d0f1bcb4 4887 RECT ss;
1ba99d2c 4888
4889 assert(IsZoomed(hwnd));
4890
d0f1bcb4 4891 if (is_full_screen())
4892 return;
4893
1ba99d2c 4894 /* Remove the window furniture. */
4895 style = GetWindowLong(hwnd, GWL_STYLE);
4896 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4897 if (cfg.scrollbar_in_fullscreen)
4898 style |= WS_VSCROLL;
4899 else
4900 style &= ~WS_VSCROLL;
4901 SetWindowLong(hwnd, GWL_STYLE, style);
4902
4903 /* Resize ourselves to exactly cover the nearest monitor. */
d0f1bcb4 4904 get_fullscreen_rect(&ss);
4905 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4906 ss.right - ss.left,
4907 ss.bottom - ss.top,
4908 SWP_FRAMECHANGED);
1ba99d2c 4909
4910 /* Tick the menu item in the System menu. */
4911 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4912 MF_CHECKED);
4913}
4914
4915/*
4916 * Clear the full-screen attributes.
4917 */
4e95095a 4918static void clear_full_screen()
1ba99d2c 4919{
4920 DWORD oldstyle, style;
4921
4922 /* Reinstate the window furniture. */
4923 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4924 style |= WS_CAPTION | WS_BORDER;
4925 if (cfg.resize_action == RESIZE_DISABLED)
4926 style &= ~WS_THICKFRAME;
4927 else
4928 style |= WS_THICKFRAME;
1ba99d2c 4929 if (cfg.scrollbar)
4930 style |= WS_VSCROLL;
4931 else
4932 style &= ~WS_VSCROLL;
4933 if (style != oldstyle) {
4934 SetWindowLong(hwnd, GWL_STYLE, style);
4935 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4936 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4937 SWP_FRAMECHANGED);
4938 }
4939
4940 /* Untick the menu item in the System menu. */
4941 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4942 MF_UNCHECKED);
4943}
4944
4945/*
4946 * Toggle full-screen mode.
4947 */
4e95095a 4948static void flip_full_screen()
1ba99d2c 4949{
4950 if (is_full_screen()) {
4951 ShowWindow(hwnd, SW_RESTORE);
4952 } else if (IsZoomed(hwnd)) {
4953 make_full_screen();
4954 } else {
4955 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4956 ShowWindow(hwnd, SW_MAXIMIZE);
4957 }
4958}
ab2c86b9 4959
b9d7bcad 4960void frontend_keypress(void *handle)
ab2c86b9 4961{
4962 /*
4963 * Keypress termination in non-Close-On-Exit mode is not
4964 * currently supported in PuTTY proper, because the window
4965 * always has a perfectly good Close button anyway. So we do
4966 * nothing here.
4967 */
4968 return;
4969}
fbf6cb3b 4970
4971int from_backend(void *frontend, int is_stderr, const char *data, int len)
4972{
4973 return term_data(term, is_stderr, data, len);
4974}
c44bf5bd 4975
4976void agent_schedule_callback(void (*callback)(void *, void *, int),
4977 void *callback_ctx, void *data, int len)
4978{
4979 struct agent_callback *c = snew(struct agent_callback);
4980 c->callback = callback;
4981 c->callback_ctx = callback_ctx;
4982 c->data = data;
4983 c->len = len;
4984 PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
4985}