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