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