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