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