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