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