faq-alternate-localhost: You can now download the fix directly rather than
[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') {
5dd103a8 528 char *atsign = strrchr(cfg.host, '@');
13eafebf 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,
0353864c 948 (UINT) p, "S&pecial Command");
63be7767 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;
49a332ff 2770 case WM_SYSCOLORCHANGE:
2771 if (cfg.system_colour) {
2772 /* Refresh palette from system colours. */
2773 /* XXX actually this zaps the entire palette. */
2774 systopalette();
2775 init_palette();
2776 /* Force a repaint of the terminal window. */
2777 term_invalidate(term);
2778 }
2779 break;
c44bf5bd 2780 case WM_AGENT_CALLBACK:
2781 {
2782 struct agent_callback *c = (struct agent_callback *)lParam;
2783 c->callback(c->callback_ctx, c->data, c->len);
2784 sfree(c);
2785 }
2786 return 0;
011b0e33 2787 default:
16837214 2788 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
c1b55581 2789 int shift_pressed=0, control_pressed=0;
011b0e33 2790
2791 if (message == WM_MOUSEWHEEL) {
2792 wheel_accumulator += (short)HIWORD(wParam);
2793 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2794 control_pressed=LOWORD(wParam) & MK_CONTROL;
2795 } else {
2796 BYTE keys[256];
2797 wheel_accumulator += (int)wParam;
2798 if (GetKeyboardState(keys)!=0) {
2799 shift_pressed=keys[VK_SHIFT]&0x80;
2800 control_pressed=keys[VK_CONTROL]&0x80;
2801 }
2802 }
2803
2804 /* process events when the threshold is reached */
2805 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2806 int b;
2807
2808 /* reduce amount for next time */
2809 if (wheel_accumulator > 0) {
2810 b = MBT_WHEEL_UP;
2811 wheel_accumulator -= WHEEL_DELTA;
2812 } else if (wheel_accumulator < 0) {
2813 b = MBT_WHEEL_DOWN;
2814 wheel_accumulator += WHEEL_DELTA;
2815 } else
2816 break;
2817
2818 if (send_raw_mouse &&
2819 !(cfg.mouse_override && shift_pressed)) {
2820 /* send a mouse-down followed by a mouse up */
fc5b0934 2821 term_mouse(term, b, translate_button(b),
011b0e33 2822 MA_CLICK,
2823 TO_CHR_X(X_POS(lParam)),
2824 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2825 control_pressed, is_alt_pressed());
fc5b0934 2826 term_mouse(term, b, translate_button(b),
2827 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
011b0e33 2828 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2829 control_pressed, is_alt_pressed());
2830 } else {
2831 /* trigger a scroll */
887035a5 2832 term_scroll(term, 0,
2833 b == MBT_WHEEL_UP ?
2834 -term->rows / 2 : term->rows / 2);
011b0e33 2835 }
2836 }
2837 return 0;
2838 }
374330e2 2839 }
2840
32874aea 2841 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2842}
2843
2844/*
ec8679e9 2845 * Move the system caret. (We maintain one, even though it's
2846 * invisible, for the benefit of blind people: apparently some
2847 * helper software tracks the system caret, so we should arrange to
2848 * have one.)
2849 */
a8327734 2850void sys_cursor(void *frontend, int x, int y)
32874aea 2851{
c1da5066 2852 int cx, cy;
2853
887035a5 2854 if (!term->has_focus) return;
c1da5066 2855
2856 /*
2857 * Avoid gratuitously re-updating the cursor position and IMM
2858 * window if there's no actual change required.
2859 */
2860 cx = x * font_width + offset_width;
2861 cy = y * font_height + offset_height;
2862 if (cx == caret_x && cy == caret_y)
2863 return;
2864 caret_x = cx;
2865 caret_y = cy;
2866
2867 sys_cursor_update();
2868}
2869
2870static void sys_cursor_update(void)
2871{
88485e4d 2872 COMPOSITIONFORM cf;
2873 HIMC hIMC;
2874
887035a5 2875 if (!term->has_focus) return;
c1da5066 2876
2877 if (caret_x < 0 || caret_y < 0)
2878 return;
2879
2880 SetCaretPos(caret_x, caret_y);
88485e4d 2881
2882 /* IMM calls on Win98 and beyond only */
2883 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2884
2885 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2886 osVersion.dwMinorVersion == 0) return; /* 95 */
2887
2888 /* we should have the IMM functions */
2889 hIMC = ImmGetContext(hwnd);
2890 cf.dwStyle = CFS_POINT;
c1da5066 2891 cf.ptCurrentPos.x = caret_x;
2892 cf.ptCurrentPos.y = caret_y;
88485e4d 2893 ImmSetCompositionWindow(hIMC, &cf);
2894
2895 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2896}
2897
2898/*
374330e2 2899 * Draw a line of text in the window, at given character
2900 * coordinates, in given attributes.
2901 *
2902 * We are allowed to fiddle with the contents of `text'.
2903 */
32874aea 2904void do_text(Context ctx, int x, int y, char *text, int len,
2905 unsigned long attr, int lattr)
2906{
374330e2 2907 COLORREF fg, bg, t;
2908 int nfg, nbg, nfont;
2909 HDC hdc = ctx;
59ad2c03 2910 RECT line_box;
2911 int force_manual_underline = 0;
32874aea 2912 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2913 int char_width = fnt_width;
2914 int text_adjust = 0;
2915 static int *IpDx = 0, IpDxLEN = 0;
2916
2917 if (attr & ATTR_WIDE)
2918 char_width *= 2;
59ad2c03 2919
4eeb7d09 2920 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2921 int i;
32874aea 2922 if (len > IpDxLEN) {
59ad2c03 2923 sfree(IpDx);
3d88e64d 2924 IpDx = snewn(len + 16, int);
32874aea 2925 IpDxLEN = (len + 16);
59ad2c03 2926 }
32874aea 2927 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2928 IpDx[i] = char_width;
59ad2c03 2929 }
374330e2 2930
5a73255e 2931 /* Only want the left half of double width lines */
887035a5 2932 if (lattr != LATTR_NORM && x*2 >= term->cols)
5a73255e 2933 return;
2934
c9def1b8 2935 x *= fnt_width;
374330e2 2936 y *= font_height;
5a73255e 2937 x += offset_width;
2938 y += offset_height;
374330e2 2939
887035a5 2940 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
4eeb7d09 2941 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2942 attr ^= ATTR_CUR_XOR;
2943 }
2944
2945 nfont = 0;
4eeb7d09 2946 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2947 /* Assume a poorman font is borken in other ways too. */
2948 lattr = LATTR_WIDE;
2949 } else
2950 switch (lattr) {
2951 case LATTR_NORM:
2952 break;
2953 case LATTR_WIDE:
2954 nfont |= FONT_WIDE;
374330e2 2955 break;
4eeb7d09 2956 default:
2957 nfont |= FONT_WIDE + FONT_HIGH;
2958 break;
2959 }
5a73255e 2960 if (attr & ATTR_NARROW)
2961 nfont |= FONT_NARROW;
4eeb7d09 2962
2963 /* Special hack for the VT100 linedraw glyphs. */
2964 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2965 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2966 switch ((unsigned char) (text[0])) {
2967 case 0xBA:
2968 text_adjust = -2 * font_height / 5;
2969 break;
2970 case 0xBB:
2971 text_adjust = -1 * font_height / 5;
2972 break;
2973 case 0xBC:
2974 text_adjust = font_height / 5;
2975 break;
2976 case 0xBD:
2977 text_adjust = 2 * font_height / 5;
32874aea 2978 break;
59ad2c03 2979 }
4eeb7d09 2980 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2981 text_adjust *= 2;
2982 attr &= ~CSET_MASK;
21d2b241 2983 text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
2984 attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
4eeb7d09 2985 if (attr & ATTR_UNDER) {
2986 attr &= ~ATTR_UNDER;
2987 force_manual_underline = 1;
2988 }
374330e2 2989 }
2990 }
2991
4eeb7d09 2992 /* Anything left as an original character set is unprintable. */
2993 if (DIRECT_CHAR(attr)) {
2994 attr &= ~CSET_MASK;
2995 attr |= 0xFF00;
5a73255e 2996 memset(text, 0xFD, len);
4eeb7d09 2997 }
2998
2999 /* OEM CP */
3000 if ((attr & CSET_MASK) == ATTR_OEMCP)
3001 nfont |= FONT_OEM;
3002
37ca32ed 3003 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
3004 nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
3005 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
3006 nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
374330e2 3007 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
3008 nfont |= FONT_BOLD;
3009 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
3010 nfont |= FONT_UNDERLINE;
4eeb7d09 3011 another_font(nfont);
32874aea 3012 if (!fonts[nfont]) {
3013 if (nfont & FONT_UNDERLINE)
59ad2c03 3014 force_manual_underline = 1;
3015 /* Don't do the same for manual bold, it could be bad news. */
3016
32874aea 3017 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 3018 }
4eeb7d09 3019 another_font(nfont);
3020 if (!fonts[nfont])
3021 nfont = FONT_NORMAL;
374330e2 3022 if (attr & ATTR_REVERSE) {
32874aea 3023 t = nfg;
3024 nfg = nbg;
3025 nbg = t;
374330e2 3026 }
3027 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
37ca32ed 3028 nfg |= 1;
59ad2c03 3029 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
37ca32ed 3030 nbg |= 1;
374330e2 3031 fg = colours[nfg];
3032 bg = colours[nbg];
32874aea 3033 SelectObject(hdc, fonts[nfont]);
3034 SetTextColor(hdc, fg);
3035 SetBkColor(hdc, bg);
3036 SetBkMode(hdc, OPAQUE);
3037 line_box.left = x;
3038 line_box.top = y;
4eeb7d09 3039 line_box.right = x + char_width * len;
32874aea 3040 line_box.bottom = y + font_height;
4eeb7d09 3041
5a73255e 3042 /* Only want the left half of double width lines */
887035a5 3043 if (line_box.right > font_width*term->cols+offset_width)
3044 line_box.right = font_width*term->cols+offset_width;
5a73255e 3045
4eeb7d09 3046 /* We're using a private area for direct to font. (512 chars.) */
21d2b241 3047 if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
4eeb7d09 3048 /* Ho Hum, dbcs fonts are a PITA! */
3049 /* To display on W9x I have to convert to UCS */
3050 static wchar_t *uni_buf = 0;
3051 static int uni_len = 0;
5a73255e 3052 int nlen, mptr;
4eeb7d09 3053 if (len > uni_len) {
3054 sfree(uni_buf);
3d88e64d 3055 uni_len = len;
3056 uni_buf = snewn(uni_len, wchar_t);
4eeb7d09 3057 }
4eeb7d09 3058
5a73255e 3059 for(nlen = mptr = 0; mptr<len; mptr++) {
3060 uni_buf[nlen] = 0xFFFD;
21d2b241 3061 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
5a73255e 3062 IpDx[nlen] += char_width;
21d2b241 3063 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
5a73255e 3064 text+mptr, 2, uni_buf+nlen, 1);
3065 mptr++;
3066 }
3067 else
3068 {
21d2b241 3069 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
5a73255e 3070 text+mptr, 1, uni_buf+nlen, 1);
3071 }
3072 nlen++;
3073 }
4eeb7d09 3074 if (nlen <= 0)
3075 return; /* Eeek! */
3076
3077 ExtTextOutW(hdc, x,
3078 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 3079 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3080 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3081 SetBkMode(hdc, TRANSPARENT);
3082 ExtTextOutW(hdc, x - 1,
3083 y - font_height * (lattr ==
3084 LATTR_BOT) + text_adjust,
5a73255e 3085 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3086 }
5a73255e 3087
3088 IpDx[0] = -1;
4eeb7d09 3089 } else if (DIRECT_FONT(attr)) {
3090 ExtTextOut(hdc, x,
3091 y - font_height * (lattr == LATTR_BOT) + text_adjust,
3092 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
3093 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3094 SetBkMode(hdc, TRANSPARENT);
3095
3096 /* GRR: This draws the character outside it's box and can leave
3097 * 'droppings' even with the clip box! I suppose I could loop it
3098 * one character at a time ... yuk.
3099 *
3100 * Or ... I could do a test print with "W", and use +1 or -1 for this
3101 * shift depending on if the leftmost column is blank...
3102 */
3103 ExtTextOut(hdc, x - 1,
3104 y - font_height * (lattr ==
3105 LATTR_BOT) + text_adjust,
3106 ETO_CLIPPED, &line_box, text, len, IpDx);
3107 }
3108 } else {
3109 /* And 'normal' unicode characters */
3110 static WCHAR *wbuf = NULL;
3111 static int wlen = 0;
3112 int i;
3113 if (wlen < len) {
3114 sfree(wbuf);
3115 wlen = len;
3d88e64d 3116 wbuf = snewn(wlen, WCHAR);
4eeb7d09 3117 }
3118 for (i = 0; i < len; i++)
3119 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
3120
f0fccd51 3121 /* print Glyphs as they are, without Windows' Shaping*/
3122 exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
3123 &line_box, wbuf, len, IpDx);
3124/* ExtTextOutW(hdc, x,
4eeb7d09 3125 y - font_height * (lattr == LATTR_BOT) + text_adjust,
3126 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
f0fccd51 3127 */
4eeb7d09 3128
3129 /* And the shadow bold hack. */
5a73255e 3130 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 3131 SetBkMode(hdc, TRANSPARENT);
3132 ExtTextOutW(hdc, x - 1,
3133 y - font_height * (lattr ==
3134 LATTR_BOT) + text_adjust,
3135 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
3136 }
374330e2 3137 }
4eeb7d09 3138 if (lattr != LATTR_TOP && (force_manual_underline ||
3139 (und_mode == UND_LINE
3140 && (attr & ATTR_UNDER)))) {
32874aea 3141 HPEN oldpen;
4eeb7d09 3142 int dec = descent;
3143 if (lattr == LATTR_BOT)
3144 dec = dec * 2 - font_height;
3145
32874aea 3146 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 3147 MoveToEx(hdc, x, y + dec, NULL);
3148 LineTo(hdc, x + len * char_width, y + dec);
32874aea 3149 oldpen = SelectObject(hdc, oldpen);
3150 DeleteObject(oldpen);
374330e2 3151 }
4eeb7d09 3152}
3153
3154void do_cursor(Context ctx, int x, int y, char *text, int len,
3155 unsigned long attr, int lattr)
3156{
3157
3158 int fnt_width;
3159 int char_width;
3160 HDC hdc = ctx;
3161 int ctype = cfg.cursor_type;
3162
887035a5 3163 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
4eeb7d09 3164 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
3165 do_text(ctx, x, y, text, len, attr, lattr);
3166 return;
3167 }
3168 ctype = 2;
3169 attr |= TATTR_RIGHTCURS;
3170 }
3171
3172 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
3173 if (attr & ATTR_WIDE)
3174 char_width *= 2;
3175 x *= fnt_width;
3176 y *= font_height;
5a73255e 3177 x += offset_width;
3178 y += offset_height;
4eeb7d09 3179
887035a5 3180 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
374330e2 3181 POINT pts[5];
32874aea 3182 HPEN oldpen;
374330e2 3183 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 3184 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 3185 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 3186 pts[1].y = pts[2].y = y + font_height - 1;
3187 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3188 Polyline(hdc, pts, 5);
3189 oldpen = SelectObject(hdc, oldpen);
3190 DeleteObject(oldpen);
4eeb7d09 3191 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 3192 int startx, starty, dx, dy, length, i;
4eeb7d09 3193 if (ctype == 1) {
32874aea 3194 startx = x;
3195 starty = y + descent;
3196 dx = 1;
3197 dy = 0;
4eeb7d09 3198 length = char_width;
32874aea 3199 } else {
4e30ff69 3200 int xadjust = 0;
4eeb7d09 3201 if (attr & TATTR_RIGHTCURS)
3202 xadjust = char_width - 1;
32874aea 3203 startx = x + xadjust;
3204 starty = y;
3205 dx = 0;
3206 dy = 1;
3207 length = font_height;
3208 }
4eeb7d09 3209 if (attr & TATTR_ACTCURS) {
32874aea 3210 HPEN oldpen;
3211 oldpen =
3212 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3213 MoveToEx(hdc, startx, starty, NULL);
3214 LineTo(hdc, startx + dx * length, starty + dy * length);
3215 oldpen = SelectObject(hdc, oldpen);
3216 DeleteObject(oldpen);
3217 } else {
3218 for (i = 0; i < length; i++) {
3219 if (i % 2 == 0) {
3220 SetPixel(hdc, startx, starty, colours[23]);
3221 }
3222 startx += dx;
3223 starty += dy;
3224 }
3225 }
4e30ff69 3226 }
374330e2 3227}
3228
5a73255e 3229/* This function gets the actual width of a character in the normal font.
3230 */
2102eb8a 3231int char_width(Context ctx, int uc) {
5a73255e 3232 HDC hdc = ctx;
3233 int ibuf = 0;
3234
3235 /* If the font max is the same as the font ave width then this
3236 * function is a no-op.
3237 */
3238 if (!font_dualwidth) return 1;
3239
3240 switch (uc & CSET_MASK) {
3241 case ATTR_ASCII:
21d2b241 3242 uc = ucsdata.unitab_line[uc & 0xFF];
5a73255e 3243 break;
3244 case ATTR_LINEDRW:
21d2b241 3245 uc = ucsdata.unitab_xterm[uc & 0xFF];
5a73255e 3246 break;
3247 case ATTR_SCOACS:
21d2b241 3248 uc = ucsdata.unitab_scoacs[uc & 0xFF];
5a73255e 3249 break;
3250 }
3251 if (DIRECT_FONT(uc)) {
21d2b241 3252 if (ucsdata.dbcs_screenfont) return 1;
5a73255e 3253
3254 /* Speedup, I know of no font where ascii is the wrong width */
3255 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3256 return 1;
3257
3258 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3259 SelectObject(hdc, fonts[FONT_NORMAL]);
3260 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3261 another_font(FONT_OEM);
3262 if (!fonts[FONT_OEM]) return 0;
3263
3264 SelectObject(hdc, fonts[FONT_OEM]);
3265 } else
3266 return 0;
3267
3268 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3269 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3270 return 0;
3271 } else {
3272 /* Speedup, I know of no font where ascii is the wrong width */
3273 if (uc >= ' ' && uc <= '~') return 1;
3274
3275 SelectObject(hdc, fonts[FONT_NORMAL]);
3276 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3277 /* Okay that one worked */ ;
3278 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3279 /* This should work on 9x too, but it's "less accurate" */ ;
3280 else
3281 return 0;
3282 }
3283
3284 ibuf += font_width / 2 -1;
3285 ibuf /= font_width;
3286
3287 return ibuf;
3288}
3289
374330e2 3290/*
c9def1b8 3291 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3292 * codes. Returns number of bytes used or zero to drop the message
3293 * or -1 to forward the message to windows.
374330e2 3294 */
3cf144db 3295static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 3296 unsigned char *output)
3297{
374330e2 3298 BYTE keystate[256];
32874aea 3299 int scan, left_alt = 0, key_down, shift_state;
3300 int r, i, code;
3301 unsigned char *p = output;
4eeb7d09 3302 static int alt_sum = 0;
374330e2 3303
00e3ba0f 3304 HKL kbd_layout = GetKeyboardLayout(0);
3305
c264be08 3306 /* keys is for ToAsciiEx. There's some ick here, see below. */
3307 static WORD keys[3];
0c50ef57 3308 static int compose_char = 0;
3309 static WPARAM compose_key = 0;
32874aea 3310
c9def1b8 3311 r = GetKeyboardState(keystate);
32874aea 3312 if (!r)
3313 memset(keystate, 0, sizeof(keystate));
3314 else {
ec55b220 3315#if 0
4eeb7d09 3316#define SHOW_TOASCII_RESULT
32874aea 3317 { /* Tell us all about key events */
3318 static BYTE oldstate[256];
3319 static int first = 1;
3320 static int scan;
3321 int ch;
3322 if (first)
3323 memcpy(oldstate, keystate, sizeof(oldstate));
3324 first = 0;
3325
3326 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3327 debug(("+"));
3328 } else if ((HIWORD(lParam) & KF_UP)
3329 && scan == (HIWORD(lParam) & 0xFF)) {
3330 debug((". U"));
3331 } else {
3332 debug((".\n"));
3333 if (wParam >= VK_F1 && wParam <= VK_F20)
3334 debug(("K_F%d", wParam + 1 - VK_F1));
3335 else
3336 switch (wParam) {
3337 case VK_SHIFT:
3338 debug(("SHIFT"));
3339 break;
3340 case VK_CONTROL:
3341 debug(("CTRL"));
3342 break;
3343 case VK_MENU:
3344 debug(("ALT"));
3345 break;
3346 default:
3347 debug(("VK_%02x", wParam));
3348 }
3349 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3350 debug(("*"));
3351 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3352
3353 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3354 if (ch >= ' ' && ch <= '~')
3355 debug((", '%c'", ch));
3356 else if (ch)
3357 debug((", $%02x", ch));
3358
3359 if (keys[0])
3360 debug((", KB0=%02x", keys[0]));
3361 if (keys[1])
3362 debug((", KB1=%02x", keys[1]));
3363 if (keys[2])
3364 debug((", KB2=%02x", keys[2]));
3365
3366 if ((keystate[VK_SHIFT] & 0x80) != 0)
3367 debug((", S"));
3368 if ((keystate[VK_CONTROL] & 0x80) != 0)
3369 debug((", C"));
3370 if ((HIWORD(lParam) & KF_EXTENDED))
3371 debug((", E"));
3372 if ((HIWORD(lParam) & KF_UP))
3373 debug((", U"));
3374 }
3375
3376 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3377 else if ((HIWORD(lParam) & KF_UP))
3378 oldstate[wParam & 0xFF] ^= 0x80;
3379 else
3380 oldstate[wParam & 0xFF] ^= 0x81;
3381
3382 for (ch = 0; ch < 256; ch++)
3383 if (oldstate[ch] != keystate[ch])
3384 debug((", M%02x=%02x", ch, keystate[ch]));
3385
3386 memcpy(oldstate, keystate, sizeof(oldstate));
3387 }
ec55b220 3388#endif
3389
32874aea 3390 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 3391 keystate[VK_RMENU] = keystate[VK_MENU];
3392 }
3393
c9def1b8 3394
3395 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
5789bc94 3396 if ((cfg.funky_type == FUNKY_VT400 ||
3397 (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
887035a5 3398 !cfg.no_applic_k))
32874aea 3399 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 3400
3401 wParam = VK_EXECUTE;
3402
3403 /* UnToggle NUMLock */
32874aea 3404 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3405 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 3406 }
3407
3408 /* And write back the 'adjusted' state */
32874aea 3409 SetKeyboardState(keystate);
c9def1b8 3410 }
3411
3412 /* Disable Auto repeat if required */
887035a5 3413 if (term->repeat_off &&
3414 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
32874aea 3415 return 0;
c9def1b8 3416
32874aea 3417 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 3418 left_alt = 1;
3419
32874aea 3420 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 3421
95bbe1ae 3422 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 3423 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 3424 if (cfg.ctrlaltkeys)
3425 keystate[VK_MENU] = 0;
3426 else {
3427 keystate[VK_RMENU] = 0x80;
3428 left_alt = 0;
3429 }
3430 }
c9def1b8 3431
3432 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3433 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3434 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3435
95bbe1ae 3436 /* Note if AltGr was pressed and if it was used as a compose key */
3437 if (!compose_state) {
159eba53 3438 compose_key = 0x100;
95bbe1ae 3439 if (cfg.compose_key) {
32874aea 3440 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3441 compose_key = wParam;
3442 }
3443 if (wParam == VK_APPS)
3444 compose_key = wParam;
3445 }
3446
32874aea 3447 if (wParam == compose_key) {
3448 if (compose_state == 0
3449 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3450 1;
3451 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3452 compose_state = 2;
3453 else
3454 compose_state = 0;
32874aea 3455 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3456 compose_state = 0;
3457
32874aea 3458 if (compose_state > 1 && left_alt)
3459 compose_state = 0;
67c339f7 3460
c9def1b8 3461 /* Sanitize the number pad if not using a PC NumPad */
887035a5 3462 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
5789bc94 3463 && cfg.funky_type != FUNKY_XTERM)
3464 || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
32874aea 3465 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3466 int nParam = 0;
32874aea 3467 switch (wParam) {
3468 case VK_INSERT:
3469 nParam = VK_NUMPAD0;
3470 break;
3471 case VK_END:
3472 nParam = VK_NUMPAD1;
3473 break;
3474 case VK_DOWN:
3475 nParam = VK_NUMPAD2;
3476 break;
3477 case VK_NEXT:
3478 nParam = VK_NUMPAD3;
3479 break;
3480 case VK_LEFT:
3481 nParam = VK_NUMPAD4;
3482 break;
3483 case VK_CLEAR:
3484 nParam = VK_NUMPAD5;
3485 break;
3486 case VK_RIGHT:
3487 nParam = VK_NUMPAD6;
3488 break;
3489 case VK_HOME:
3490 nParam = VK_NUMPAD7;
3491 break;
3492 case VK_UP:
3493 nParam = VK_NUMPAD8;
3494 break;
3495 case VK_PRIOR:
3496 nParam = VK_NUMPAD9;
3497 break;
3498 case VK_DELETE:
3499 nParam = VK_DECIMAL;
3500 break;
c9def1b8 3501 }
32874aea 3502 if (nParam) {
3503 if (keystate[VK_NUMLOCK] & 1)
3504 shift_state |= 1;
c9def1b8 3505 wParam = nParam;
3506 }
25d39ef6 3507 }
3508 }
3509
c9def1b8 3510 /* If a key is pressed and AltGr is not active */
32874aea 3511 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3512 /* Okay, prepare for most alts then ... */
3513 if (left_alt)
3514 *p++ = '\033';
374330e2 3515
c9def1b8 3516 /* Lets see if it's a pattern we know all about ... */
3517 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3518 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3519 return 0;
c9def1b8 3520 }
153580da 3521 if (wParam == VK_PRIOR && shift_state == 2) {
3522 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3523 return 0;
3524 }
c9def1b8 3525 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3526 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3527 return 0;
3528 }
153580da 3529 if (wParam == VK_NEXT && shift_state == 2) {
3530 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3531 return 0;
3532 }
32874aea 3533 if (wParam == VK_INSERT && shift_state == 1) {
887035a5 3534 term_do_paste(term);
32874aea 3535 return 0;
3536 }
c9def1b8 3537 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3538 return -1;
c9def1b8 3539 }
3540 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3541 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3542 return -1;
c9def1b8 3543 }
2cae8529 3544 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3545 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3546 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3547 flip_full_screen();
8f57d753 3548 return -1;
3549 }
ec55b220 3550 /* Control-Numlock for app-keypad mode switch */
3551 if (wParam == VK_PAUSE && shift_state == 2) {
887035a5 3552 term->app_keypad_keys ^= 1;
ec55b220 3553 return 0;
3554 }
374330e2 3555
c9def1b8 3556 /* Nethack keypad */
3557 if (cfg.nethack_keypad && !left_alt) {
32874aea 3558 switch (wParam) {
3559 case VK_NUMPAD1:
3560 *p++ = shift_state ? 'B' : 'b';
3561 return p - output;
3562 case VK_NUMPAD2:
3563 *p++ = shift_state ? 'J' : 'j';
3564 return p - output;
3565 case VK_NUMPAD3:
3566 *p++ = shift_state ? 'N' : 'n';
3567 return p - output;
3568 case VK_NUMPAD4:
3569 *p++ = shift_state ? 'H' : 'h';
3570 return p - output;
3571 case VK_NUMPAD5:
3572 *p++ = shift_state ? '.' : '.';
3573 return p - output;
3574 case VK_NUMPAD6:
3575 *p++ = shift_state ? 'L' : 'l';
3576 return p - output;
3577 case VK_NUMPAD7:
3578 *p++ = shift_state ? 'Y' : 'y';
3579 return p - output;
3580 case VK_NUMPAD8:
3581 *p++ = shift_state ? 'K' : 'k';
3582 return p - output;
3583 case VK_NUMPAD9:
3584 *p++ = shift_state ? 'U' : 'u';
3585 return p - output;
3586 }
c9def1b8 3587 }
3588
3589 /* Application Keypad */
3590 if (!left_alt) {
32874aea 3591 int xkey = 0;
3592
5789bc94 3593 if (cfg.funky_type == FUNKY_VT400 ||
3594 (cfg.funky_type <= FUNKY_LINUX &&
887035a5 3595 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
32874aea 3596 case VK_EXECUTE:
3597 xkey = 'P';
3598 break;
3599 case VK_DIVIDE:
3600 xkey = 'Q';
3601 break;
3602 case VK_MULTIPLY:
3603 xkey = 'R';
3604 break;
3605 case VK_SUBTRACT:
3606 xkey = 'S';
3607 break;
3608 }
887035a5 3609 if (term->app_keypad_keys && !cfg.no_applic_k)
32874aea 3610 switch (wParam) {
3611 case VK_NUMPAD0:
3612 xkey = 'p';
3613 break;
3614 case VK_NUMPAD1:
3615 xkey = 'q';
3616 break;
3617 case VK_NUMPAD2:
3618 xkey = 'r';
3619 break;
3620 case VK_NUMPAD3:
3621 xkey = 's';
3622 break;
3623 case VK_NUMPAD4:
3624 xkey = 't';
3625 break;
3626 case VK_NUMPAD5:
3627 xkey = 'u';
3628 break;
3629 case VK_NUMPAD6:
3630 xkey = 'v';
3631 break;
3632 case VK_NUMPAD7:
3633 xkey = 'w';
3634 break;
3635 case VK_NUMPAD8:
3636 xkey = 'x';
3637 break;
3638 case VK_NUMPAD9:
3639 xkey = 'y';
3640 break;
3641
3642 case VK_DECIMAL:
3643 xkey = 'n';
3644 break;
3645 case VK_ADD:
5789bc94 3646 if (cfg.funky_type == FUNKY_XTERM) {
32874aea 3647 if (shift_state)
3648 xkey = 'l';
3649 else
3650 xkey = 'k';
3651 } else if (shift_state)
3652 xkey = 'm';
c9def1b8 3653 else
32874aea 3654 xkey = 'l';
3655 break;
3656
3657 case VK_DIVIDE:
5789bc94 3658 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3659 xkey = 'o';
3660 break;
3661 case VK_MULTIPLY:
5789bc94 3662 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3663 xkey = 'j';
3664 break;
3665 case VK_SUBTRACT:
5789bc94 3666 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3667 xkey = 'm';
3668 break;
3669
3670 case VK_RETURN:
3671 if (HIWORD(lParam) & KF_EXTENDED)
3672 xkey = 'M';
3673 break;
c9def1b8 3674 }
32874aea 3675 if (xkey) {
887035a5 3676 if (term->vt52_mode) {
32874aea 3677 if (xkey >= 'P' && xkey <= 'S')
3678 p += sprintf((char *) p, "\x1B%c", xkey);
3679 else
3680 p += sprintf((char *) p, "\x1B?%c", xkey);
3681 } else
3682 p += sprintf((char *) p, "\x1BO%c", xkey);
3683 return p - output;
c9def1b8 3684 }
3685 }
3686
32874aea 3687 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3688 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3689 *p++ = 0;
3690 return -2;
c9def1b8 3691 }
e8e8d6e2 3692 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3693 /* We do the opposite of what is configured */
3694 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3695 *p++ = 0;
3696 return -2;
3697 }
32874aea 3698 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3699 *p++ = 0x1B;
3700 *p++ = '[';
3701 *p++ = 'Z';
3702 return p - output;
c9def1b8 3703 }
32874aea 3704 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3705 *p++ = 0;
3706 return p - output;
c9def1b8 3707 }
32874aea 3708 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3709 *p++ = 160;
3710 return p - output;
c9def1b8 3711 }
32874aea 3712 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3713 *p++ = 3;
a5f3e637 3714 *p++ = 0;
3715 return -2;
c9def1b8 3716 }
32874aea 3717 if (wParam == VK_PAUSE) { /* Break/Pause */
3718 *p++ = 26;
3719 *p++ = 0;
3720 return -2;
95bbe1ae 3721 }
c9def1b8 3722 /* Control-2 to Control-8 are special */
32874aea 3723 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3724 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3725 return p - output;
3726 }
237f2b6e 3727 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
c9def1b8 3728 *p++ = 0x1F;
3729 return p - output;
3730 }
3731 if (shift_state == 2 && wParam == 0xDF) {
3732 *p++ = 0x1C;
3733 return p - output;
3734 }
9aa461e4 3735 if (shift_state == 3 && wParam == 0xDE) {
3736 *p++ = 0x1E; /* Ctrl-~ == Ctrl-^ in xterm at least */
3737 return p - output;
3738 }
887035a5 3739 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
32874aea 3740 *p++ = '\r';
3741 *p++ = '\n';
c9def1b8 3742 return p - output;
3743 }
374330e2 3744
c5e9c988 3745 /*
c9def1b8 3746 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3747 * for integer decimal nn.)
3748 *
3749 * We also deal with the weird ones here. Linux VCs replace F1
3750 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3751 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3752 * respectively.
c5e9c988 3753 */
c9def1b8 3754 code = 0;
3755 switch (wParam) {
32874aea 3756 case VK_F1:
3757 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3758 break;
3759 case VK_F2:
3760 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3761 break;
3762 case VK_F3:
3763 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3764 break;
3765 case VK_F4:
3766 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3767 break;
3768 case VK_F5:
3769 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3770 break;
3771 case VK_F6:
3772 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3773 break;
3774 case VK_F7:
3775 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3776 break;
3777 case VK_F8:
3778 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3779 break;
3780 case VK_F9:
3781 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3782 break;
3783 case VK_F10:
3784 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3785 break;
3786 case VK_F11:
3787 code = 23;
3788 break;
3789 case VK_F12:
3790 code = 24;
3791 break;
3792 case VK_F13:
3793 code = 25;
3794 break;
3795 case VK_F14:
3796 code = 26;
3797 break;
3798 case VK_F15:
3799 code = 28;
3800 break;
3801 case VK_F16:
3802 code = 29;
3803 break;
3804 case VK_F17:
3805 code = 31;
3806 break;
3807 case VK_F18:
3808 code = 32;
3809 break;
3810 case VK_F19:
3811 code = 33;
3812 break;
3813 case VK_F20:
3814 code = 34;
3815 break;
dfca2656 3816 }
3817 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3818 case VK_HOME:
3819 code = 1;
3820 break;
3821 case VK_INSERT:
3822 code = 2;
3823 break;
3824 case VK_DELETE:
3825 code = 3;
3826 break;
3827 case VK_END:
3828 code = 4;
3829 break;
3830 case VK_PRIOR:
3831 code = 5;
3832 break;
3833 case VK_NEXT:
3834 code = 6;
3835 break;
374330e2 3836 }
ec55b220 3837 /* Reorder edit keys to physical order */
5789bc94 3838 if (cfg.funky_type == FUNKY_VT400 && code <= 6)
32874aea 3839 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3840
887035a5 3841 if (term->vt52_mode && code > 0 && code <= 6) {
32874aea 3842 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3843 return p - output;
3844 }
3845
5789bc94 3846 if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */
9bc81a2c 3847 code >= 11 && code <= 34) {
e24b1972 3848 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3849 int index = 0;
3850 switch (wParam) {
3851 case VK_F1: index = 0; break;
3852 case VK_F2: index = 1; break;
3853 case VK_F3: index = 2; break;
3854 case VK_F4: index = 3; break;
3855 case VK_F5: index = 4; break;
3856 case VK_F6: index = 5; break;
3857 case VK_F7: index = 6; break;
3858 case VK_F8: index = 7; break;
3859 case VK_F9: index = 8; break;
3860 case VK_F10: index = 9; break;
3861 case VK_F11: index = 10; break;
3862 case VK_F12: index = 11; break;
3863 }
3864 if (keystate[VK_SHIFT] & 0x80) index += 12;
3865 if (keystate[VK_CONTROL] & 0x80) index += 24;
3866 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3867 return p - output;
3868 }
5789bc94 3869 if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */
9bc81a2c 3870 code >= 1 && code <= 6) {
3871 char codes[] = "HL.FIG";
3872 if (code == 3) {
3873 *p++ = '\x7F';
3874 } else {
3875 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3876 }
3877 return p - output;
3878 }
5789bc94 3879 if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
f37caa11 3880 int offt = 0;
32874aea 3881 if (code > 15)
3882 offt++;
3883 if (code > 21)
3884 offt++;
887035a5 3885 if (term->vt52_mode)
32874aea 3886 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3887 else
32874aea 3888 p +=
3889 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3890 return p - output;
3891 }
5789bc94 3892 if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
32874aea 3893 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3894 return p - output;
3895 }
5789bc94 3896 if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
887035a5 3897 if (term->vt52_mode)
32874aea 3898 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3899 else
32874aea 3900 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3901 return p - output;
3902 }
3903 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3904 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3905 return p - output;
3906 }
3907 if (code) {
32874aea 3908 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3909 return p - output;
374330e2 3910 }
45dabbc5 3911
c9def1b8 3912 /*
3913 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3914 * some reason seems to send VK_CLEAR to Windows...).
3915 */
3916 {
3917 char xkey = 0;
3918 switch (wParam) {
32874aea 3919 case VK_UP:
3920 xkey = 'A';
3921 break;
3922 case VK_DOWN:
3923 xkey = 'B';
3924 break;
3925 case VK_RIGHT:
3926 xkey = 'C';
3927 break;
3928 case VK_LEFT:
3929 xkey = 'D';
3930 break;
3931 case VK_CLEAR:
3932 xkey = 'G';
3933 break;
c9def1b8 3934 }
32874aea 3935 if (xkey) {
887035a5 3936 if (term->vt52_mode)
32874aea 3937 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3938 else {
887035a5 3939 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3a953121 3940#if 0
3941 /*
3942 * RDB: VT100 & VT102 manuals both state the
3943 * app cursor keys only work if the app keypad
3944 * is on.
3945 *
3946 * SGT: That may well be true, but xterm
3947 * disagrees and so does at least one
3948 * application, so I've #if'ed this out and the
3949 * behaviour is back to PuTTY's original: app
3950 * cursor and app keypad are independently
3951 * switchable modes. If anyone complains about
3952 * _this_ I'll have to put in a configurable
3953 * option.
e864f84f 3954 */
887035a5 3955 if (!term->app_keypad_keys)
e864f84f 3956 app_flg = 0;
3a953121 3957#endif
e864f84f 3958 /* Useful mapping of Ctrl-arrows */
3959 if (shift_state == 2)
3960 app_flg = !app_flg;
3961
3962 if (app_flg)
3963 p += sprintf((char *) p, "\x1BO%c", xkey);
3964 else
3965 p += sprintf((char *) p, "\x1B[%c", xkey);
3966 }
c9def1b8 3967 return p - output;
3968 }
3969 }
0c50ef57 3970
3971 /*
3972 * Finally, deal with Return ourselves. (Win95 seems to
3973 * foul it up when Alt is pressed, for some reason.)
3974 */
32874aea 3975 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3976 *p++ = 0x0D;
a5f3e637 3977 *p++ = 0;
3978 return -2;
0c50ef57 3979 }
4eeb7d09 3980
3981 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3982 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3983 else
3984 alt_sum = 0;
67c339f7 3985 }
374330e2 3986
c9def1b8 3987 /* Okay we've done everything interesting; let windows deal with
3988 * the boring stuff */
3989 {
a9c02454 3990 BOOL capsOn=0;
3991
3992 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3993 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3994 capsOn= !left_alt;
3995 keystate[VK_CAPITAL] = 0;
3996 }
3997
c264be08 3998 /* XXX how do we know what the max size of the keys array should
3999 * be is? There's indication on MS' website of an Inquire/InquireEx
4000 * functioning returning a KBINFO structure which tells us. */
4001 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
4002 /* XXX 'keys' parameter is declared in MSDN documentation as
4003 * 'LPWORD lpChar'.
4004 * The experience of a French user indicates that on
4005 * Win98, WORD[] should be passed in, but on Win2K, it should
4006 * be BYTE[]. German WinXP and my Win2K with "US International"
4007 * driver corroborate this.
4008 * Experimentally I've conditionalised the behaviour on the
4009 * Win9x/NT split, but I suspect it's worse than that.
4010 * See wishlist item `win-dead-keys' for more horrible detail
4011 * and speculations. */
4012 BYTE keybs[3];
4013 int i;
4014 r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
4015 for (i=0; i<3; i++) keys[i] = keybs[i];
4016 } else {
4017 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4018 }
4eeb7d09 4019#ifdef SHOW_TOASCII_RESULT
4020 if (r == 1 && !key_down) {
4021 if (alt_sum) {
21d2b241 4022 if (in_utf(term) || ucsdata.dbcs_screenfont)
4eeb7d09 4023 debug((", (U+%04x)", alt_sum));
4024 else
4025 debug((", LCH(%d)", alt_sum));
4026 } else {
4027 debug((", ACH(%d)", keys[0]));
4028 }
4029 } else if (r > 0) {
4030 int r1;
4031 debug((", ASC("));
4032 for (r1 = 0; r1 < r; r1++) {
4033 debug(("%s%d", r1 ? "," : "", keys[r1]));
4034 }
4035 debug((")"));
4036 }
4037#endif
32874aea 4038 if (r > 0) {
4eeb7d09 4039 WCHAR keybuf;
256cb87c 4040
4041 /*
4042 * Interrupt an ongoing paste. I'm not sure this is
4043 * sensible, but for the moment it's preferable to
4044 * having to faff about buffering things.
4045 */
887035a5 4046 term_nopaste(term);
256cb87c 4047
c9def1b8 4048 p = output;
32874aea 4049 for (i = 0; i < r; i++) {
4050 unsigned char ch = (unsigned char) keys[i];
14963b8f 4051
32874aea 4052 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4053 compose_char = ch;
32874aea 4054 compose_state++;
c9def1b8 4055 continue;
4056 }
32874aea 4057 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4058 int nc;
4059 compose_state = 0;
4060
32874aea 4061 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 4062 MessageBeep(MB_ICONHAND);
c9def1b8 4063 return 0;
4064 }
4eeb7d09 4065 keybuf = nc;
887035a5 4066 term_seen_key_event(term);
e3be8de5 4067 if (ldisc)
4068 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4069 continue;
c9def1b8 4070 }
374330e2 4071
c9def1b8 4072 compose_state = 0;
374330e2 4073
4eeb7d09 4074 if (!key_down) {
4075 if (alt_sum) {
21d2b241 4076 if (in_utf(term) || ucsdata.dbcs_screenfont) {
4eeb7d09 4077 keybuf = alt_sum;
887035a5 4078 term_seen_key_event(term);
e3be8de5 4079 if (ldisc)
4080 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4081 } else {
4082 ch = (char) alt_sum;
5471d09a 4083 /*
4084 * We need not bother about stdin
4085 * backlogs here, because in GUI PuTTY
4086 * we can't do anything about it
4087 * anyway; there's no means of asking
4088 * Windows to hold off on KEYDOWN
4089 * messages. We _have_ to buffer
4090 * everything we're sent.
4091 */
887035a5 4092 term_seen_key_event(term);
e3be8de5 4093 if (ldisc)
4094 ldisc_send(ldisc, &ch, 1, 1);
4eeb7d09 4095 }
4096 alt_sum = 0;
405a0c29 4097 } else {
887035a5 4098 term_seen_key_event(term);
e3be8de5 4099 if (ldisc)
4100 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
405a0c29 4101 }
4eeb7d09 4102 } else {
a9c02454 4103 if(capsOn && ch < 0x80) {
4104 WCHAR cbuf[2];
4105 cbuf[0] = 27;
4106 cbuf[1] = xlat_uskbd2cyrllic(ch);
887035a5 4107 term_seen_key_event(term);
e3be8de5 4108 if (ldisc)
4109 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4110 } else {
4111 char cbuf[2];
4112 cbuf[0] = '\033';
4113 cbuf[1] = ch;
887035a5 4114 term_seen_key_event(term);
e3be8de5 4115 if (ldisc)
4116 lpage_send(ldisc, kbd_codepage,
4117 cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4118 }
c9def1b8 4119 }
bca9517a 4120 show_mouseptr(0);
c9def1b8 4121 }
374330e2 4122
c9def1b8 4123 /* This is so the ALT-Numpad and dead keys work correctly. */
4124 keys[0] = 0;
4125
32874aea 4126 return p - output;
c9def1b8 4127 }
159eba53 4128 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 4129 if (!left_alt)
4130 keys[0] = 0;
4eeb7d09 4131 /* If we will be using alt_sum fix the 256s */
21d2b241 4132 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
4eeb7d09 4133 keys[0] = 10;
374330e2 4134 }
4135
dfca2656 4136 /*
4137 * ALT alone may or may not want to bring up the System menu.
4138 * If it's not meant to, we return 0 on presses or releases of
4139 * ALT, to show that we've swallowed the keystroke. Otherwise
4140 * we return -1, which means Windows will give the keystroke
4141 * its default handling (i.e. bring up the System menu).
4142 */
4143 if (wParam == VK_MENU && !cfg.alt_only)
4144 return 0;
374330e2 4145
c9def1b8 4146 return -1;
374330e2 4147}
4148
a8327734 4149void request_paste(void *frontend)
e6346999 4150{
4151 /*
4152 * In Windows, pasting is synchronous: we can read the
4153 * clipboard with no difficulty, so request_paste() can just go
4154 * ahead and paste.
4155 */
887035a5 4156 term_do_paste(term);
e6346999 4157}
4158
a8327734 4159void set_title(void *frontend, char *title)
32874aea 4160{
4161 sfree(window_name);
3d88e64d 4162 window_name = snewn(1 + strlen(title), char);
32874aea 4163 strcpy(window_name, title);
37508af4 4164 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 4165 SetWindowText(hwnd, title);
374330e2 4166}
4167
a8327734 4168void set_icon(void *frontend, char *title)
32874aea 4169{
4170 sfree(icon_name);
3d88e64d 4171 icon_name = snewn(1 + strlen(title), char);
32874aea 4172 strcpy(icon_name, title);
37508af4 4173 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 4174 SetWindowText(hwnd, title);
374330e2 4175}
4176
a8327734 4177void set_sbar(void *frontend, int total, int start, int page)
32874aea 4178{
374330e2 4179 SCROLLINFO si;
c9def1b8 4180
1ba99d2c 4181 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 4182 return;
c9def1b8 4183
374330e2 4184 si.cbSize = sizeof(si);
c9def1b8 4185 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 4186 si.nMin = 0;
4187 si.nMax = total - 1;
4188 si.nPage = page;
4189 si.nPos = start;
c1f5f956 4190 if (hwnd)
32874aea 4191 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 4192}
4193
a8327734 4194Context get_ctx(void *frontend)
32874aea 4195{
374330e2 4196 HDC hdc;
4197 if (hwnd) {
32874aea 4198 hdc = GetDC(hwnd);
374330e2 4199 if (hdc && pal)
32874aea 4200 SelectPalette(hdc, pal, FALSE);
374330e2 4201 return hdc;
4202 } else
4203 return NULL;
4204}
4205
32874aea 4206void free_ctx(Context ctx)
4207{
4208 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
4209 ReleaseDC(hwnd, ctx);
374330e2 4210}
4211
32874aea 4212static void real_palette_set(int n, int r, int g, int b)
4213{
374330e2 4214 if (pal) {
4215 logpal->palPalEntry[n].peRed = r;
4216 logpal->palPalEntry[n].peGreen = g;
4217 logpal->palPalEntry[n].peBlue = b;
4218 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
4219 colours[n] = PALETTERGB(r, g, b);
32874aea 4220 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 4221 } else
4222 colours[n] = RGB(r, g, b);
4223}
4224
a8327734 4225void palette_set(void *frontend, int n, int r, int g, int b)
32874aea 4226{
374330e2 4227 static const int first[21] = {
4228 0, 2, 4, 6, 8, 10, 12, 14,
4229 1, 3, 5, 7, 9, 11, 13, 15,
4230 16, 17, 18, 20, 22
4231 };
32874aea 4232 real_palette_set(first[n], r, g, b);
374330e2 4233 if (first[n] >= 18)
32874aea 4234 real_palette_set(first[n] + 1, r, g, b);
374330e2 4235 if (pal) {
a8327734 4236 HDC hdc = get_ctx(frontend);
32874aea 4237 UnrealizeObject(pal);
4238 RealizePalette(hdc);
4239 free_ctx(hdc);
374330e2 4240 }
4241}
4242
a8327734 4243void palette_reset(void *frontend)
32874aea 4244{
374330e2 4245 int i;
4246
4247 for (i = 0; i < NCOLOURS; i++) {
4248 if (pal) {
4249 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4250 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4251 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4252 logpal->palPalEntry[i].peFlags = 0;
4253 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4254 defpal[i].rgbtGreen,
4255 defpal[i].rgbtBlue);
4256 } else
4257 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 4258 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 4259 }
4260
4261 if (pal) {
4262 HDC hdc;
32874aea 4263 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
a8327734 4264 hdc = get_ctx(frontend);
32874aea 4265 RealizePalette(hdc);
4266 free_ctx(hdc);
374330e2 4267 }
4268}
4269
a8327734 4270void write_aclip(void *frontend, char *data, int len, int must_deselect)
32874aea 4271{
374330e2 4272 HGLOBAL clipdata;
4273 void *lock;
4274
32874aea 4275 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 4276 if (!clipdata)
4277 return;
32874aea 4278 lock = GlobalLock(clipdata);
374330e2 4279 if (!lock)
4280 return;
32874aea 4281 memcpy(lock, data, len);
4282 ((unsigned char *) lock)[len] = 0;
4283 GlobalUnlock(clipdata);
374330e2 4284
f0df44da 4285 if (!must_deselect)
32874aea 4286 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 4287
32874aea 4288 if (OpenClipboard(hwnd)) {
374330e2 4289 EmptyClipboard();
32874aea 4290 SetClipboardData(CF_TEXT, clipdata);
374330e2 4291 CloseClipboard();
4292 } else
32874aea 4293 GlobalFree(clipdata);
f0df44da 4294
4295 if (!must_deselect)
32874aea 4296 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 4297}
4298
4eeb7d09 4299/*
4300 * Note: unlike write_aclip() this will not append a nul.
4301 */
a8327734 4302void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4eeb7d09 4303{
a7419ea4 4304 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 4305 int len2;
a7419ea4 4306 void *lock, *lock2, *lock3;
4eeb7d09 4307
4308 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4309
4310 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4311 len * sizeof(wchar_t));
4312 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4313
0e5e7f46 4314 if (!clipdata || !clipdata2) {
4eeb7d09 4315 if (clipdata)
4316 GlobalFree(clipdata);
4317 if (clipdata2)
4318 GlobalFree(clipdata2);
4319 return;
4320 }
4321 if (!(lock = GlobalLock(clipdata)))
4322 return;
4323 if (!(lock2 = GlobalLock(clipdata2)))
4324 return;
4325
4326 memcpy(lock, data, len * sizeof(wchar_t));
4327 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4328
a7419ea4 4329 if (cfg.rtf_paste) {
4330 wchar_t unitab[256];
4331 char *rtf = NULL;
4332 unsigned char *tdata = (unsigned char *)lock2;
4333 wchar_t *udata = (wchar_t *)lock;
4334 int rtflen = 0, uindex = 0, tindex = 0;
4335 int rtfsize = 0;
4336 int multilen, blen, alen, totallen, i;
4337 char before[16], after[4];
4338
4339 get_unitab(CP_ACP, unitab, 0);
4340
9a30e26b 4341 rtfsize = 100 + strlen(cfg.font.name);
3d88e64d 4342 rtf = snewn(rtfsize, char);
a7419ea4 4343 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
9a30e26b 4344 GetACP(), cfg.font.name);
a7419ea4 4345 rtflen = strlen(rtf);
4346
4347 /*
4348 * We want to construct a piece of RTF that specifies the
4349 * same Unicode text. To do this we will read back in
4350 * parallel from the Unicode data in `udata' and the
4351 * non-Unicode data in `tdata'. For each character in
4352 * `tdata' which becomes the right thing in `udata' when
4353 * looked up in `unitab', we just copy straight over from
4354 * tdata. For each one that doesn't, we must WCToMB it
4355 * individually and produce a \u escape sequence.
4356 *
4357 * It would probably be more robust to just bite the bullet
4358 * and WCToMB each individual Unicode character one by one,
4359 * then MBToWC each one back to see if it was an accurate
4360 * translation; but that strikes me as a horrifying number
4361 * of Windows API calls so I want to see if this faster way
4362 * will work. If it screws up badly we can always revert to
4363 * the simple and slow way.
4364 */
4365 while (tindex < len2 && uindex < len &&
4366 tdata[tindex] && udata[uindex]) {
4367 if (tindex + 1 < len2 &&
4368 tdata[tindex] == '\r' &&
4369 tdata[tindex+1] == '\n') {
4370 tindex++;
4371 uindex++;
4372 }
4373 if (unitab[tdata[tindex]] == udata[uindex]) {
4374 multilen = 1;
4375 before[0] = '\0';
4376 after[0] = '\0';
4377 blen = alen = 0;
4378 } else {
4379 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4380 NULL, 0, NULL, NULL);
4381 if (multilen != 1) {
c3b032a6 4382 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4383 udata[uindex]);
a7419ea4 4384 alen = 1; strcpy(after, "}");
4385 } else {
4386 blen = sprintf(before, "\\u%d", udata[uindex]);
4387 alen = 0; after[0] = '\0';
4388 }
4389 }
4390 assert(tindex + multilen <= len2);
4391 totallen = blen + alen;
4392 for (i = 0; i < multilen; i++) {
4393 if (tdata[tindex+i] == '\\' ||
4394 tdata[tindex+i] == '{' ||
4395 tdata[tindex+i] == '}')
4396 totallen += 2;
4397 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4398 totallen += 6; /* \par\r\n */
4399 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4400 totallen += 4;
4401 else
4402 totallen++;
4403 }
4404
4405 if (rtfsize < rtflen + totallen + 3) {
4406 rtfsize = rtflen + totallen + 512;
3d88e64d 4407 rtf = sresize(rtf, rtfsize, char);
a7419ea4 4408 }
4409
4410 strcpy(rtf + rtflen, before); rtflen += blen;
4411 for (i = 0; i < multilen; i++) {
4412 if (tdata[tindex+i] == '\\' ||
4413 tdata[tindex+i] == '{' ||
4414 tdata[tindex+i] == '}') {
4415 rtf[rtflen++] = '\\';
4416 rtf[rtflen++] = tdata[tindex+i];
4417 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4418 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4419 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4420 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4421 } else {
4422 rtf[rtflen++] = tdata[tindex+i];
4423 }
4424 }
4425 strcpy(rtf + rtflen, after); rtflen += alen;
4426
4427 tindex += multilen;
4428 uindex++;
4429 }
4430
4431 strcpy(rtf + rtflen, "}");
4432 rtflen += 2;
4433
4434 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4435 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4436 strcpy(lock3, rtf);
4437 GlobalUnlock(clipdata3);
4438 }
4439 sfree(rtf);
4440 } else
4441 clipdata3 = NULL;
4442
4eeb7d09 4443 GlobalUnlock(clipdata);
4444 GlobalUnlock(clipdata2);
4445
4446 if (!must_deselect)
4447 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4448
4449 if (OpenClipboard(hwnd)) {
4450 EmptyClipboard();
4451 SetClipboardData(CF_UNICODETEXT, clipdata);
4452 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4453 if (clipdata3)
4454 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4455 CloseClipboard();
4456 } else {
4457 GlobalFree(clipdata);
4458 GlobalFree(clipdata2);
4459 }
4460
4461 if (!must_deselect)
4462 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4463}
4464
a8327734 4465void get_clip(void *frontend, wchar_t ** p, int *len)
32874aea 4466{
374330e2 4467 static HGLOBAL clipdata = NULL;
4eeb7d09 4468 static wchar_t *converted = 0;
4469 wchar_t *p2;
374330e2 4470
4eeb7d09 4471 if (converted) {
4472 sfree(converted);
4473 converted = 0;
4474 }
374330e2 4475 if (!p) {
4476 if (clipdata)
32874aea 4477 GlobalUnlock(clipdata);
374330e2 4478 clipdata = NULL;
4479 return;
4eeb7d09 4480 } else if (OpenClipboard(NULL)) {
2d466ffd 4481 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4482 CloseClipboard();
4eeb7d09 4483 *p = GlobalLock(clipdata);
4484 if (*p) {
4485 for (p2 = *p; *p2; p2++);
4486 *len = p2 - *p;
4487 return;
374330e2 4488 }
2d466ffd 4489 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4490 char *s;
4491 int i;
4492 CloseClipboard();
4493 s = GlobalLock(clipdata);
4494 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3d88e64d 4495 *p = converted = snewn(i, wchar_t);
4eeb7d09 4496 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4497 *len = i - 1;
4498 return;
4499 } else
4500 CloseClipboard();
374330e2 4501 }
4502
4503 *p = NULL;
4504 *len = 0;
4505}
4506
4eeb7d09 4507#if 0
374330e2 4508/*
4509 * Move `lines' lines from position `from' to position `to' in the
4510 * window.
4511 */
a8327734 4512void optimised_move(void *frontend, int to, int from, int lines)
32874aea 4513{
374330e2 4514 RECT r;
f67b4e85 4515 int min, max;
374330e2 4516
4517 min = (to < from ? to : from);
4518 max = to + from - min;
374330e2 4519
5a73255e 4520 r.left = offset_width;
887035a5 4521 r.right = offset_width + term->cols * font_width;
5a73255e 4522 r.top = offset_height + min * font_height;
4523 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4524 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4525}
4eeb7d09 4526#endif
374330e2 4527
4528/*
4529 * Print a message box and perform a fatal exit.
4530 */
32874aea 4531void fatalbox(char *fmt, ...)
4532{
374330e2 4533 va_list ap;
971bcc0a 4534 char *stuff, morestuff[100];
374330e2 4535
4536 va_start(ap, fmt);
971bcc0a 4537 stuff = dupvprintf(fmt, ap);
374330e2 4538 va_end(ap);
f6f450e2 4539 sprintf(morestuff, "%.70s Fatal Error", appname);
4540 MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
971bcc0a 4541 sfree(stuff);
93b581bd 4542 cleanup_exit(1);
374330e2 4543}
4544
4545/*
1709795f 4546 * Print a modal (Really Bad) message box and perform a fatal exit.
4547 */
4548void modalfatalbox(char *fmt, ...)
4549{
4550 va_list ap;
971bcc0a 4551 char *stuff, morestuff[100];
1709795f 4552
4553 va_start(ap, fmt);
971bcc0a 4554 stuff = dupvprintf(fmt, ap);
1709795f 4555 va_end(ap);
f6f450e2 4556 sprintf(morestuff, "%.70s Fatal Error", appname);
4557 MessageBox(hwnd, stuff, morestuff,
1709795f 4558 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
971bcc0a 4559 sfree(stuff);
1709795f 4560 cleanup_exit(1);
4561}
4562
4563/*
f8a28d1f 4564 * Manage window caption / taskbar flashing, if enabled.
4565 * 0 = stop, 1 = maintain, 2 = start
4566 */
4567static void flash_window(int mode)
4568{
4569 static long last_flash = 0;
4570 static int flashing = 0;
4571 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4572 /* stop */
4573 if (flashing) {
4574 FlashWindow(hwnd, FALSE);
4575 flashing = 0;
4576 }
4577
4578 } else if (mode == 2) {
4579 /* start */
4580 if (!flashing) {
4581 last_flash = GetTickCount();
4582 flashing = 1;
4583 FlashWindow(hwnd, TRUE);
4584 }
4585
4586 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4587 /* maintain */
4588 if (flashing) {
4589 long now = GetTickCount();
4590 long fdiff = now - last_flash;
4591 if (fdiff < 0 || fdiff > 450) {
4592 last_flash = now;
4593 FlashWindow(hwnd, TRUE); /* toggle */
4594 }
4595 }
4596 }
4597}
4598
4599/*
374330e2 4600 * Beep.
4601 */
a8327734 4602void beep(void *frontend, int mode)
32874aea 4603{
03169ad0 4604 if (mode == BELL_DEFAULT) {
eb04402e 4605 /*
4606 * For MessageBeep style bells, we want to be careful of
4607 * timing, because they don't have the nice property of
4608 * PlaySound bells that each one cancels the previous
4609 * active one. So we limit the rate to one per 50ms or so.
4610 */
4611 static long lastbeep = 0;
d51cdf1e 4612 long beepdiff;
eb04402e 4613
d51cdf1e 4614 beepdiff = GetTickCount() - lastbeep;
eb04402e 4615 if (beepdiff >= 0 && beepdiff < 50)
4616 return;
156686ef 4617 MessageBeep(MB_OK);
d51cdf1e 4618 /*
4619 * The above MessageBeep call takes time, so we record the
4620 * time _after_ it finishes rather than before it starts.
4621 */
4622 lastbeep = GetTickCount();
03169ad0 4623 } else if (mode == BELL_WAVEFILE) {
9a30e26b 4624 if (!PlaySound(cfg.bell_wavefile.path, NULL,
4625 SND_ASYNC | SND_FILENAME)) {
850323f9 4626 char buf[sizeof(cfg.bell_wavefile.path) + 80];
f6f450e2 4627 char otherbuf[100];
03169ad0 4628 sprintf(buf, "Unable to play sound file\n%s\n"
debe102c 4629 "Using default sound instead", cfg.bell_wavefile.path);
f6f450e2 4630 sprintf(otherbuf, "%.70s Sound Error", appname);
4631 MessageBox(hwnd, buf, otherbuf,
32874aea 4632 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4633 cfg.beep = BELL_DEFAULT;
4634 }
85f6b361 4635 } else if (mode == BELL_PCSPEAKER) {
4636 static long lastbeep = 0;
4637 long beepdiff;
4638
4639 beepdiff = GetTickCount() - lastbeep;
4640 if (beepdiff >= 0 && beepdiff < 50)
4641 return;
4642
4643 /*
4644 * We must beep in different ways depending on whether this
4645 * is a 95-series or NT-series OS.
4646 */
4647 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
4648 Beep(800, 100);
4649 else
4650 MessageBeep(-1);
4651 lastbeep = GetTickCount();
03169ad0 4652 }
f8a28d1f 4653 /* Otherwise, either visual bell or disabled; do nothing here */
887035a5 4654 if (!term->has_focus) {
f8a28d1f 4655 flash_window(2); /* start */
4656 }
374330e2 4657}
8f57d753 4658
4659/*
68f9b3d9 4660 * Minimise or restore the window in response to a server-side
4661 * request.
4662 */
a8327734 4663void set_iconic(void *frontend, int iconic)
68f9b3d9 4664{
4665 if (IsIconic(hwnd)) {
4666 if (!iconic)
4667 ShowWindow(hwnd, SW_RESTORE);
4668 } else {
4669 if (iconic)
4670 ShowWindow(hwnd, SW_MINIMIZE);
4671 }
4672}
4673
4674/*
4675 * Move the window in response to a server-side request.
4676 */
a8327734 4677void move_window(void *frontend, int x, int y)
68f9b3d9 4678{
d1e0a352 4679 if (cfg.resize_action == RESIZE_DISABLED ||
4680 cfg.resize_action == RESIZE_FONT ||
4681 IsZoomed(hwnd))
4682 return;
4683
68f9b3d9 4684 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4685}
4686
4687/*
4688 * Move the window to the top or bottom of the z-order in response
4689 * to a server-side request.
4690 */
a8327734 4691void set_zorder(void *frontend, int top)
68f9b3d9 4692{
1ba99d2c 4693 if (cfg.alwaysontop)
68f9b3d9 4694 return; /* ignore */
4695 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4696 SWP_NOMOVE | SWP_NOSIZE);
4697}
4698
4699/*
4700 * Refresh the window in response to a server-side request.
4701 */
a8327734 4702void refresh_window(void *frontend)
68f9b3d9 4703{
4704 InvalidateRect(hwnd, NULL, TRUE);
4705}
4706
4707/*
4708 * Maximise or restore the window in response to a server-side
4709 * request.
4710 */
a8327734 4711void set_zoomed(void *frontend, int zoomed)
68f9b3d9 4712{
1ba99d2c 4713 if (IsZoomed(hwnd)) {
4714 if (!zoomed)
4715 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4716 } else {
4717 if (zoomed)
4718 ShowWindow(hwnd, SW_MAXIMIZE);
4719 }
4720}
4721
4722/*
4723 * Report whether the window is iconic, for terminal reports.
4724 */
a8327734 4725int is_iconic(void *frontend)
68f9b3d9 4726{
4727 return IsIconic(hwnd);
4728}
4729
4730/*
4731 * Report the window's position, for terminal reports.
4732 */
a8327734 4733void get_window_pos(void *frontend, int *x, int *y)
68f9b3d9 4734{
4735 RECT r;
4736 GetWindowRect(hwnd, &r);
4737 *x = r.left;
4738 *y = r.top;
4739}
4740
4741/*
4742 * Report the window's pixel size, for terminal reports.
4743 */
a8327734 4744void get_window_pixels(void *frontend, int *x, int *y)
68f9b3d9 4745{
4746 RECT r;
4747 GetWindowRect(hwnd, &r);
4748 *x = r.right - r.left;
4749 *y = r.bottom - r.top;
4750}
4751
4752/*
4753 * Return the window or icon title.
4754 */
a8327734 4755char *get_window_title(void *frontend, int icon)
68f9b3d9 4756{
4757 return icon ? icon_name : window_name;
4758}
1ba99d2c 4759
4760/*
4761 * See if we're in full-screen mode.
4762 */
4e95095a 4763static int is_full_screen()
1ba99d2c 4764{
4765 if (!IsZoomed(hwnd))
4766 return FALSE;
4767 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4768 return FALSE;
4769 return TRUE;
4770}
4771
d0f1bcb4 4772/* Get the rect/size of a full screen window using the nearest available
4773 * monitor in multimon systems; default to something sensible if only
4774 * one monitor is present. */
4775static int get_fullscreen_rect(RECT * ss)
4776{
5d145a14 4777#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
d0f1bcb4 4778 HMONITOR mon;
4779 MONITORINFO mi;
4780 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4781 mi.cbSize = sizeof(mi);
4782 GetMonitorInfo(mon, &mi);
4783
4784 /* structure copy */
4785 *ss = mi.rcMonitor;
4786 return TRUE;
4787#else
4788/* could also use code like this:
4789 ss->left = ss->top = 0;
4790 ss->right = GetSystemMetrics(SM_CXSCREEN);
4791 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4792*/
e6f8202f 4793 return GetClientRect(GetDesktopWindow(), ss);
d0f1bcb4 4794#endif
4795}
4796
4797
1ba99d2c 4798/*
4799 * Go full-screen. This should only be called when we are already
4800 * maximised.
4801 */
4e95095a 4802static void make_full_screen()
1ba99d2c 4803{
4804 DWORD style;
d0f1bcb4 4805 RECT ss;
1ba99d2c 4806
4807 assert(IsZoomed(hwnd));
4808
d0f1bcb4 4809 if (is_full_screen())
4810 return;
4811
1ba99d2c 4812 /* Remove the window furniture. */
4813 style = GetWindowLong(hwnd, GWL_STYLE);
4814 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4815 if (cfg.scrollbar_in_fullscreen)
4816 style |= WS_VSCROLL;
4817 else
4818 style &= ~WS_VSCROLL;
4819 SetWindowLong(hwnd, GWL_STYLE, style);
4820
4821 /* Resize ourselves to exactly cover the nearest monitor. */
d0f1bcb4 4822 get_fullscreen_rect(&ss);
4823 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4824 ss.right - ss.left,
4825 ss.bottom - ss.top,
4826 SWP_FRAMECHANGED);
1ba99d2c 4827
4828 /* Tick the menu item in the System menu. */
4829 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4830 MF_CHECKED);
4831}
4832
4833/*
4834 * Clear the full-screen attributes.
4835 */
4e95095a 4836static void clear_full_screen()
1ba99d2c 4837{
4838 DWORD oldstyle, style;
4839
4840 /* Reinstate the window furniture. */
4841 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4842 style |= WS_CAPTION | WS_BORDER;
4843 if (cfg.resize_action == RESIZE_DISABLED)
4844 style &= ~WS_THICKFRAME;
4845 else
4846 style |= WS_THICKFRAME;
1ba99d2c 4847 if (cfg.scrollbar)
4848 style |= WS_VSCROLL;
4849 else
4850 style &= ~WS_VSCROLL;
4851 if (style != oldstyle) {
4852 SetWindowLong(hwnd, GWL_STYLE, style);
4853 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4854 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4855 SWP_FRAMECHANGED);
4856 }
4857
4858 /* Untick the menu item in the System menu. */
4859 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4860 MF_UNCHECKED);
4861}
4862
4863/*
4864 * Toggle full-screen mode.
4865 */
4e95095a 4866static void flip_full_screen()
1ba99d2c 4867{
4868 if (is_full_screen()) {
4869 ShowWindow(hwnd, SW_RESTORE);
4870 } else if (IsZoomed(hwnd)) {
4871 make_full_screen();
4872 } else {
4873 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4874 ShowWindow(hwnd, SW_MAXIMIZE);
4875 }
4876}
ab2c86b9 4877
b9d7bcad 4878void frontend_keypress(void *handle)
ab2c86b9 4879{
4880 /*
4881 * Keypress termination in non-Close-On-Exit mode is not
4882 * currently supported in PuTTY proper, because the window
4883 * always has a perfectly good Close button anyway. So we do
4884 * nothing here.
4885 */
4886 return;
4887}
fbf6cb3b 4888
4889int from_backend(void *frontend, int is_stderr, const char *data, int len)
4890{
4891 return term_data(term, is_stderr, data, len);
4892}
c44bf5bd 4893
4894void agent_schedule_callback(void (*callback)(void *, void *, int),
4895 void *callback_ctx, void *data, int len)
4896{
4897 struct agent_callback *c = snew(struct agent_callback);
4898 c->callback = callback;
4899 c->callback_ctx = callback_ctx;
4900 c->data = data;
4901 c->len = len;
4902 PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
4903}