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