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