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