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