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