Markus Kuhn's UTF-8 page
[u/mdw/putty] / window.c
CommitLineData
374330e2 1#include <stdio.h>
2#include <stdlib.h>
1d470ad2 3#include <ctype.h>
ec55b220 4#include <time.h>
a7419ea4 5#include <assert.h>
374330e2 6
32874aea 7#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 8#include "putty.h"
887035a5 9#include "terminal.h"
d5859615 10#include "storage.h"
374330e2 11#include "win_res.h"
12
7440fd44 13#ifndef NO_MULTIMON
14#if WINVER < 0x0500
15#define COMPILE_MULTIMON_STUBS
16#include <multimon.h>
17#endif
18#endif
19
20#include <imm.h>
21#include <commctrl.h>
22#include <richedit.h>
23#include <mmsystem.h>
24
6833a413 25#define IDM_SHOWLOG 0x0010
26#define IDM_NEWSESS 0x0020
27#define IDM_DUPSESS 0x0030
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,
c6958dfe 1139 CONST INT *lpDx, int opaque)
f0fccd51 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,
c6958dfe 1155 FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC);
f0fccd51 1156
c6958dfe 1157 ExtTextOut(hdc, x, y,
1158 ETO_GLYPH_INDEX | ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0),
1159 lprc, buffer, cbCount, lpDx);
f0fccd51 1160}
1161
1162/*
4eeb7d09 1163 * Initialise all the fonts we will need initially. There may be as many as
1164 * three or as few as one. The other (poentially) twentyone fonts are done
1165 * if/when they are needed.
1166 *
1167 * We also:
374330e2 1168 *
1169 * - check the font width and height, correcting our guesses if
1170 * necessary.
1171 *
1172 * - verify that the bold font is the same width as the ordinary
1173 * one, and engage shadow bolding if not.
1174 *
1175 * - verify that the underlined font is the same width as the
1176 * ordinary one (manual underlining by means of line drawing can
1177 * be done in a pinch).
374330e2 1178 */
5a73255e 1179static void init_fonts(int pick_width, int pick_height)
32874aea 1180{
374330e2 1181 TEXTMETRIC tm;
4eeb7d09 1182 CPINFO cpinfo;
1183 int fontsize[3];
97fc891e 1184 int i;
374330e2 1185 HDC hdc;
1186 int fw_dontcare, fw_bold;
1187
4eeb7d09 1188 for (i = 0; i < FONT_MAXNO; i++)
374330e2 1189 fonts[i] = NULL;
1190
5a73255e 1191 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1192 und_mode = UND_FONT;
1193
9a30e26b 1194 if (cfg.font.isbold) {
374330e2 1195 fw_dontcare = FW_BOLD;
5b80d07f 1196 fw_bold = FW_HEAVY;
32874aea 1197 } else {
374330e2 1198 fw_dontcare = FW_DONTCARE;
1199 fw_bold = FW_BOLD;
1200 }
1201
97fc891e 1202 hdc = GetDC(hwnd);
1203
5a73255e 1204 if (pick_height)
1205 font_height = pick_height;
1206 else {
9a30e26b 1207 font_height = cfg.font.height;
5a73255e 1208 if (font_height > 0) {
1209 font_height =
1210 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1211 }
3b2c664e 1212 }
59ad2c03 1213 font_width = pick_width;
97fc891e 1214
374330e2 1215#define f(i,c,w,u) \
97fc891e 1216 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 1217 c, OUT_DEFAULT_PRECIS, \
1218 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
9a30e26b 1219 FIXED_PITCH | FF_DONTCARE, cfg.font.name)
97fc891e 1220
9a30e26b 1221 f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
97fc891e 1222
71f6951d 1223 lfont.lfHeight = font_height;
1224 lfont.lfWidth = font_width;
1225 lfont.lfEscapement = 0;
1226 lfont.lfOrientation = 0;
1227 lfont.lfWeight = fw_dontcare;
1228 lfont.lfItalic = FALSE;
1229 lfont.lfUnderline = FALSE;
1230 lfont.lfStrikeOut = FALSE;
9a30e26b 1231 lfont.lfCharSet = cfg.font.charset;
71f6951d 1232 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1233 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1234 lfont.lfQuality = DEFAULT_QUALITY;
1235 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
9a30e26b 1236 strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
71f6951d 1237
4eeb7d09 1238 SelectObject(hdc, fonts[FONT_NORMAL]);
1239 GetTextMetrics(hdc, &tm);
5a73255e 1240
1241 if (pick_width == 0 || pick_height == 0) {
1242 font_height = tm.tmHeight;
1243 font_width = tm.tmAveCharWidth;
1244 }
1245 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1246
1247#ifdef RDB_DEBUG_PATCH
1248 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1249 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1250#endif
97fc891e 1251
4eeb7d09 1252 {
1253 CHARSETINFO info;
1254 DWORD cset = tm.tmCharSet;
1255 memset(&info, 0xFF, sizeof(info));
97fc891e 1256
4eeb7d09 1257 /* !!! Yes the next line is right */
1258 if (cset == OEM_CHARSET)
21d2b241 1259 ucsdata.font_codepage = GetOEMCP();
4eeb7d09 1260 else
21d2b241 1261 if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
1262 ucsdata.font_codepage = info.ciACP;
4eeb7d09 1263 else
21d2b241 1264 ucsdata.font_codepage = -1;
32874aea 1265
21d2b241 1266 GetCPInfo(ucsdata.font_codepage, &cpinfo);
1267 ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
4eeb7d09 1268 }
97fc891e 1269
9a30e26b 1270 f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
97fc891e 1271
4eeb7d09 1272 /*
1273 * Some fonts, e.g. 9-pt Courier, draw their underlines
1274 * outside their character cell. We successfully prevent
1275 * screen corruption by clipping the text output, but then
1276 * we lose the underline completely. Here we try to work
1277 * out whether this is such a font, and if it is, we set a
1278 * flag that causes underlines to be drawn by hand.
1279 *
1280 * Having tried other more sophisticated approaches (such
1281 * as examining the TEXTMETRIC structure or requesting the
1282 * height of a string), I think we'll do this the brute
1283 * force way: we create a small bitmap, draw an underlined
1284 * space on it, and test to see whether any pixels are
1285 * foreground-coloured. (Since we expect the underline to
1286 * go all the way across the character cell, we only search
1287 * down a single column of the bitmap, half way across.)
1288 */
1289 {
1290 HDC und_dc;
1291 HBITMAP und_bm, und_oldbm;
1292 int i, gotit;
1293 COLORREF c;
1294
1295 und_dc = CreateCompatibleDC(hdc);
1296 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1297 und_oldbm = SelectObject(und_dc, und_bm);
1298 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1299 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1300 SetTextColor(und_dc, RGB(255, 255, 255));
1301 SetBkColor(und_dc, RGB(0, 0, 0));
1302 SetBkMode(und_dc, OPAQUE);
1303 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1304 gotit = FALSE;
1305 for (i = 0; i < font_height; i++) {
1306 c = GetPixel(und_dc, font_width / 2, i);
1307 if (c != RGB(0, 0, 0))
1308 gotit = TRUE;
1309 }
1310 SelectObject(und_dc, und_oldbm);
1311 DeleteObject(und_bm);
1312 DeleteDC(und_dc);
1313 if (!gotit) {
1314 und_mode = UND_LINE;
1315 DeleteObject(fonts[FONT_UNDERLINE]);
1316 fonts[FONT_UNDERLINE] = 0;
32874aea 1317 }
4eeb7d09 1318 }
97fc891e 1319
4eeb7d09 1320 if (bold_mode == BOLD_FONT) {
9a30e26b 1321 f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
374330e2 1322 }
1323#undef f
1324
97fc891e 1325 descent = tm.tmAscent + 1;
1326 if (descent >= font_height)
1327 descent = font_height - 1;
374330e2 1328
4eeb7d09 1329 for (i = 0; i < 3; i++) {
97fc891e 1330 if (fonts[i]) {
32874aea 1331 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
4eeb7d09 1332 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
32874aea 1333 else
4eeb7d09 1334 fontsize[i] = -i;
32874aea 1335 } else
4eeb7d09 1336 fontsize[i] = -i;
374330e2 1337 }
1338
32874aea 1339 ReleaseDC(hwnd, hdc);
374330e2 1340
4eeb7d09 1341 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
374330e2 1342 und_mode = UND_LINE;
32874aea 1343 DeleteObject(fonts[FONT_UNDERLINE]);
4eeb7d09 1344 fonts[FONT_UNDERLINE] = 0;
374330e2 1345 }
1346
4eeb7d09 1347 if (bold_mode == BOLD_FONT &&
1348 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
374330e2 1349 bold_mode = BOLD_SHADOW;
32874aea 1350 DeleteObject(fonts[FONT_BOLD]);
4eeb7d09 1351 fonts[FONT_BOLD] = 0;
374330e2 1352 }
4eeb7d09 1353 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1354
21d2b241 1355 init_ucs(&cfg, &ucsdata);
4eeb7d09 1356}
1357
1358static void another_font(int fontno)
1359{
1360 int basefont;
1361 int fw_dontcare, fw_bold;
1362 int c, u, w, x;
1363 char *s;
1364
1365 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1366 return;
1367
1368 basefont = (fontno & ~(FONT_BOLDUND));
1369 if (basefont != fontno && !fontflag[basefont])
1370 another_font(basefont);
97fc891e 1371
9a30e26b 1372 if (cfg.font.isbold) {
4eeb7d09 1373 fw_dontcare = FW_BOLD;
1374 fw_bold = FW_HEAVY;
1375 } else {
1376 fw_dontcare = FW_DONTCARE;
1377 fw_bold = FW_BOLD;
1378 }
1379
9a30e26b 1380 c = cfg.font.charset;
4eeb7d09 1381 w = fw_dontcare;
1382 u = FALSE;
9a30e26b 1383 s = cfg.font.name;
4eeb7d09 1384 x = font_width;
1385
1386 if (fontno & FONT_WIDE)
1387 x *= 2;
1388 if (fontno & FONT_NARROW)
5a73255e 1389 x = (x+1)/2;
4eeb7d09 1390 if (fontno & FONT_OEM)
1391 c = OEM_CHARSET;
1392 if (fontno & FONT_BOLD)
1393 w = fw_bold;
1394 if (fontno & FONT_UNDERLINE)
1395 u = TRUE;
1396
1397 fonts[fontno] =
1398 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1399 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1400 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1401 FIXED_PITCH | FF_DONTCARE, s);
1402
1403 fontflag[fontno] = 1;
1404}
1405
1406static void deinit_fonts(void)
1407{
1408 int i;
1409 for (i = 0; i < FONT_MAXNO; i++) {
1410 if (fonts[i])
1411 DeleteObject(fonts[i]);
1412 fonts[i] = 0;
1413 fontflag[i] = 0;
374330e2 1414 }
1415}
1416
a8327734 1417void request_resize(void *frontend, int w, int h)
32874aea 1418{
59ad2c03 1419 int width, height;
c9def1b8 1420
1421 /* If the window is maximized supress resizing attempts */
5a73255e 1422 if (IsZoomed(hwnd)) {
0ed2f48e 1423 if (cfg.resize_action == RESIZE_TERM)
5a73255e 1424 return;
1425 }
32874aea 1426
ad5c93cc 1427 if (cfg.resize_action == RESIZE_DISABLED) return;
887035a5 1428 if (h == term->rows && w == term->cols) return;
5a73255e 1429
1430 /* Sanity checks ... */
1431 {
32874aea 1432 static int first_time = 1;
1433 static RECT ss;
1434
1435 switch (first_time) {
1436 case 1:
1437 /* Get the size of the screen */
d0f1bcb4 1438 if (get_fullscreen_rect(&ss))
32874aea 1439 /* first_time = 0 */ ;
1440 else {
1441 first_time = 2;
1442 break;
1443 }
1444 case 0:
1445 /* Make sure the values are sane */
5a73255e 1446 width = (ss.right - ss.left - extra_width) / 4;
1447 height = (ss.bottom - ss.top - extra_height) / 6;
32874aea 1448
5a73255e 1449 if (w > width || h > height)
1450 return;
32874aea 1451 if (w < 15)
1452 w = 15;
1453 if (h < 1)
5a73255e 1454 h = 1;
32874aea 1455 }
c9def1b8 1456 }
59ad2c03 1457
887035a5 1458 term_size(term, h, w, cfg.savelines);
5a73255e 1459
0ed2f48e 1460 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
5a73255e 1461 width = extra_width + font_width * w;
1462 height = extra_height + font_height * h;
374330e2 1463
5a73255e 1464 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1465 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1466 SWP_NOMOVE | SWP_NOZORDER);
1467 } else
1468 reset_window(0);
1469
1470 InvalidateRect(hwnd, NULL, TRUE);
1471}
1472
1473static void reset_window(int reinit) {
1474 /*
1475 * This function decides how to resize or redraw when the
1476 * user changes something.
1477 *
1478 * This function doesn't like to change the terminal size but if the
1479 * font size is locked that may be it's only soluion.
1480 */
1481 int win_width, win_height;
1482 RECT cr, wr;
1483
1484#ifdef RDB_DEBUG_PATCH
1485 debug((27, "reset_window()"));
1486#endif
1487
1488 /* Current window sizes ... */
1489 GetWindowRect(hwnd, &wr);
1490 GetClientRect(hwnd, &cr);
1491
1492 win_width = cr.right - cr.left;
1493 win_height = cr.bottom - cr.top;
1494
0ed2f48e 1495 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1496
5a73255e 1497 /* Are we being forced to reload the fonts ? */
1498 if (reinit>1) {
1499#ifdef RDB_DEBUG_PATCH
1500 debug((27, "reset_window() -- Forced deinit"));
1501#endif
1502 deinit_fonts();
1503 init_fonts(0,0);
1504 }
1505
1506 /* Oh, looks like we're minimised */
1507 if (win_width == 0 || win_height == 0)
1508 return;
1509
1510 /* Is the window out of position ? */
1511 if ( !reinit &&
887035a5 1512 (offset_width != (win_width-font_width*term->cols)/2 ||
1513 offset_height != (win_height-font_height*term->rows)/2) ){
1514 offset_width = (win_width-font_width*term->cols)/2;
1515 offset_height = (win_height-font_height*term->rows)/2;
5a73255e 1516 InvalidateRect(hwnd, NULL, TRUE);
1517#ifdef RDB_DEBUG_PATCH
1518 debug((27, "reset_window() -> Reposition terminal"));
1519#endif
1520 }
1521
6003660f 1522 if (IsZoomed(hwnd)) {
5a73255e 1523 /* We're fullscreen, this means we must not change the size of
1524 * the window so it's the font size or the terminal itself.
1525 */
1526
1527 extra_width = wr.right - wr.left - cr.right + cr.left;
1528 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1529
0ed2f48e 1530 if (cfg.resize_action != RESIZE_TERM) {
887035a5 1531 if ( font_width != win_width/term->cols ||
1532 font_height != win_height/term->rows) {
5a73255e 1533 deinit_fonts();
887035a5 1534 init_fonts(win_width/term->cols, win_height/term->rows);
1535 offset_width = (win_width-font_width*term->cols)/2;
1536 offset_height = (win_height-font_height*term->rows)/2;
5a73255e 1537 InvalidateRect(hwnd, NULL, TRUE);
1538#ifdef RDB_DEBUG_PATCH
1539 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1540 font_width, font_height));
1541#endif
1542 }
1543 } else {
887035a5 1544 if ( font_width != win_width/term->cols ||
1545 font_height != win_height/term->rows) {
5a73255e 1546 /* Our only choice at this point is to change the
1547 * size of the terminal; Oh well.
1548 */
887035a5 1549 term_size(term, win_height/font_height, win_width/font_width,
1550 cfg.savelines);
1551 offset_width = (win_width-font_width*term->cols)/2;
1552 offset_height = (win_height-font_height*term->rows)/2;
5a73255e 1553 InvalidateRect(hwnd, NULL, TRUE);
1554#ifdef RDB_DEBUG_PATCH
1555 debug((27, "reset_window() -> Zoomed term_size"));
1556#endif
1557 }
1558 }
1559 return;
1560 }
1561
1562 /* Hmm, a force re-init means we should ignore the current window
1563 * so we resize to the default font size.
1564 */
1565 if (reinit>0) {
1566#ifdef RDB_DEBUG_PATCH
1567 debug((27, "reset_window() -> Forced re-init"));
1568#endif
1569
1570 offset_width = offset_height = cfg.window_border;
1571 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1572 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1573
887035a5 1574 if (win_width != font_width*term->cols + offset_width*2 ||
1575 win_height != font_height*term->rows + offset_height*2) {
5a73255e 1576
1577 /* If this is too large windows will resize it to the maximum
1578 * allowed window size, we will then be back in here and resize
1579 * the font or terminal to fit.
1580 */
1581 SetWindowPos(hwnd, NULL, 0, 0,
887035a5 1582 font_width*term->cols + extra_width,
1583 font_height*term->rows + extra_height,
5a73255e 1584 SWP_NOMOVE | SWP_NOZORDER);
1585 }
0ed2f48e 1586
1587 InvalidateRect(hwnd, NULL, TRUE);
5a73255e 1588 return;
1589 }
1590
1591 /* Okay the user doesn't want us to change the font so we try the
1592 * window. But that may be too big for the screen which forces us
1593 * to change the terminal.
1594 */
0ed2f48e 1595 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1596 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1597 reinit>0) {
5a73255e 1598 offset_width = offset_height = cfg.window_border;
1599 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1600 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1601
887035a5 1602 if (win_width != font_width*term->cols + offset_width*2 ||
1603 win_height != font_height*term->rows + offset_height*2) {
5a73255e 1604
1605 static RECT ss;
1606 int width, height;
d0f1bcb4 1607
1608 get_fullscreen_rect(&ss);
5a73255e 1609
5a73255e 1610 width = (ss.right - ss.left - extra_width) / font_width;
1611 height = (ss.bottom - ss.top - extra_height) / font_height;
1612
1613 /* Grrr too big */
887035a5 1614 if ( term->rows > height || term->cols > width ) {
0ed2f48e 1615 if (cfg.resize_action == RESIZE_EITHER) {
1616 /* Make the font the biggest we can */
887035a5 1617 if (term->cols > width)
1618 font_width = (ss.right - ss.left - extra_width)
1619 / term->cols;
1620 if (term->rows > height)
1621 font_height = (ss.bottom - ss.top - extra_height)
1622 / term->rows;
0ed2f48e 1623
1624 deinit_fonts();
1625 init_fonts(font_width, font_height);
1626
1627 width = (ss.right - ss.left - extra_width) / font_width;
1628 height = (ss.bottom - ss.top - extra_height) / font_height;
1629 } else {
887035a5 1630 if ( height > term->rows ) height = term->rows;
1631 if ( width > term->cols ) width = term->cols;
1632 term_size(term, height, width, cfg.savelines);
5a73255e 1633#ifdef RDB_DEBUG_PATCH
0ed2f48e 1634 debug((27, "reset_window() -> term resize to (%d,%d)",
1635 height, width));
5a73255e 1636#endif
0ed2f48e 1637 }
5a73255e 1638 }
1639
1640 SetWindowPos(hwnd, NULL, 0, 0,
887035a5 1641 font_width*term->cols + extra_width,
1642 font_height*term->rows + extra_height,
5a73255e 1643 SWP_NOMOVE | SWP_NOZORDER);
1644
1645 InvalidateRect(hwnd, NULL, TRUE);
1646#ifdef RDB_DEBUG_PATCH
1647 debug((27, "reset_window() -> window resize to (%d,%d)",
887035a5 1648 font_width*term->cols + extra_width,
1649 font_height*term->rows + extra_height));
5a73255e 1650#endif
1651 }
1652 return;
1653 }
1654
1655 /* We're allowed to or must change the font but do we want to ? */
1656
887035a5 1657 if (font_width != (win_width-cfg.window_border*2)/term->cols ||
1658 font_height != (win_height-cfg.window_border*2)/term->rows) {
5a73255e 1659
1660 deinit_fonts();
887035a5 1661 init_fonts((win_width-cfg.window_border*2)/term->cols,
1662 (win_height-cfg.window_border*2)/term->rows);
1663 offset_width = (win_width-font_width*term->cols)/2;
1664 offset_height = (win_height-font_height*term->rows)/2;
5a73255e 1665
1666 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1667 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1668
1669 InvalidateRect(hwnd, NULL, TRUE);
1670#ifdef RDB_DEBUG_PATCH
1671 debug((25, "reset_window() -> font resize to (%d,%d)",
1672 font_width, font_height));
1673#endif
1674 }
374330e2 1675}
1676
a5ce2e9f 1677static void set_input_locale(HKL kl)
1678{
1679 char lbuf[20];
1680
1681 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1682 lbuf, sizeof(lbuf));
1683
1684 kbd_codepage = atoi(lbuf);
1685}
1686
6908fed7 1687static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
32874aea 1688{
fdedf2c8 1689 int thistime = GetMessageTime();
1690
b90840c3 1691 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1692 lastbtn = MBT_NOTHING;
fc5b0934 1693 term_mouse(term, b, translate_button(b), MA_CLICK,
1694 x, y, shift, ctrl, alt);
01c034ad 1695 return;
1696 }
1697
fdedf2c8 1698 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 1699 lastact = (lastact == MA_CLICK ? MA_2CLK :
1700 lastact == MA_2CLK ? MA_3CLK :
1701 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1702 } else {
1703 lastbtn = b;
1704 lastact = MA_CLICK;
1705 }
1706 if (lastact != MA_NOTHING)
fc5b0934 1707 term_mouse(term, b, translate_button(b), lastact,
1708 x, y, shift, ctrl, alt);
fdedf2c8 1709 lasttime = thistime;
374330e2 1710}
1711
01c034ad 1712/*
1713 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1714 * into a cooked one (SELECT, EXTEND, PASTE).
1715 */
2c02ed02 1716static Mouse_Button translate_button(Mouse_Button button)
32874aea 1717{
01c034ad 1718 if (button == MBT_LEFT)
1719 return MBT_SELECT;
1720 if (button == MBT_MIDDLE)
ebc0310d 1721 return cfg.mouse_is_xterm == 1 ? MBT_PASTE : MBT_EXTEND;
01c034ad 1722 if (button == MBT_RIGHT)
ebc0310d 1723 return cfg.mouse_is_xterm == 1 ? MBT_EXTEND : MBT_PASTE;
2d466ffd 1724 return 0; /* shouldn't happen */
01c034ad 1725}
1726
32874aea 1727static void show_mouseptr(int show)
1728{
554c540d 1729 static int cursor_visible = 1;
32874aea 1730 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1731 show = 1;
554c540d 1732 if (cursor_visible && !show)
32874aea 1733 ShowCursor(FALSE);
554c540d 1734 else if (!cursor_visible && show)
32874aea 1735 ShowCursor(TRUE);
554c540d 1736 cursor_visible = show;
1737}
1738
6908fed7 1739static int is_alt_pressed(void)
1740{
1741 BYTE keystate[256];
1742 int r = GetKeyboardState(keystate);
1743 if (!r)
1744 return FALSE;
1745 if (keystate[VK_MENU] & 0x80)
1746 return TRUE;
1747 if (keystate[VK_RMENU] & 0x80)
1748 return TRUE;
1749 return FALSE;
1750}
1751
25dba9e5 1752static int is_shift_pressed(void)
1753{
1754 BYTE keystate[256];
1755 int r = GetKeyboardState(keystate);
1756 if (!r)
1757 return FALSE;
1758 if (keystate[VK_SHIFT] & 0x80)
1759 return TRUE;
1760 return FALSE;
1761}
1762
68f9b3d9 1763static int resizing;
1764
32874aea 1765static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1766 WPARAM wParam, LPARAM lParam)
1767{
374330e2 1768 HDC hdc;
374330e2 1769 static int ignore_clip = FALSE;
3ad8c6db 1770 static int need_backend_resize = FALSE;
1ba99d2c 1771 static int fullscr_on_max = FALSE;
d8a13f62 1772 static UINT last_mousemove = 0;
374330e2 1773
1774 switch (message) {
59ad2c03 1775 case WM_TIMER:
1776 if (pending_netevent)
1777 enact_pending_netevent();
25dba9e5 1778 if (GetCapture() != hwnd ||
1779 (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
887035a5 1780 term_out(term);
32874aea 1781 noise_regular();
1782 HideCaret(hwnd);
887035a5 1783 term_update(term);
32874aea 1784 ShowCaret(hwnd);
1785 if (cfg.ping_interval > 0) {
1786 time_t now;
1787 time(&now);
1788 if (now - last_movement > cfg.ping_interval) {
e3be8de5 1789 if (back)
1790 back->special(backhandle, TS_PING);
32874aea 1791 last_movement = now;
1792 }
1793 }
7732d38a 1794 net_pending_errors();
59ad2c03 1795 return 0;
374330e2 1796 case WM_CREATE:
1797 break;
68130d34 1798 case WM_CLOSE:
f6f450e2 1799 {
1800 char *str;
1801 show_mouseptr(1);
1802 str = dupprintf("%s Exit Confirmation", appname);
1803 if (!cfg.warn_on_close || session_closed ||
1804 MessageBox(hwnd,
1805 "Are you sure you want to close this session?",
1806 str, MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1807 DestroyWindow(hwnd);
1808 sfree(str);
1809 }
68130d34 1810 return 0;
374330e2 1811 case WM_DESTROY:
32874aea 1812 show_mouseptr(1);
1813 PostQuitMessage(0);
374330e2 1814 return 0;
63be7767 1815 case WM_COMMAND:
6833a413 1816 case WM_SYSCOMMAND:
1817 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1818 case IDM_SHOWLOG:
c5e9c988 1819 showeventlog(hwnd);
374330e2 1820 break;
1821 case IDM_NEWSESS:
1822 case IDM_DUPSESS:
6833a413 1823 case IDM_SAVEDSESS:
374330e2 1824 {
1825 char b[2048];
1826 char c[30], *cl;
e4e4cc7e 1827 int freecl = FALSE;
374330e2 1828 STARTUPINFO si;
1829 PROCESS_INFORMATION pi;
1830 HANDLE filemap = NULL;
1831
1832 if (wParam == IDM_DUPSESS) {
1833 /*
1834 * Allocate a file-mapping memory chunk for the
1835 * config structure.
1836 */
1837 SECURITY_ATTRIBUTES sa;
1838 Config *p;
1839
1840 sa.nLength = sizeof(sa);
1841 sa.lpSecurityDescriptor = NULL;
1842 sa.bInheritHandle = TRUE;
32874aea 1843 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1844 &sa,
1845 PAGE_READWRITE,
32874aea 1846 0, sizeof(Config), NULL);
374330e2 1847 if (filemap) {
32874aea 1848 p = (Config *) MapViewOfFile(filemap,
1849 FILE_MAP_WRITE,
1850 0, 0, sizeof(Config));
374330e2 1851 if (p) {
1852 *p = cfg; /* structure copy */
1853 UnmapViewOfFile(p);
1854 }
1855 }
1d470ad2 1856 sprintf(c, "putty &%p", filemap);
374330e2 1857 cl = c;
0a4aa984 1858 } else if (wParam == IDM_SAVEDSESS) {
0b4f0bc0 1859 if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
f8d7977b 1860 char *session =
0b4f0bc0 1861 sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
3d88e64d 1862 cl = snewn(16 + strlen(session), char);
f8d7977b 1863 /* 8, but play safe */
1864 if (!cl)
1865 cl = NULL;
1866 /* not a very important failure mode */
1867 else {
1868 sprintf(cl, "putty @%s", session);
1869 freecl = TRUE;
1870 }
1871 } else
1872 break;
374330e2 1873 } else
6833a413 1874 cl = NULL;
374330e2 1875
32874aea 1876 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1877 si.cb = sizeof(si);
1878 si.lpReserved = NULL;
1879 si.lpDesktop = NULL;
1880 si.lpTitle = NULL;
1881 si.dwFlags = 0;
1882 si.cbReserved2 = 0;
1883 si.lpReserved2 = NULL;
32874aea 1884 CreateProcess(b, cl, NULL, NULL, TRUE,
1885 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1886
1887 if (filemap)
1888 CloseHandle(filemap);
e4e4cc7e 1889 if (freecl)
dcbde236 1890 sfree(cl);
374330e2 1891 }
1892 break;
e3be8de5 1893 case IDM_RESTART:
1894 if (!back) {
1895 logevent(NULL, "----- Session restarted -----");
1896 start_backend();
1897 }
1898
1899 break;
32874aea 1900 case IDM_RECONF:
1901 {
5a73255e 1902 Config prev_cfg;
1903 int init_lvl = 1;
1904
32874aea 1905 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
5a73255e 1906 prev_cfg = cfg;
e1c8e0ed 1907
32874aea 1908 if (!do_reconfig(hwnd))
1909 break;
e1c8e0ed 1910
2cae8529 1911 {
1912 /* Disable full-screen if resizing forbidden */
1913 HMENU m = GetSystemMenu (hwnd, FALSE);
1914 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1915 (cfg.resize_action == RESIZE_DISABLED)
1916 ? MF_GRAYED : MF_ENABLED);
1917 /* Gracefully unzoom if necessary */
1ba99d2c 1918 if (IsZoomed(hwnd) &&
2cae8529 1919 (cfg.resize_action == RESIZE_DISABLED)) {
1ba99d2c 1920 ShowWindow(hwnd, SW_RESTORE);
2cae8529 1921 }
a401e5f3 1922 }
1923
c229ef97 1924 /* Pass new config data to the logging module */
1925 log_reconfig(logctx, &cfg);
e1c8e0ed 1926
32874aea 1927 sfree(logpal);
1928 /*
1929 * Flush the line discipline's edit buffer in the
1930 * case where local editing has just been disabled.
1931 */
e3be8de5 1932 if (ldisc)
1933 ldisc_send(ldisc, NULL, 0, 0);
32874aea 1934 if (pal)
1935 DeleteObject(pal);
1936 logpal = NULL;
1937 pal = NULL;
1938 cfgtopalette();
1939 init_palette();
1940
64734920 1941 /* Pass new config data to the terminal */
1942 term_reconfig(term, &cfg);
0d2086c5 1943
86916870 1944 /* Pass new config data to the back end */
e3be8de5 1945 if (back)
1946 back->reconfig(backhandle, &cfg);
86916870 1947
5a73255e 1948 /* Screen size changed ? */
1949 if (cfg.height != prev_cfg.height ||
1950 cfg.width != prev_cfg.width ||
1951 cfg.savelines != prev_cfg.savelines ||
0ed2f48e 1952 cfg.resize_action == RESIZE_FONT ||
811f10b3 1953 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
0ed2f48e 1954 cfg.resize_action == RESIZE_DISABLED)
887035a5 1955 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 1956
32874aea 1957 /* Enable or disable the scroll bar, etc */
1958 {
1959 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1960 LONG nexflag, exflag =
1961 GetWindowLong(hwnd, GWL_EXSTYLE);
1962
1963 nexflag = exflag;
5a73255e 1964 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 1965 if (cfg.alwaysontop) {
1966 nexflag |= WS_EX_TOPMOST;
1967 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1968 SWP_NOMOVE | SWP_NOSIZE);
1969 } else {
1970 nexflag &= ~(WS_EX_TOPMOST);
1971 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1972 SWP_NOMOVE | SWP_NOSIZE);
1973 }
1974 }
1975 if (cfg.sunken_edge)
1976 nexflag |= WS_EX_CLIENTEDGE;
1977 else
1978 nexflag &= ~(WS_EX_CLIENTEDGE);
1979
1980 nflg = flag;
1ba99d2c 1981 if (is_full_screen() ?
2cae8529 1982 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
32874aea 1983 nflg |= WS_VSCROLL;
1984 else
1985 nflg &= ~WS_VSCROLL;
811f10b3 1986
1987 if (cfg.resize_action == RESIZE_DISABLED ||
1988 is_full_screen())
1989 nflg &= ~WS_THICKFRAME;
1990 else
1991 nflg |= WS_THICKFRAME;
1992
ad5c93cc 1993 if (cfg.resize_action == RESIZE_DISABLED)
811f10b3 1994 nflg &= ~WS_MAXIMIZEBOX;
32874aea 1995 else
811f10b3 1996 nflg |= WS_MAXIMIZEBOX;
32874aea 1997
1998 if (nflg != flag || nexflag != exflag) {
32874aea 1999 if (nflg != flag)
2000 SetWindowLong(hwnd, GWL_STYLE, nflg);
2001 if (nexflag != exflag)
2002 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
2003
32874aea 2004 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
2005 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 2006 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
2007 SWP_FRAMECHANGED);
c9def1b8 2008
5a73255e 2009 init_lvl = 2;
e44d78b6 2010 }
32874aea 2011 }
5a73255e 2012
e44d78b6 2013 /* Oops */
ad5c93cc 2014 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 2015 force_normal(hwnd);
5a73255e 2016 init_lvl = 2;
2017 }
2018
a8327734 2019 set_title(NULL, cfg.wintitle);
32874aea 2020 if (IsIconic(hwnd)) {
2021 SetWindowText(hwnd,
2022 cfg.win_name_always ? window_name :
2023 icon_name);
3da0b1d2 2024 }
5a73255e 2025
9a30e26b 2026 if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
5a73255e 2027 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
9a30e26b 2028 cfg.font.isbold != prev_cfg.font.isbold ||
2029 cfg.font.height != prev_cfg.font.height ||
2030 cfg.font.charset != prev_cfg.font.charset ||
5a73255e 2031 cfg.vtmode != prev_cfg.vtmode ||
2032 cfg.bold_colour != prev_cfg.bold_colour ||
0ed2f48e 2033 cfg.resize_action == RESIZE_DISABLED ||
2034 cfg.resize_action == RESIZE_EITHER ||
2035 (cfg.resize_action != prev_cfg.resize_action))
5a73255e 2036 init_lvl = 2;
2037
2038 InvalidateRect(hwnd, NULL, TRUE);
2039 reset_window(init_lvl);
7732d38a 2040 net_pending_errors();
32874aea 2041 }
2042 break;
bc1235d4 2043 case IDM_COPYALL:
887035a5 2044 term_copyall(term);
bc1235d4 2045 break;
63be7767 2046 case IDM_PASTE:
2047 term_do_paste(term);
2048 break;
32874aea 2049 case IDM_CLRSB:
887035a5 2050 term_clrsb(term);
32874aea 2051 break;
2052 case IDM_RESET:
887035a5 2053 term_pwron(term);
e3be8de5 2054 if (ldisc)
2055 ldisc_send(ldisc, NULL, 0, 0);
32874aea 2056 break;
374330e2 2057 case IDM_ABOUT:
32874aea 2058 showabout(hwnd);
374330e2 2059 break;
70133c0e 2060 case IDM_HELP:
2061 WinHelp(hwnd, help_path,
2062 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
2063 break;
0d6dcf38 2064 case SC_MOUSEMENU:
2065 /*
2066 * We get this if the System menu has been activated
2067 * using the mouse.
2068 */
2069 show_mouseptr(1);
2070 break;
dfca2656 2071 case SC_KEYMENU:
2072 /*
0d6dcf38 2073 * We get this if the System menu has been activated
2074 * using the keyboard. This might happen from within
2075 * TranslateKey, in which case it really wants to be
2076 * followed by a `space' character to actually _bring
2077 * the menu up_ rather than just sitting there in
2078 * `ready to appear' state.
dfca2656 2079 */
0d6dcf38 2080 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 2081 if( lParam == 0 )
2082 PostMessage(hwnd, WM_CHAR, ' ', 0);
2083 break;
a401e5f3 2084 case IDM_FULLSCREEN:
1ba99d2c 2085 flip_full_screen();
2086 break;
32874aea 2087 default:
2088 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
2089 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
2090 }
125105d1 2091 if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) {
2092 int i = (wParam - IDM_SPECIAL_MIN) / 0x10;
2093 int j;
2094 /*
2095 * Ensure we haven't been sent a bogus SYSCOMMAND
2096 * which would cause us to reference invalid memory
2097 * and crash. Perhaps I'm just too paranoid here.
2098 */
2099 for (j = 0; j < i; j++)
2100 if (!specials || !specials[j].name)
2101 break;
2102 if (j == i) {
e3be8de5 2103 if (back)
2104 back->special(backhandle, specials[i].code);
125105d1 2105 net_pending_errors();
2106 }
2107 }
374330e2 2108 }
2109 break;
37508af4 2110
2111#define X_POS(l) ((int)(short)LOWORD(l))
2112#define Y_POS(l) ((int)(short)HIWORD(l))
2113
5a73255e 2114#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2115#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
374330e2 2116 case WM_LBUTTONDOWN:
374330e2 2117 case WM_MBUTTONDOWN:
374330e2 2118 case WM_RBUTTONDOWN:
01c034ad 2119 case WM_LBUTTONUP:
2120 case WM_MBUTTONUP:
374330e2 2121 case WM_RBUTTONUP:
ebc0310d 2122 if (message == WM_RBUTTONDOWN &&
2123 ((wParam & MK_CONTROL) || (cfg.mouse_is_xterm == 2))) {
63be7767 2124 POINT cursorpos;
2125
ebc0310d 2126 show_mouseptr(1); /* make sure pointer is visible */
63be7767 2127 GetCursorPos(&cursorpos);
2128 TrackPopupMenu(popup_menus[CTXMENU].menu,
2129 TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON,
2130 cursorpos.x, cursorpos.y,
2131 0, hwnd, NULL);
2132 break;
2133 }
01c034ad 2134 {
2135 int button, press;
6908fed7 2136
01c034ad 2137 switch (message) {
32874aea 2138 case WM_LBUTTONDOWN:
2139 button = MBT_LEFT;
2140 press = 1;
2141 break;
2142 case WM_MBUTTONDOWN:
2143 button = MBT_MIDDLE;
2144 press = 1;
2145 break;
2146 case WM_RBUTTONDOWN:
2147 button = MBT_RIGHT;
2148 press = 1;
2149 break;
2150 case WM_LBUTTONUP:
2151 button = MBT_LEFT;
2152 press = 0;
2153 break;
2154 case WM_MBUTTONUP:
2155 button = MBT_MIDDLE;
2156 press = 0;
2157 break;
2158 case WM_RBUTTONUP:
2159 button = MBT_RIGHT;
2160 press = 0;
2161 break;
2d466ffd 2162 default:
2163 button = press = 0; /* shouldn't happen */
01c034ad 2164 }
2165 show_mouseptr(1);
2cae8529 2166 /*
2167 * Special case: in full-screen mode, if the left
2168 * button is clicked in the very top left corner of the
2169 * window, we put up the System menu instead of doing
2170 * selection.
2171 */
52a4d159 2172 {
2173 char mouse_on_hotspot = 0;
2174 POINT pt;
2175
2176 GetCursorPos(&pt);
2177#ifndef NO_MULTIMON
2178 {
2179 HMONITOR mon;
2180 MONITORINFO mi;
2181
2182 mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
2183
2184 if (mon != NULL) {
2185 mi.cbSize = sizeof(MONITORINFO);
2186 GetMonitorInfo(mon, &mi);
2187
2188 if (mi.rcMonitor.left == pt.x &&
2189 mi.rcMonitor.top == pt.y) {
2190 mouse_on_hotspot = 1;
2191 }
52a4d159 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 */
c6958dfe 2904void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
2905 unsigned long attr, int lattr)
32874aea 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;
36566009 2912 int fnt_width, char_width;
4eeb7d09 2913 int text_adjust = 0;
2914 static int *IpDx = 0, IpDxLEN = 0;
2915
36566009 2916 lattr &= LATTR_MODE;
2917
2918 char_width = fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2919
4eeb7d09 2920 if (attr & ATTR_WIDE)
2921 char_width *= 2;
59ad2c03 2922
4eeb7d09 2923 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2924 int i;
32874aea 2925 if (len > IpDxLEN) {
59ad2c03 2926 sfree(IpDx);
3d88e64d 2927 IpDx = snewn(len + 16, int);
32874aea 2928 IpDxLEN = (len + 16);
59ad2c03 2929 }
32874aea 2930 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2931 IpDx[i] = char_width;
59ad2c03 2932 }
374330e2 2933
5a73255e 2934 /* Only want the left half of double width lines */
887035a5 2935 if (lattr != LATTR_NORM && x*2 >= term->cols)
5a73255e 2936 return;
2937
c9def1b8 2938 x *= fnt_width;
374330e2 2939 y *= font_height;
5a73255e 2940 x += offset_width;
2941 y += offset_height;
374330e2 2942
887035a5 2943 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
4eeb7d09 2944 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2945 attr ^= ATTR_CUR_XOR;
2946 }
2947
2948 nfont = 0;
4eeb7d09 2949 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2950 /* Assume a poorman font is borken in other ways too. */
2951 lattr = LATTR_WIDE;
2952 } else
2953 switch (lattr) {
2954 case LATTR_NORM:
2955 break;
2956 case LATTR_WIDE:
2957 nfont |= FONT_WIDE;
374330e2 2958 break;
4eeb7d09 2959 default:
2960 nfont |= FONT_WIDE + FONT_HIGH;
2961 break;
2962 }
5a73255e 2963 if (attr & ATTR_NARROW)
2964 nfont |= FONT_NARROW;
4eeb7d09 2965
2966 /* Special hack for the VT100 linedraw glyphs. */
36566009 2967 if (text[0] >= 0x23BA && text[0] <= 0x23BD) {
2968 switch ((unsigned char) (text[0])) {
2969 case 0xBA:
2970 text_adjust = -2 * font_height / 5;
2971 break;
2972 case 0xBB:
2973 text_adjust = -1 * font_height / 5;
2974 break;
2975 case 0xBC:
2976 text_adjust = font_height / 5;
2977 break;
2978 case 0xBD:
2979 text_adjust = 2 * font_height / 5;
2980 break;
2981 }
2982 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2983 text_adjust *= 2;
2984 text[0] = ucsdata.unitab_xterm['q'];
2985 if (attr & ATTR_UNDER) {
2986 attr &= ~ATTR_UNDER;
2987 force_manual_underline = 1;
374330e2 2988 }
2989 }
2990
4eeb7d09 2991 /* Anything left as an original character set is unprintable. */
36566009 2992 if (DIRECT_CHAR(text[0])) {
2993 int i;
2994 for (i = 0; i < len; i++)
2995 text[i] = 0xFFFD;
4eeb7d09 2996 }
2997
2998 /* OEM CP */
36566009 2999 if ((text[0] & CSET_MASK) == CSET_OEMCP)
4eeb7d09 3000 nfont |= FONT_OEM;
3001
37ca32ed 3002 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
3003 nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
3004 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
3005 nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
374330e2 3006 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
3007 nfont |= FONT_BOLD;
3008 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
3009 nfont |= FONT_UNDERLINE;
4eeb7d09 3010 another_font(nfont);
32874aea 3011 if (!fonts[nfont]) {
3012 if (nfont & FONT_UNDERLINE)
59ad2c03 3013 force_manual_underline = 1;
3014 /* Don't do the same for manual bold, it could be bad news. */
3015
32874aea 3016 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 3017 }
4eeb7d09 3018 another_font(nfont);
3019 if (!fonts[nfont])
3020 nfont = FONT_NORMAL;
374330e2 3021 if (attr & ATTR_REVERSE) {
32874aea 3022 t = nfg;
3023 nfg = nbg;
3024 nbg = t;
374330e2 3025 }
3026 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
37ca32ed 3027 nfg |= 1;
59ad2c03 3028 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
37ca32ed 3029 nbg |= 1;
374330e2 3030 fg = colours[nfg];
3031 bg = colours[nbg];
32874aea 3032 SelectObject(hdc, fonts[nfont]);
3033 SetTextColor(hdc, fg);
3034 SetBkColor(hdc, bg);
c6958dfe 3035 if (attr & TATTR_COMBINING)
3036 SetBkMode(hdc, TRANSPARENT);
3037 else
3038 SetBkMode(hdc, OPAQUE);
32874aea 3039 line_box.left = x;
3040 line_box.top = y;
4eeb7d09 3041 line_box.right = x + char_width * len;
32874aea 3042 line_box.bottom = y + font_height;
4eeb7d09 3043
5a73255e 3044 /* Only want the left half of double width lines */
887035a5 3045 if (line_box.right > font_width*term->cols+offset_width)
3046 line_box.right = font_width*term->cols+offset_width;
5a73255e 3047
4eeb7d09 3048 /* We're using a private area for direct to font. (512 chars.) */
36566009 3049 if (ucsdata.dbcs_screenfont && (text[0] & CSET_MASK) == CSET_ACP) {
4eeb7d09 3050 /* Ho Hum, dbcs fonts are a PITA! */
3051 /* To display on W9x I have to convert to UCS */
3052 static wchar_t *uni_buf = 0;
3053 static int uni_len = 0;
5a73255e 3054 int nlen, mptr;
4eeb7d09 3055 if (len > uni_len) {
3056 sfree(uni_buf);
3d88e64d 3057 uni_len = len;
3058 uni_buf = snewn(uni_len, wchar_t);
4eeb7d09 3059 }
4eeb7d09 3060
5a73255e 3061 for(nlen = mptr = 0; mptr<len; mptr++) {
3062 uni_buf[nlen] = 0xFFFD;
21d2b241 3063 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
36566009 3064 char dbcstext[2];
3065 dbcstext[0] = text[mptr] & 0xFF;
3066 dbcstext[1] = text[mptr+1] & 0xFF;
5a73255e 3067 IpDx[nlen] += char_width;
21d2b241 3068 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
36566009 3069 dbcstext, 2, uni_buf+nlen, 1);
5a73255e 3070 mptr++;
3071 }
3072 else
3073 {
36566009 3074 char dbcstext[1];
3075 dbcstext[0] = text[mptr] & 0xFF;
21d2b241 3076 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
36566009 3077 dbcstext, 1, uni_buf+nlen, 1);
5a73255e 3078 }
3079 nlen++;
3080 }
4eeb7d09 3081 if (nlen <= 0)
3082 return; /* Eeek! */
3083
3084 ExtTextOutW(hdc, x,
3085 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 3086 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3087 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3088 SetBkMode(hdc, TRANSPARENT);
3089 ExtTextOutW(hdc, x - 1,
3090 y - font_height * (lattr ==
3091 LATTR_BOT) + text_adjust,
5a73255e 3092 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 3093 }
5a73255e 3094
3095 IpDx[0] = -1;
36566009 3096 } else if (DIRECT_FONT(text[0])) {
3097 static char *directbuf = NULL;
3098 static int directlen = 0;
3099 int i;
3100 if (len > directlen) {
3101 directlen = len;
3102 directbuf = sresize(directbuf, directlen, char);
3103 }
3104
3105 for (i = 0; i < len; i++)
3106 directbuf[i] = text[i] & 0xFF;
3107
4eeb7d09 3108 ExtTextOut(hdc, x,
3109 y - font_height * (lattr == LATTR_BOT) + text_adjust,
36566009 3110 ETO_CLIPPED | ETO_OPAQUE, &line_box, directbuf, len, IpDx);
4eeb7d09 3111 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
3112 SetBkMode(hdc, TRANSPARENT);
3113
3114 /* GRR: This draws the character outside it's box and can leave
3115 * 'droppings' even with the clip box! I suppose I could loop it
3116 * one character at a time ... yuk.
3117 *
3118 * Or ... I could do a test print with "W", and use +1 or -1 for this
3119 * shift depending on if the leftmost column is blank...
3120 */
3121 ExtTextOut(hdc, x - 1,
3122 y - font_height * (lattr ==
3123 LATTR_BOT) + text_adjust,
36566009 3124 ETO_CLIPPED, &line_box, directbuf, len, IpDx);
4eeb7d09 3125 }
3126 } else {
3127 /* And 'normal' unicode characters */
3128 static WCHAR *wbuf = NULL;
3129 static int wlen = 0;
3130 int i;
c6958dfe 3131
4eeb7d09 3132 if (wlen < len) {
3133 sfree(wbuf);
3134 wlen = len;
3d88e64d 3135 wbuf = snewn(wlen, WCHAR);
4eeb7d09 3136 }
c6958dfe 3137
4eeb7d09 3138 for (i = 0; i < len; i++)
36566009 3139 wbuf[i] = text[i];
4eeb7d09 3140
f0fccd51 3141 /* print Glyphs as they are, without Windows' Shaping*/
3142 exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
c6958dfe 3143 &line_box, wbuf, len, IpDx, !(attr & TATTR_COMBINING));
f0fccd51 3144/* ExtTextOutW(hdc, x,
4eeb7d09 3145 y - font_height * (lattr == LATTR_BOT) + text_adjust,
3146 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
f0fccd51 3147 */
4eeb7d09 3148
3149 /* And the shadow bold hack. */
5a73255e 3150 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 3151 SetBkMode(hdc, TRANSPARENT);
3152 ExtTextOutW(hdc, x - 1,
3153 y - font_height * (lattr ==
3154 LATTR_BOT) + text_adjust,
3155 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
3156 }
374330e2 3157 }
4eeb7d09 3158 if (lattr != LATTR_TOP && (force_manual_underline ||
3159 (und_mode == UND_LINE
3160 && (attr & ATTR_UNDER)))) {
32874aea 3161 HPEN oldpen;
4eeb7d09 3162 int dec = descent;
3163 if (lattr == LATTR_BOT)
3164 dec = dec * 2 - font_height;
3165
32874aea 3166 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 3167 MoveToEx(hdc, x, y + dec, NULL);
3168 LineTo(hdc, x + len * char_width, y + dec);
32874aea 3169 oldpen = SelectObject(hdc, oldpen);
3170 DeleteObject(oldpen);
374330e2 3171 }
4eeb7d09 3172}
3173
c6958dfe 3174/*
3175 * Wrapper that handles combining characters.
3176 */
3177void do_text(Context ctx, int x, int y, wchar_t *text, int len,
3178 unsigned long attr, int lattr)
3179{
3180 if (attr & TATTR_COMBINING) {
3181 unsigned long a = 0;
3182 attr &= ~TATTR_COMBINING;
3183 while (len--) {
3184 do_text_internal(ctx, x, y, text, 1, attr | a, lattr);
3185 text++;
3186 a = TATTR_COMBINING;
3187 }
3188 } else
3189 do_text_internal(ctx, x, y, text, len, attr, lattr);
3190}
3191
36566009 3192void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
4eeb7d09 3193 unsigned long attr, int lattr)
3194{
3195
3196 int fnt_width;
3197 int char_width;
3198 HDC hdc = ctx;
3199 int ctype = cfg.cursor_type;
3200
887035a5 3201 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
36566009 3202 if (*text != UCSWIDE) {
4eeb7d09 3203 do_text(ctx, x, y, text, len, attr, lattr);
3204 return;
3205 }
3206 ctype = 2;
3207 attr |= TATTR_RIGHTCURS;
3208 }
3209
3210 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
3211 if (attr & ATTR_WIDE)
3212 char_width *= 2;
3213 x *= fnt_width;
3214 y *= font_height;
5a73255e 3215 x += offset_width;
3216 y += offset_height;
4eeb7d09 3217
887035a5 3218 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
374330e2 3219 POINT pts[5];
32874aea 3220 HPEN oldpen;
374330e2 3221 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 3222 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 3223 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 3224 pts[1].y = pts[2].y = y + font_height - 1;
3225 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3226 Polyline(hdc, pts, 5);
3227 oldpen = SelectObject(hdc, oldpen);
3228 DeleteObject(oldpen);
4eeb7d09 3229 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 3230 int startx, starty, dx, dy, length, i;
4eeb7d09 3231 if (ctype == 1) {
32874aea 3232 startx = x;
3233 starty = y + descent;
3234 dx = 1;
3235 dy = 0;
4eeb7d09 3236 length = char_width;
32874aea 3237 } else {
4e30ff69 3238 int xadjust = 0;
4eeb7d09 3239 if (attr & TATTR_RIGHTCURS)
3240 xadjust = char_width - 1;
32874aea 3241 startx = x + xadjust;
3242 starty = y;
3243 dx = 0;
3244 dy = 1;
3245 length = font_height;
3246 }
4eeb7d09 3247 if (attr & TATTR_ACTCURS) {
32874aea 3248 HPEN oldpen;
3249 oldpen =
3250 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3251 MoveToEx(hdc, startx, starty, NULL);
3252 LineTo(hdc, startx + dx * length, starty + dy * length);
3253 oldpen = SelectObject(hdc, oldpen);
3254 DeleteObject(oldpen);
3255 } else {
3256 for (i = 0; i < length; i++) {
3257 if (i % 2 == 0) {
3258 SetPixel(hdc, startx, starty, colours[23]);
3259 }
3260 startx += dx;
3261 starty += dy;
3262 }
3263 }
4e30ff69 3264 }
374330e2 3265}
3266
5a73255e 3267/* This function gets the actual width of a character in the normal font.
3268 */
2102eb8a 3269int char_width(Context ctx, int uc) {
5a73255e 3270 HDC hdc = ctx;
3271 int ibuf = 0;
3272
3273 /* If the font max is the same as the font ave width then this
3274 * function is a no-op.
3275 */
3276 if (!font_dualwidth) return 1;
3277
3278 switch (uc & CSET_MASK) {
36566009 3279 case CSET_ASCII:
21d2b241 3280 uc = ucsdata.unitab_line[uc & 0xFF];
5a73255e 3281 break;
36566009 3282 case CSET_LINEDRW:
21d2b241 3283 uc = ucsdata.unitab_xterm[uc & 0xFF];
5a73255e 3284 break;
36566009 3285 case CSET_SCOACS:
21d2b241 3286 uc = ucsdata.unitab_scoacs[uc & 0xFF];
5a73255e 3287 break;
3288 }
3289 if (DIRECT_FONT(uc)) {
21d2b241 3290 if (ucsdata.dbcs_screenfont) return 1;
5a73255e 3291
3292 /* Speedup, I know of no font where ascii is the wrong width */
36566009 3293 if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~')
5a73255e 3294 return 1;
3295
36566009 3296 if ( (uc & CSET_MASK) == CSET_ACP ) {
5a73255e 3297 SelectObject(hdc, fonts[FONT_NORMAL]);
36566009 3298 } else if ( (uc & CSET_MASK) == CSET_OEMCP ) {
5a73255e 3299 another_font(FONT_OEM);
3300 if (!fonts[FONT_OEM]) return 0;
3301
3302 SelectObject(hdc, fonts[FONT_OEM]);
3303 } else
3304 return 0;
3305
36566009 3306 if ( GetCharWidth32(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1 &&
3307 GetCharWidth(hdc, uc&~CSET_MASK, uc&~CSET_MASK, &ibuf) != 1)
5a73255e 3308 return 0;
3309 } else {
3310 /* Speedup, I know of no font where ascii is the wrong width */
3311 if (uc >= ' ' && uc <= '~') return 1;
3312
3313 SelectObject(hdc, fonts[FONT_NORMAL]);
3314 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3315 /* Okay that one worked */ ;
3316 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3317 /* This should work on 9x too, but it's "less accurate" */ ;
3318 else
3319 return 0;
3320 }
3321
3322 ibuf += font_width / 2 -1;
3323 ibuf /= font_width;
3324
3325 return ibuf;
3326}
3327
374330e2 3328/*
c9def1b8 3329 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3330 * codes. Returns number of bytes used or zero to drop the message
3331 * or -1 to forward the message to windows.
374330e2 3332 */
3cf144db 3333static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 3334 unsigned char *output)
3335{
374330e2 3336 BYTE keystate[256];
32874aea 3337 int scan, left_alt = 0, key_down, shift_state;
3338 int r, i, code;
3339 unsigned char *p = output;
4eeb7d09 3340 static int alt_sum = 0;
374330e2 3341
00e3ba0f 3342 HKL kbd_layout = GetKeyboardLayout(0);
3343
c264be08 3344 /* keys is for ToAsciiEx. There's some ick here, see below. */
3345 static WORD keys[3];
0c50ef57 3346 static int compose_char = 0;
3347 static WPARAM compose_key = 0;
32874aea 3348
c9def1b8 3349 r = GetKeyboardState(keystate);
32874aea 3350 if (!r)
3351 memset(keystate, 0, sizeof(keystate));
3352 else {
ec55b220 3353#if 0
4eeb7d09 3354#define SHOW_TOASCII_RESULT
32874aea 3355 { /* Tell us all about key events */
3356 static BYTE oldstate[256];
3357 static int first = 1;
3358 static int scan;
3359 int ch;
3360 if (first)
3361 memcpy(oldstate, keystate, sizeof(oldstate));
3362 first = 0;
3363
3364 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3365 debug(("+"));
3366 } else if ((HIWORD(lParam) & KF_UP)
3367 && scan == (HIWORD(lParam) & 0xFF)) {
3368 debug((". U"));
3369 } else {
3370 debug((".\n"));
3371 if (wParam >= VK_F1 && wParam <= VK_F20)
3372 debug(("K_F%d", wParam + 1 - VK_F1));
3373 else
3374 switch (wParam) {
3375 case VK_SHIFT:
3376 debug(("SHIFT"));
3377 break;
3378 case VK_CONTROL:
3379 debug(("CTRL"));
3380 break;
3381 case VK_MENU:
3382 debug(("ALT"));
3383 break;
3384 default:
3385 debug(("VK_%02x", wParam));
3386 }
3387 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3388 debug(("*"));
3389 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3390
3391 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3392 if (ch >= ' ' && ch <= '~')
3393 debug((", '%c'", ch));
3394 else if (ch)
3395 debug((", $%02x", ch));
3396
3397 if (keys[0])
3398 debug((", KB0=%02x", keys[0]));
3399 if (keys[1])
3400 debug((", KB1=%02x", keys[1]));
3401 if (keys[2])
3402 debug((", KB2=%02x", keys[2]));
3403
3404 if ((keystate[VK_SHIFT] & 0x80) != 0)
3405 debug((", S"));
3406 if ((keystate[VK_CONTROL] & 0x80) != 0)
3407 debug((", C"));
3408 if ((HIWORD(lParam) & KF_EXTENDED))
3409 debug((", E"));
3410 if ((HIWORD(lParam) & KF_UP))
3411 debug((", U"));
3412 }
3413
3414 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3415 else if ((HIWORD(lParam) & KF_UP))
3416 oldstate[wParam & 0xFF] ^= 0x80;
3417 else
3418 oldstate[wParam & 0xFF] ^= 0x81;
3419
3420 for (ch = 0; ch < 256; ch++)
3421 if (oldstate[ch] != keystate[ch])
3422 debug((", M%02x=%02x", ch, keystate[ch]));
3423
3424 memcpy(oldstate, keystate, sizeof(oldstate));
3425 }
ec55b220 3426#endif
3427
32874aea 3428 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 3429 keystate[VK_RMENU] = keystate[VK_MENU];
3430 }
3431
c9def1b8 3432
3433 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
5789bc94 3434 if ((cfg.funky_type == FUNKY_VT400 ||
3435 (cfg.funky_type <= FUNKY_LINUX && term->app_keypad_keys &&
887035a5 3436 !cfg.no_applic_k))
32874aea 3437 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 3438
3439 wParam = VK_EXECUTE;
3440
3441 /* UnToggle NUMLock */
32874aea 3442 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3443 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 3444 }
3445
3446 /* And write back the 'adjusted' state */
32874aea 3447 SetKeyboardState(keystate);
c9def1b8 3448 }
3449
3450 /* Disable Auto repeat if required */
887035a5 3451 if (term->repeat_off &&
3452 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
32874aea 3453 return 0;
c9def1b8 3454
32874aea 3455 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 3456 left_alt = 1;
3457
32874aea 3458 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 3459
95bbe1ae 3460 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 3461 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 3462 if (cfg.ctrlaltkeys)
3463 keystate[VK_MENU] = 0;
3464 else {
3465 keystate[VK_RMENU] = 0x80;
3466 left_alt = 0;
3467 }
3468 }
c9def1b8 3469
3470 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3471 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3472 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3473
95bbe1ae 3474 /* Note if AltGr was pressed and if it was used as a compose key */
3475 if (!compose_state) {
159eba53 3476 compose_key = 0x100;
95bbe1ae 3477 if (cfg.compose_key) {
32874aea 3478 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3479 compose_key = wParam;
3480 }
3481 if (wParam == VK_APPS)
3482 compose_key = wParam;
3483 }
3484
32874aea 3485 if (wParam == compose_key) {
3486 if (compose_state == 0
3487 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3488 1;
3489 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3490 compose_state = 2;
3491 else
3492 compose_state = 0;
32874aea 3493 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3494 compose_state = 0;
3495
32874aea 3496 if (compose_state > 1 && left_alt)
3497 compose_state = 0;
67c339f7 3498
c9def1b8 3499 /* Sanitize the number pad if not using a PC NumPad */
887035a5 3500 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
5789bc94 3501 && cfg.funky_type != FUNKY_XTERM)
3502 || cfg.funky_type == FUNKY_VT400 || cfg.nethack_keypad || compose_state) {
32874aea 3503 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3504 int nParam = 0;
32874aea 3505 switch (wParam) {
3506 case VK_INSERT:
3507 nParam = VK_NUMPAD0;
3508 break;
3509 case VK_END:
3510 nParam = VK_NUMPAD1;
3511 break;
3512 case VK_DOWN:
3513 nParam = VK_NUMPAD2;
3514 break;
3515 case VK_NEXT:
3516 nParam = VK_NUMPAD3;
3517 break;
3518 case VK_LEFT:
3519 nParam = VK_NUMPAD4;
3520 break;
3521 case VK_CLEAR:
3522 nParam = VK_NUMPAD5;
3523 break;
3524 case VK_RIGHT:
3525 nParam = VK_NUMPAD6;
3526 break;
3527 case VK_HOME:
3528 nParam = VK_NUMPAD7;
3529 break;
3530 case VK_UP:
3531 nParam = VK_NUMPAD8;
3532 break;
3533 case VK_PRIOR:
3534 nParam = VK_NUMPAD9;
3535 break;
3536 case VK_DELETE:
3537 nParam = VK_DECIMAL;
3538 break;
c9def1b8 3539 }
32874aea 3540 if (nParam) {
3541 if (keystate[VK_NUMLOCK] & 1)
3542 shift_state |= 1;
c9def1b8 3543 wParam = nParam;
3544 }
25d39ef6 3545 }
3546 }
3547
c9def1b8 3548 /* If a key is pressed and AltGr is not active */
32874aea 3549 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3550 /* Okay, prepare for most alts then ... */
3551 if (left_alt)
3552 *p++ = '\033';
374330e2 3553
c9def1b8 3554 /* Lets see if it's a pattern we know all about ... */
3555 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3556 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3557 return 0;
c9def1b8 3558 }
153580da 3559 if (wParam == VK_PRIOR && shift_state == 2) {
3560 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3561 return 0;
3562 }
c9def1b8 3563 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3564 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3565 return 0;
3566 }
153580da 3567 if (wParam == VK_NEXT && shift_state == 2) {
3568 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3569 return 0;
3570 }
32874aea 3571 if (wParam == VK_INSERT && shift_state == 1) {
887035a5 3572 term_do_paste(term);
32874aea 3573 return 0;
3574 }
c9def1b8 3575 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3576 return -1;
c9def1b8 3577 }
3578 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3579 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3580 return -1;
c9def1b8 3581 }
2cae8529 3582 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3583 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3584 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3585 flip_full_screen();
8f57d753 3586 return -1;
3587 }
ec55b220 3588 /* Control-Numlock for app-keypad mode switch */
3589 if (wParam == VK_PAUSE && shift_state == 2) {
887035a5 3590 term->app_keypad_keys ^= 1;
ec55b220 3591 return 0;
3592 }
374330e2 3593
c9def1b8 3594 /* Nethack keypad */
3595 if (cfg.nethack_keypad && !left_alt) {
32874aea 3596 switch (wParam) {
3597 case VK_NUMPAD1:
3598 *p++ = shift_state ? 'B' : 'b';
3599 return p - output;
3600 case VK_NUMPAD2:
3601 *p++ = shift_state ? 'J' : 'j';
3602 return p - output;
3603 case VK_NUMPAD3:
3604 *p++ = shift_state ? 'N' : 'n';
3605 return p - output;
3606 case VK_NUMPAD4:
3607 *p++ = shift_state ? 'H' : 'h';
3608 return p - output;
3609 case VK_NUMPAD5:
3610 *p++ = shift_state ? '.' : '.';
3611 return p - output;
3612 case VK_NUMPAD6:
3613 *p++ = shift_state ? 'L' : 'l';
3614 return p - output;
3615 case VK_NUMPAD7:
3616 *p++ = shift_state ? 'Y' : 'y';
3617 return p - output;
3618 case VK_NUMPAD8:
3619 *p++ = shift_state ? 'K' : 'k';
3620 return p - output;
3621 case VK_NUMPAD9:
3622 *p++ = shift_state ? 'U' : 'u';
3623 return p - output;
3624 }
c9def1b8 3625 }
3626
3627 /* Application Keypad */
3628 if (!left_alt) {
32874aea 3629 int xkey = 0;
3630
5789bc94 3631 if (cfg.funky_type == FUNKY_VT400 ||
3632 (cfg.funky_type <= FUNKY_LINUX &&
887035a5 3633 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
32874aea 3634 case VK_EXECUTE:
3635 xkey = 'P';
3636 break;
3637 case VK_DIVIDE:
3638 xkey = 'Q';
3639 break;
3640 case VK_MULTIPLY:
3641 xkey = 'R';
3642 break;
3643 case VK_SUBTRACT:
3644 xkey = 'S';
3645 break;
3646 }
887035a5 3647 if (term->app_keypad_keys && !cfg.no_applic_k)
32874aea 3648 switch (wParam) {
3649 case VK_NUMPAD0:
3650 xkey = 'p';
3651 break;
3652 case VK_NUMPAD1:
3653 xkey = 'q';
3654 break;
3655 case VK_NUMPAD2:
3656 xkey = 'r';
3657 break;
3658 case VK_NUMPAD3:
3659 xkey = 's';
3660 break;
3661 case VK_NUMPAD4:
3662 xkey = 't';
3663 break;
3664 case VK_NUMPAD5:
3665 xkey = 'u';
3666 break;
3667 case VK_NUMPAD6:
3668 xkey = 'v';
3669 break;
3670 case VK_NUMPAD7:
3671 xkey = 'w';
3672 break;
3673 case VK_NUMPAD8:
3674 xkey = 'x';
3675 break;
3676 case VK_NUMPAD9:
3677 xkey = 'y';
3678 break;
3679
3680 case VK_DECIMAL:
3681 xkey = 'n';
3682 break;
3683 case VK_ADD:
5789bc94 3684 if (cfg.funky_type == FUNKY_XTERM) {
32874aea 3685 if (shift_state)
3686 xkey = 'l';
3687 else
3688 xkey = 'k';
3689 } else if (shift_state)
3690 xkey = 'm';
c9def1b8 3691 else
32874aea 3692 xkey = 'l';
3693 break;
3694
3695 case VK_DIVIDE:
5789bc94 3696 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3697 xkey = 'o';
3698 break;
3699 case VK_MULTIPLY:
5789bc94 3700 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3701 xkey = 'j';
3702 break;
3703 case VK_SUBTRACT:
5789bc94 3704 if (cfg.funky_type == FUNKY_XTERM)
32874aea 3705 xkey = 'm';
3706 break;
3707
3708 case VK_RETURN:
3709 if (HIWORD(lParam) & KF_EXTENDED)
3710 xkey = 'M';
3711 break;
c9def1b8 3712 }
32874aea 3713 if (xkey) {
887035a5 3714 if (term->vt52_mode) {
32874aea 3715 if (xkey >= 'P' && xkey <= 'S')
3716 p += sprintf((char *) p, "\x1B%c", xkey);
3717 else
3718 p += sprintf((char *) p, "\x1B?%c", xkey);
3719 } else
3720 p += sprintf((char *) p, "\x1BO%c", xkey);
3721 return p - output;
c9def1b8 3722 }
3723 }
3724
32874aea 3725 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3726 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3727 *p++ = 0;
3728 return -2;
c9def1b8 3729 }
e8e8d6e2 3730 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3731 /* We do the opposite of what is configured */
3732 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3733 *p++ = 0;
3734 return -2;
3735 }
32874aea 3736 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3737 *p++ = 0x1B;
3738 *p++ = '[';
3739 *p++ = 'Z';
3740 return p - output;
c9def1b8 3741 }
32874aea 3742 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3743 *p++ = 0;
3744 return p - output;
c9def1b8 3745 }
32874aea 3746 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3747 *p++ = 160;
3748 return p - output;
c9def1b8 3749 }
32874aea 3750 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3751 *p++ = 3;
a5f3e637 3752 *p++ = 0;
3753 return -2;
c9def1b8 3754 }
32874aea 3755 if (wParam == VK_PAUSE) { /* Break/Pause */
3756 *p++ = 26;
3757 *p++ = 0;
3758 return -2;
95bbe1ae 3759 }
c9def1b8 3760 /* Control-2 to Control-8 are special */
32874aea 3761 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3762 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3763 return p - output;
3764 }
237f2b6e 3765 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
c9def1b8 3766 *p++ = 0x1F;
3767 return p - output;
3768 }
3769 if (shift_state == 2 && wParam == 0xDF) {
3770 *p++ = 0x1C;
3771 return p - output;
3772 }
9aa461e4 3773 if (shift_state == 3 && wParam == 0xDE) {
3774 *p++ = 0x1E; /* Ctrl-~ == Ctrl-^ in xterm at least */
3775 return p - output;
3776 }
887035a5 3777 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
32874aea 3778 *p++ = '\r';
3779 *p++ = '\n';
c9def1b8 3780 return p - output;
3781 }
374330e2 3782
c5e9c988 3783 /*
c9def1b8 3784 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3785 * for integer decimal nn.)
3786 *
3787 * We also deal with the weird ones here. Linux VCs replace F1
3788 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3789 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3790 * respectively.
c5e9c988 3791 */
c9def1b8 3792 code = 0;
3793 switch (wParam) {
32874aea 3794 case VK_F1:
3795 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3796 break;
3797 case VK_F2:
3798 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3799 break;
3800 case VK_F3:
3801 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3802 break;
3803 case VK_F4:
3804 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3805 break;
3806 case VK_F5:
3807 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3808 break;
3809 case VK_F6:
3810 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3811 break;
3812 case VK_F7:
3813 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3814 break;
3815 case VK_F8:
3816 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3817 break;
3818 case VK_F9:
3819 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3820 break;
3821 case VK_F10:
3822 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3823 break;
3824 case VK_F11:
3825 code = 23;
3826 break;
3827 case VK_F12:
3828 code = 24;
3829 break;
3830 case VK_F13:
3831 code = 25;
3832 break;
3833 case VK_F14:
3834 code = 26;
3835 break;
3836 case VK_F15:
3837 code = 28;
3838 break;
3839 case VK_F16:
3840 code = 29;
3841 break;
3842 case VK_F17:
3843 code = 31;
3844 break;
3845 case VK_F18:
3846 code = 32;
3847 break;
3848 case VK_F19:
3849 code = 33;
3850 break;
3851 case VK_F20:
3852 code = 34;
3853 break;
dfca2656 3854 }
3855 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3856 case VK_HOME:
3857 code = 1;
3858 break;
3859 case VK_INSERT:
3860 code = 2;
3861 break;
3862 case VK_DELETE:
3863 code = 3;
3864 break;
3865 case VK_END:
3866 code = 4;
3867 break;
3868 case VK_PRIOR:
3869 code = 5;
3870 break;
3871 case VK_NEXT:
3872 code = 6;
3873 break;
374330e2 3874 }
ec55b220 3875 /* Reorder edit keys to physical order */
5789bc94 3876 if (cfg.funky_type == FUNKY_VT400 && code <= 6)
32874aea 3877 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3878
887035a5 3879 if (term->vt52_mode && code > 0 && code <= 6) {
32874aea 3880 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3881 return p - output;
3882 }
3883
5789bc94 3884 if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */
9bc81a2c 3885 code >= 11 && code <= 34) {
e24b1972 3886 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3887 int index = 0;
3888 switch (wParam) {
3889 case VK_F1: index = 0; break;
3890 case VK_F2: index = 1; break;
3891 case VK_F3: index = 2; break;
3892 case VK_F4: index = 3; break;
3893 case VK_F5: index = 4; break;
3894 case VK_F6: index = 5; break;
3895 case VK_F7: index = 6; break;
3896 case VK_F8: index = 7; break;
3897 case VK_F9: index = 8; break;
3898 case VK_F10: index = 9; break;
3899 case VK_F11: index = 10; break;
3900 case VK_F12: index = 11; break;
3901 }
3902 if (keystate[VK_SHIFT] & 0x80) index += 12;
3903 if (keystate[VK_CONTROL] & 0x80) index += 24;
3904 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3905 return p - output;
3906 }
5789bc94 3907 if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */
9bc81a2c 3908 code >= 1 && code <= 6) {
3909 char codes[] = "HL.FIG";
3910 if (code == 3) {
3911 *p++ = '\x7F';
3912 } else {
3913 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3914 }
3915 return p - output;
3916 }
5789bc94 3917 if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && code >= 11 && code <= 24) {
f37caa11 3918 int offt = 0;
32874aea 3919 if (code > 15)
3920 offt++;
3921 if (code > 21)
3922 offt++;
887035a5 3923 if (term->vt52_mode)
32874aea 3924 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3925 else
32874aea 3926 p +=
3927 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3928 return p - output;
3929 }
5789bc94 3930 if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
32874aea 3931 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3932 return p - output;
3933 }
5789bc94 3934 if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
887035a5 3935 if (term->vt52_mode)
32874aea 3936 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3937 else
32874aea 3938 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3939 return p - output;
3940 }
3941 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3942 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3943 return p - output;
3944 }
3945 if (code) {
32874aea 3946 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3947 return p - output;
374330e2 3948 }
45dabbc5 3949
c9def1b8 3950 /*
3951 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3952 * some reason seems to send VK_CLEAR to Windows...).
3953 */
3954 {
3955 char xkey = 0;
3956 switch (wParam) {
32874aea 3957 case VK_UP:
3958 xkey = 'A';
3959 break;
3960 case VK_DOWN:
3961 xkey = 'B';
3962 break;
3963 case VK_RIGHT:
3964 xkey = 'C';
3965 break;
3966 case VK_LEFT:
3967 xkey = 'D';
3968 break;
3969 case VK_CLEAR:
3970 xkey = 'G';
3971 break;
c9def1b8 3972 }
32874aea 3973 if (xkey) {
887035a5 3974 if (term->vt52_mode)
32874aea 3975 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3976 else {
887035a5 3977 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3a953121 3978#if 0
3979 /*
3980 * RDB: VT100 & VT102 manuals both state the
3981 * app cursor keys only work if the app keypad
3982 * is on.
3983 *
3984 * SGT: That may well be true, but xterm
3985 * disagrees and so does at least one
3986 * application, so I've #if'ed this out and the
3987 * behaviour is back to PuTTY's original: app
3988 * cursor and app keypad are independently
3989 * switchable modes. If anyone complains about
3990 * _this_ I'll have to put in a configurable
3991 * option.
e864f84f 3992 */
887035a5 3993 if (!term->app_keypad_keys)
e864f84f 3994 app_flg = 0;
3a953121 3995#endif
e864f84f 3996 /* Useful mapping of Ctrl-arrows */
3997 if (shift_state == 2)
3998 app_flg = !app_flg;
3999
4000 if (app_flg)
4001 p += sprintf((char *) p, "\x1BO%c", xkey);
4002 else
4003 p += sprintf((char *) p, "\x1B[%c", xkey);
4004 }
c9def1b8 4005 return p - output;
4006 }
4007 }
0c50ef57 4008
4009 /*
4010 * Finally, deal with Return ourselves. (Win95 seems to
4011 * foul it up when Alt is pressed, for some reason.)
4012 */
32874aea 4013 if (wParam == VK_RETURN) { /* Return */
0c50ef57 4014 *p++ = 0x0D;
a5f3e637 4015 *p++ = 0;
4016 return -2;
0c50ef57 4017 }
4eeb7d09 4018
4019 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
4020 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
4021 else
4022 alt_sum = 0;
67c339f7 4023 }
374330e2 4024
c9def1b8 4025 /* Okay we've done everything interesting; let windows deal with
4026 * the boring stuff */
4027 {
a9c02454 4028 BOOL capsOn=0;
4029
4030 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
4031 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
4032 capsOn= !left_alt;
4033 keystate[VK_CAPITAL] = 0;
4034 }
4035
c264be08 4036 /* XXX how do we know what the max size of the keys array should
4037 * be is? There's indication on MS' website of an Inquire/InquireEx
4038 * functioning returning a KBINFO structure which tells us. */
4039 if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
4040 /* XXX 'keys' parameter is declared in MSDN documentation as
4041 * 'LPWORD lpChar'.
4042 * The experience of a French user indicates that on
4043 * Win98, WORD[] should be passed in, but on Win2K, it should
4044 * be BYTE[]. German WinXP and my Win2K with "US International"
4045 * driver corroborate this.
4046 * Experimentally I've conditionalised the behaviour on the
4047 * Win9x/NT split, but I suspect it's worse than that.
4048 * See wishlist item `win-dead-keys' for more horrible detail
4049 * and speculations. */
4050 BYTE keybs[3];
4051 int i;
4052 r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
4053 for (i=0; i<3; i++) keys[i] = keybs[i];
4054 } else {
4055 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4056 }
4eeb7d09 4057#ifdef SHOW_TOASCII_RESULT
4058 if (r == 1 && !key_down) {
4059 if (alt_sum) {
21d2b241 4060 if (in_utf(term) || ucsdata.dbcs_screenfont)
4eeb7d09 4061 debug((", (U+%04x)", alt_sum));
4062 else
4063 debug((", LCH(%d)", alt_sum));
4064 } else {
4065 debug((", ACH(%d)", keys[0]));
4066 }
4067 } else if (r > 0) {
4068 int r1;
4069 debug((", ASC("));
4070 for (r1 = 0; r1 < r; r1++) {
4071 debug(("%s%d", r1 ? "," : "", keys[r1]));
4072 }
4073 debug((")"));
4074 }
4075#endif
32874aea 4076 if (r > 0) {
4eeb7d09 4077 WCHAR keybuf;
256cb87c 4078
4079 /*
4080 * Interrupt an ongoing paste. I'm not sure this is
4081 * sensible, but for the moment it's preferable to
4082 * having to faff about buffering things.
4083 */
887035a5 4084 term_nopaste(term);
256cb87c 4085
c9def1b8 4086 p = output;
32874aea 4087 for (i = 0; i < r; i++) {
4088 unsigned char ch = (unsigned char) keys[i];
14963b8f 4089
32874aea 4090 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4091 compose_char = ch;
32874aea 4092 compose_state++;
c9def1b8 4093 continue;
4094 }
32874aea 4095 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 4096 int nc;
4097 compose_state = 0;
4098
32874aea 4099 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 4100 MessageBeep(MB_ICONHAND);
c9def1b8 4101 return 0;
4102 }
4eeb7d09 4103 keybuf = nc;
887035a5 4104 term_seen_key_event(term);
e3be8de5 4105 if (ldisc)
4106 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4107 continue;
c9def1b8 4108 }
374330e2 4109
c9def1b8 4110 compose_state = 0;
374330e2 4111
4eeb7d09 4112 if (!key_down) {
4113 if (alt_sum) {
21d2b241 4114 if (in_utf(term) || ucsdata.dbcs_screenfont) {
4eeb7d09 4115 keybuf = alt_sum;
887035a5 4116 term_seen_key_event(term);
e3be8de5 4117 if (ldisc)
4118 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 4119 } else {
4120 ch = (char) alt_sum;
5471d09a 4121 /*
4122 * We need not bother about stdin
4123 * backlogs here, because in GUI PuTTY
4124 * we can't do anything about it
4125 * anyway; there's no means of asking
4126 * Windows to hold off on KEYDOWN
4127 * messages. We _have_ to buffer
4128 * everything we're sent.
4129 */
887035a5 4130 term_seen_key_event(term);
e3be8de5 4131 if (ldisc)
4132 ldisc_send(ldisc, &ch, 1, 1);
4eeb7d09 4133 }
4134 alt_sum = 0;
405a0c29 4135 } else {
887035a5 4136 term_seen_key_event(term);
e3be8de5 4137 if (ldisc)
4138 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
405a0c29 4139 }
4eeb7d09 4140 } else {
a9c02454 4141 if(capsOn && ch < 0x80) {
4142 WCHAR cbuf[2];
4143 cbuf[0] = 27;
4144 cbuf[1] = xlat_uskbd2cyrllic(ch);
887035a5 4145 term_seen_key_event(term);
e3be8de5 4146 if (ldisc)
4147 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4148 } else {
4149 char cbuf[2];
4150 cbuf[0] = '\033';
4151 cbuf[1] = ch;
887035a5 4152 term_seen_key_event(term);
e3be8de5 4153 if (ldisc)
4154 lpage_send(ldisc, kbd_codepage,
4155 cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 4156 }
c9def1b8 4157 }
bca9517a 4158 show_mouseptr(0);
c9def1b8 4159 }
374330e2 4160
c9def1b8 4161 /* This is so the ALT-Numpad and dead keys work correctly. */
4162 keys[0] = 0;
4163
32874aea 4164 return p - output;
c9def1b8 4165 }
159eba53 4166 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 4167 if (!left_alt)
4168 keys[0] = 0;
4eeb7d09 4169 /* If we will be using alt_sum fix the 256s */
21d2b241 4170 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
4eeb7d09 4171 keys[0] = 10;
374330e2 4172 }
4173
dfca2656 4174 /*
4175 * ALT alone may or may not want to bring up the System menu.
4176 * If it's not meant to, we return 0 on presses or releases of
4177 * ALT, to show that we've swallowed the keystroke. Otherwise
4178 * we return -1, which means Windows will give the keystroke
4179 * its default handling (i.e. bring up the System menu).
4180 */
4181 if (wParam == VK_MENU && !cfg.alt_only)
4182 return 0;
374330e2 4183
c9def1b8 4184 return -1;
374330e2 4185}
4186
a8327734 4187void request_paste(void *frontend)
e6346999 4188{
4189 /*
4190 * In Windows, pasting is synchronous: we can read the
4191 * clipboard with no difficulty, so request_paste() can just go
4192 * ahead and paste.
4193 */
887035a5 4194 term_do_paste(term);
e6346999 4195}
4196
a8327734 4197void set_title(void *frontend, char *title)
32874aea 4198{
4199 sfree(window_name);
3d88e64d 4200 window_name = snewn(1 + strlen(title), char);
32874aea 4201 strcpy(window_name, title);
37508af4 4202 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 4203 SetWindowText(hwnd, title);
374330e2 4204}
4205
a8327734 4206void set_icon(void *frontend, char *title)
32874aea 4207{
4208 sfree(icon_name);
3d88e64d 4209 icon_name = snewn(1 + strlen(title), char);
32874aea 4210 strcpy(icon_name, title);
37508af4 4211 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 4212 SetWindowText(hwnd, title);
374330e2 4213}
4214
a8327734 4215void set_sbar(void *frontend, int total, int start, int page)
32874aea 4216{
374330e2 4217 SCROLLINFO si;
c9def1b8 4218
1ba99d2c 4219 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 4220 return;
c9def1b8 4221
374330e2 4222 si.cbSize = sizeof(si);
c9def1b8 4223 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 4224 si.nMin = 0;
4225 si.nMax = total - 1;
4226 si.nPage = page;
4227 si.nPos = start;
c1f5f956 4228 if (hwnd)
32874aea 4229 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 4230}
4231
a8327734 4232Context get_ctx(void *frontend)
32874aea 4233{
374330e2 4234 HDC hdc;
4235 if (hwnd) {
32874aea 4236 hdc = GetDC(hwnd);
374330e2 4237 if (hdc && pal)
32874aea 4238 SelectPalette(hdc, pal, FALSE);
374330e2 4239 return hdc;
4240 } else
4241 return NULL;
4242}
4243
32874aea 4244void free_ctx(Context ctx)
4245{
4246 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
4247 ReleaseDC(hwnd, ctx);
374330e2 4248}
4249
32874aea 4250static void real_palette_set(int n, int r, int g, int b)
4251{
374330e2 4252 if (pal) {
4253 logpal->palPalEntry[n].peRed = r;
4254 logpal->palPalEntry[n].peGreen = g;
4255 logpal->palPalEntry[n].peBlue = b;
4256 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
4257 colours[n] = PALETTERGB(r, g, b);
32874aea 4258 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 4259 } else
4260 colours[n] = RGB(r, g, b);
4261}
4262
a8327734 4263void palette_set(void *frontend, int n, int r, int g, int b)
32874aea 4264{
374330e2 4265 static const int first[21] = {
4266 0, 2, 4, 6, 8, 10, 12, 14,
4267 1, 3, 5, 7, 9, 11, 13, 15,
4268 16, 17, 18, 20, 22
4269 };
32874aea 4270 real_palette_set(first[n], r, g, b);
374330e2 4271 if (first[n] >= 18)
32874aea 4272 real_palette_set(first[n] + 1, r, g, b);
374330e2 4273 if (pal) {
a8327734 4274 HDC hdc = get_ctx(frontend);
32874aea 4275 UnrealizeObject(pal);
4276 RealizePalette(hdc);
4277 free_ctx(hdc);
374330e2 4278 }
4279}
4280
a8327734 4281void palette_reset(void *frontend)
32874aea 4282{
374330e2 4283 int i;
4284
4285 for (i = 0; i < NCOLOURS; i++) {
4286 if (pal) {
4287 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4288 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4289 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4290 logpal->palPalEntry[i].peFlags = 0;
4291 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4292 defpal[i].rgbtGreen,
4293 defpal[i].rgbtBlue);
4294 } else
4295 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 4296 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 4297 }
4298
4299 if (pal) {
4300 HDC hdc;
32874aea 4301 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
a8327734 4302 hdc = get_ctx(frontend);
32874aea 4303 RealizePalette(hdc);
4304 free_ctx(hdc);
374330e2 4305 }
4306}
4307
a8327734 4308void write_aclip(void *frontend, char *data, int len, int must_deselect)
32874aea 4309{
374330e2 4310 HGLOBAL clipdata;
4311 void *lock;
4312
32874aea 4313 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 4314 if (!clipdata)
4315 return;
32874aea 4316 lock = GlobalLock(clipdata);
374330e2 4317 if (!lock)
4318 return;
32874aea 4319 memcpy(lock, data, len);
4320 ((unsigned char *) lock)[len] = 0;
4321 GlobalUnlock(clipdata);
374330e2 4322
f0df44da 4323 if (!must_deselect)
32874aea 4324 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 4325
32874aea 4326 if (OpenClipboard(hwnd)) {
374330e2 4327 EmptyClipboard();
32874aea 4328 SetClipboardData(CF_TEXT, clipdata);
374330e2 4329 CloseClipboard();
4330 } else
32874aea 4331 GlobalFree(clipdata);
f0df44da 4332
4333 if (!must_deselect)
32874aea 4334 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 4335}
4336
4eeb7d09 4337/*
4338 * Note: unlike write_aclip() this will not append a nul.
4339 */
a8327734 4340void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4eeb7d09 4341{
a7419ea4 4342 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 4343 int len2;
a7419ea4 4344 void *lock, *lock2, *lock3;
4eeb7d09 4345
4346 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4347
4348 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4349 len * sizeof(wchar_t));
4350 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4351
0e5e7f46 4352 if (!clipdata || !clipdata2) {
4eeb7d09 4353 if (clipdata)
4354 GlobalFree(clipdata);
4355 if (clipdata2)
4356 GlobalFree(clipdata2);
4357 return;
4358 }
4359 if (!(lock = GlobalLock(clipdata)))
4360 return;
4361 if (!(lock2 = GlobalLock(clipdata2)))
4362 return;
4363
4364 memcpy(lock, data, len * sizeof(wchar_t));
4365 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4366
a7419ea4 4367 if (cfg.rtf_paste) {
4368 wchar_t unitab[256];
4369 char *rtf = NULL;
4370 unsigned char *tdata = (unsigned char *)lock2;
4371 wchar_t *udata = (wchar_t *)lock;
4372 int rtflen = 0, uindex = 0, tindex = 0;
4373 int rtfsize = 0;
4374 int multilen, blen, alen, totallen, i;
4375 char before[16], after[4];
4376
4377 get_unitab(CP_ACP, unitab, 0);
4378
9a30e26b 4379 rtfsize = 100 + strlen(cfg.font.name);
3d88e64d 4380 rtf = snewn(rtfsize, char);
a7419ea4 4381 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
9a30e26b 4382 GetACP(), cfg.font.name);
a7419ea4 4383 rtflen = strlen(rtf);
4384
4385 /*
4386 * We want to construct a piece of RTF that specifies the
4387 * same Unicode text. To do this we will read back in
4388 * parallel from the Unicode data in `udata' and the
4389 * non-Unicode data in `tdata'. For each character in
4390 * `tdata' which becomes the right thing in `udata' when
4391 * looked up in `unitab', we just copy straight over from
4392 * tdata. For each one that doesn't, we must WCToMB it
4393 * individually and produce a \u escape sequence.
4394 *
4395 * It would probably be more robust to just bite the bullet
4396 * and WCToMB each individual Unicode character one by one,
4397 * then MBToWC each one back to see if it was an accurate
4398 * translation; but that strikes me as a horrifying number
4399 * of Windows API calls so I want to see if this faster way
4400 * will work. If it screws up badly we can always revert to
4401 * the simple and slow way.
4402 */
4403 while (tindex < len2 && uindex < len &&
4404 tdata[tindex] && udata[uindex]) {
4405 if (tindex + 1 < len2 &&
4406 tdata[tindex] == '\r' &&
4407 tdata[tindex+1] == '\n') {
4408 tindex++;
4409 uindex++;
4410 }
4411 if (unitab[tdata[tindex]] == udata[uindex]) {
4412 multilen = 1;
4413 before[0] = '\0';
4414 after[0] = '\0';
4415 blen = alen = 0;
4416 } else {
4417 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4418 NULL, 0, NULL, NULL);
4419 if (multilen != 1) {
c3b032a6 4420 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4421 udata[uindex]);
a7419ea4 4422 alen = 1; strcpy(after, "}");
4423 } else {
4424 blen = sprintf(before, "\\u%d", udata[uindex]);
4425 alen = 0; after[0] = '\0';
4426 }
4427 }
4428 assert(tindex + multilen <= len2);
4429 totallen = blen + alen;
4430 for (i = 0; i < multilen; i++) {
4431 if (tdata[tindex+i] == '\\' ||
4432 tdata[tindex+i] == '{' ||
4433 tdata[tindex+i] == '}')
4434 totallen += 2;
4435 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4436 totallen += 6; /* \par\r\n */
4437 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4438 totallen += 4;
4439 else
4440 totallen++;
4441 }
4442
4443 if (rtfsize < rtflen + totallen + 3) {
4444 rtfsize = rtflen + totallen + 512;
3d88e64d 4445 rtf = sresize(rtf, rtfsize, char);
a7419ea4 4446 }
4447
4448 strcpy(rtf + rtflen, before); rtflen += blen;
4449 for (i = 0; i < multilen; i++) {
4450 if (tdata[tindex+i] == '\\' ||
4451 tdata[tindex+i] == '{' ||
4452 tdata[tindex+i] == '}') {
4453 rtf[rtflen++] = '\\';
4454 rtf[rtflen++] = tdata[tindex+i];
4455 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4456 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4457 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4458 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4459 } else {
4460 rtf[rtflen++] = tdata[tindex+i];
4461 }
4462 }
4463 strcpy(rtf + rtflen, after); rtflen += alen;
4464
4465 tindex += multilen;
4466 uindex++;
4467 }
4468
4469 strcpy(rtf + rtflen, "}");
4470 rtflen += 2;
4471
4472 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4473 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4474 strcpy(lock3, rtf);
4475 GlobalUnlock(clipdata3);
4476 }
4477 sfree(rtf);
4478 } else
4479 clipdata3 = NULL;
4480
4eeb7d09 4481 GlobalUnlock(clipdata);
4482 GlobalUnlock(clipdata2);
4483
4484 if (!must_deselect)
4485 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4486
4487 if (OpenClipboard(hwnd)) {
4488 EmptyClipboard();
4489 SetClipboardData(CF_UNICODETEXT, clipdata);
4490 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4491 if (clipdata3)
4492 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4493 CloseClipboard();
4494 } else {
4495 GlobalFree(clipdata);
4496 GlobalFree(clipdata2);
4497 }
4498
4499 if (!must_deselect)
4500 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4501}
4502
a8327734 4503void get_clip(void *frontend, wchar_t ** p, int *len)
32874aea 4504{
374330e2 4505 static HGLOBAL clipdata = NULL;
4eeb7d09 4506 static wchar_t *converted = 0;
4507 wchar_t *p2;
374330e2 4508
4eeb7d09 4509 if (converted) {
4510 sfree(converted);
4511 converted = 0;
4512 }
374330e2 4513 if (!p) {
4514 if (clipdata)
32874aea 4515 GlobalUnlock(clipdata);
374330e2 4516 clipdata = NULL;
4517 return;
4eeb7d09 4518 } else if (OpenClipboard(NULL)) {
2d466ffd 4519 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4520 CloseClipboard();
4eeb7d09 4521 *p = GlobalLock(clipdata);
4522 if (*p) {
4523 for (p2 = *p; *p2; p2++);
4524 *len = p2 - *p;
4525 return;
374330e2 4526 }
2d466ffd 4527 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4528 char *s;
4529 int i;
4530 CloseClipboard();
4531 s = GlobalLock(clipdata);
4532 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3d88e64d 4533 *p = converted = snewn(i, wchar_t);
4eeb7d09 4534 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4535 *len = i - 1;
4536 return;
4537 } else
4538 CloseClipboard();
374330e2 4539 }
4540
4541 *p = NULL;
4542 *len = 0;
4543}
4544
4eeb7d09 4545#if 0
374330e2 4546/*
4547 * Move `lines' lines from position `from' to position `to' in the
4548 * window.
4549 */
a8327734 4550void optimised_move(void *frontend, int to, int from, int lines)
32874aea 4551{
374330e2 4552 RECT r;
f67b4e85 4553 int min, max;
374330e2 4554
4555 min = (to < from ? to : from);
4556 max = to + from - min;
374330e2 4557
5a73255e 4558 r.left = offset_width;
887035a5 4559 r.right = offset_width + term->cols * font_width;
5a73255e 4560 r.top = offset_height + min * font_height;
4561 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4562 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4563}
4eeb7d09 4564#endif
374330e2 4565
4566/*
4567 * Print a message box and perform a fatal exit.
4568 */
32874aea 4569void fatalbox(char *fmt, ...)
4570{
374330e2 4571 va_list ap;
971bcc0a 4572 char *stuff, morestuff[100];
374330e2 4573
4574 va_start(ap, fmt);
971bcc0a 4575 stuff = dupvprintf(fmt, ap);
374330e2 4576 va_end(ap);
f6f450e2 4577 sprintf(morestuff, "%.70s Fatal Error", appname);
4578 MessageBox(hwnd, stuff, morestuff, MB_ICONERROR | MB_OK);
971bcc0a 4579 sfree(stuff);
93b581bd 4580 cleanup_exit(1);
374330e2 4581}
4582
4583/*
1709795f 4584 * Print a modal (Really Bad) message box and perform a fatal exit.
4585 */
4586void modalfatalbox(char *fmt, ...)
4587{
4588 va_list ap;
971bcc0a 4589 char *stuff, morestuff[100];
1709795f 4590
4591 va_start(ap, fmt);
971bcc0a 4592 stuff = dupvprintf(fmt, ap);
1709795f 4593 va_end(ap);
f6f450e2 4594 sprintf(morestuff, "%.70s Fatal Error", appname);
4595 MessageBox(hwnd, stuff, morestuff,
1709795f 4596 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
971bcc0a 4597 sfree(stuff);
1709795f 4598 cleanup_exit(1);
4599}
4600
4601/*
f8a28d1f 4602 * Manage window caption / taskbar flashing, if enabled.
4603 * 0 = stop, 1 = maintain, 2 = start
4604 */
4605static void flash_window(int mode)
4606{
4607 static long last_flash = 0;
4608 static int flashing = 0;
4609 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4610 /* stop */
4611 if (flashing) {
4612 FlashWindow(hwnd, FALSE);
4613 flashing = 0;
4614 }
4615
4616 } else if (mode == 2) {
4617 /* start */
4618 if (!flashing) {
4619 last_flash = GetTickCount();
4620 flashing = 1;
4621 FlashWindow(hwnd, TRUE);
4622 }
4623
4624 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4625 /* maintain */
4626 if (flashing) {
4627 long now = GetTickCount();
4628 long fdiff = now - last_flash;
4629 if (fdiff < 0 || fdiff > 450) {
4630 last_flash = now;
4631 FlashWindow(hwnd, TRUE); /* toggle */
4632 }
4633 }
4634 }
4635}
4636
4637/*
374330e2 4638 * Beep.
4639 */
a8327734 4640void beep(void *frontend, int mode)
32874aea 4641{
03169ad0 4642 if (mode == BELL_DEFAULT) {
eb04402e 4643 /*
4644 * For MessageBeep style bells, we want to be careful of
4645 * timing, because they don't have the nice property of
4646 * PlaySound bells that each one cancels the previous
4647 * active one. So we limit the rate to one per 50ms or so.
4648 */
4649 static long lastbeep = 0;
d51cdf1e 4650 long beepdiff;
eb04402e 4651
d51cdf1e 4652 beepdiff = GetTickCount() - lastbeep;
eb04402e 4653 if (beepdiff >= 0 && beepdiff < 50)
4654 return;
156686ef 4655 MessageBeep(MB_OK);
d51cdf1e 4656 /*
4657 * The above MessageBeep call takes time, so we record the
4658 * time _after_ it finishes rather than before it starts.
4659 */
4660 lastbeep = GetTickCount();
03169ad0 4661 } else if (mode == BELL_WAVEFILE) {
9a30e26b 4662 if (!PlaySound(cfg.bell_wavefile.path, NULL,
4663 SND_ASYNC | SND_FILENAME)) {
850323f9 4664 char buf[sizeof(cfg.bell_wavefile.path) + 80];
f6f450e2 4665 char otherbuf[100];
03169ad0 4666 sprintf(buf, "Unable to play sound file\n%s\n"
debe102c 4667 "Using default sound instead", cfg.bell_wavefile.path);
f6f450e2 4668 sprintf(otherbuf, "%.70s Sound Error", appname);
4669 MessageBox(hwnd, buf, otherbuf,
32874aea 4670 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4671 cfg.beep = BELL_DEFAULT;
4672 }
85f6b361 4673 } else if (mode == BELL_PCSPEAKER) {
4674 static long lastbeep = 0;
4675 long beepdiff;
4676
4677 beepdiff = GetTickCount() - lastbeep;
4678 if (beepdiff >= 0 && beepdiff < 50)
4679 return;
4680
4681 /*
4682 * We must beep in different ways depending on whether this
4683 * is a 95-series or NT-series OS.
4684 */
4685 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
4686 Beep(800, 100);
4687 else
4688 MessageBeep(-1);
4689 lastbeep = GetTickCount();
03169ad0 4690 }
f8a28d1f 4691 /* Otherwise, either visual bell or disabled; do nothing here */
887035a5 4692 if (!term->has_focus) {
f8a28d1f 4693 flash_window(2); /* start */
4694 }
374330e2 4695}
8f57d753 4696
4697/*
68f9b3d9 4698 * Minimise or restore the window in response to a server-side
4699 * request.
4700 */
a8327734 4701void set_iconic(void *frontend, int iconic)
68f9b3d9 4702{
4703 if (IsIconic(hwnd)) {
4704 if (!iconic)
4705 ShowWindow(hwnd, SW_RESTORE);
4706 } else {
4707 if (iconic)
4708 ShowWindow(hwnd, SW_MINIMIZE);
4709 }
4710}
4711
4712/*
4713 * Move the window in response to a server-side request.
4714 */
a8327734 4715void move_window(void *frontend, int x, int y)
68f9b3d9 4716{
d1e0a352 4717 if (cfg.resize_action == RESIZE_DISABLED ||
4718 cfg.resize_action == RESIZE_FONT ||
4719 IsZoomed(hwnd))
4720 return;
4721
68f9b3d9 4722 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4723}
4724
4725/*
4726 * Move the window to the top or bottom of the z-order in response
4727 * to a server-side request.
4728 */
a8327734 4729void set_zorder(void *frontend, int top)
68f9b3d9 4730{
1ba99d2c 4731 if (cfg.alwaysontop)
68f9b3d9 4732 return; /* ignore */
4733 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4734 SWP_NOMOVE | SWP_NOSIZE);
4735}
4736
4737/*
4738 * Refresh the window in response to a server-side request.
4739 */
a8327734 4740void refresh_window(void *frontend)
68f9b3d9 4741{
4742 InvalidateRect(hwnd, NULL, TRUE);
4743}
4744
4745/*
4746 * Maximise or restore the window in response to a server-side
4747 * request.
4748 */
a8327734 4749void set_zoomed(void *frontend, int zoomed)
68f9b3d9 4750{
1ba99d2c 4751 if (IsZoomed(hwnd)) {
4752 if (!zoomed)
4753 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4754 } else {
4755 if (zoomed)
4756 ShowWindow(hwnd, SW_MAXIMIZE);
4757 }
4758}
4759
4760/*
4761 * Report whether the window is iconic, for terminal reports.
4762 */
a8327734 4763int is_iconic(void *frontend)
68f9b3d9 4764{
4765 return IsIconic(hwnd);
4766}
4767
4768/*
4769 * Report the window's position, for terminal reports.
4770 */
a8327734 4771void get_window_pos(void *frontend, int *x, int *y)
68f9b3d9 4772{
4773 RECT r;
4774 GetWindowRect(hwnd, &r);
4775 *x = r.left;
4776 *y = r.top;
4777}
4778
4779/*
4780 * Report the window's pixel size, for terminal reports.
4781 */
a8327734 4782void get_window_pixels(void *frontend, int *x, int *y)
68f9b3d9 4783{
4784 RECT r;
4785 GetWindowRect(hwnd, &r);
4786 *x = r.right - r.left;
4787 *y = r.bottom - r.top;
4788}
4789
4790/*
4791 * Return the window or icon title.
4792 */
a8327734 4793char *get_window_title(void *frontend, int icon)
68f9b3d9 4794{
4795 return icon ? icon_name : window_name;
4796}
1ba99d2c 4797
4798/*
4799 * See if we're in full-screen mode.
4800 */
4e95095a 4801static int is_full_screen()
1ba99d2c 4802{
4803 if (!IsZoomed(hwnd))
4804 return FALSE;
4805 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4806 return FALSE;
4807 return TRUE;
4808}
4809
d0f1bcb4 4810/* Get the rect/size of a full screen window using the nearest available
4811 * monitor in multimon systems; default to something sensible if only
4812 * one monitor is present. */
4813static int get_fullscreen_rect(RECT * ss)
4814{
5d145a14 4815#if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON)
d0f1bcb4 4816 HMONITOR mon;
4817 MONITORINFO mi;
4818 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4819 mi.cbSize = sizeof(mi);
4820 GetMonitorInfo(mon, &mi);
4821
4822 /* structure copy */
4823 *ss = mi.rcMonitor;
4824 return TRUE;
4825#else
4826/* could also use code like this:
4827 ss->left = ss->top = 0;
4828 ss->right = GetSystemMetrics(SM_CXSCREEN);
4829 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4830*/
e6f8202f 4831 return GetClientRect(GetDesktopWindow(), ss);
d0f1bcb4 4832#endif
4833}
4834
4835
1ba99d2c 4836/*
4837 * Go full-screen. This should only be called when we are already
4838 * maximised.
4839 */
4e95095a 4840static void make_full_screen()
1ba99d2c 4841{
4842 DWORD style;
d0f1bcb4 4843 RECT ss;
1ba99d2c 4844
4845 assert(IsZoomed(hwnd));
4846
d0f1bcb4 4847 if (is_full_screen())
4848 return;
4849
1ba99d2c 4850 /* Remove the window furniture. */
4851 style = GetWindowLong(hwnd, GWL_STYLE);
4852 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4853 if (cfg.scrollbar_in_fullscreen)
4854 style |= WS_VSCROLL;
4855 else
4856 style &= ~WS_VSCROLL;
4857 SetWindowLong(hwnd, GWL_STYLE, style);
4858
4859 /* Resize ourselves to exactly cover the nearest monitor. */
d0f1bcb4 4860 get_fullscreen_rect(&ss);
4861 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4862 ss.right - ss.left,
4863 ss.bottom - ss.top,
4864 SWP_FRAMECHANGED);
1ba99d2c 4865
4866 /* Tick the menu item in the System menu. */
4867 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4868 MF_CHECKED);
4869}
4870
4871/*
4872 * Clear the full-screen attributes.
4873 */
4e95095a 4874static void clear_full_screen()
1ba99d2c 4875{
4876 DWORD oldstyle, style;
4877
4878 /* Reinstate the window furniture. */
4879 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4880 style |= WS_CAPTION | WS_BORDER;
4881 if (cfg.resize_action == RESIZE_DISABLED)
4882 style &= ~WS_THICKFRAME;
4883 else
4884 style |= WS_THICKFRAME;
1ba99d2c 4885 if (cfg.scrollbar)
4886 style |= WS_VSCROLL;
4887 else
4888 style &= ~WS_VSCROLL;
4889 if (style != oldstyle) {
4890 SetWindowLong(hwnd, GWL_STYLE, style);
4891 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4892 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4893 SWP_FRAMECHANGED);
4894 }
4895
4896 /* Untick the menu item in the System menu. */
4897 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4898 MF_UNCHECKED);
4899}
4900
4901/*
4902 * Toggle full-screen mode.
4903 */
4e95095a 4904static void flip_full_screen()
1ba99d2c 4905{
4906 if (is_full_screen()) {
4907 ShowWindow(hwnd, SW_RESTORE);
4908 } else if (IsZoomed(hwnd)) {
4909 make_full_screen();
4910 } else {
4911 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4912 ShowWindow(hwnd, SW_MAXIMIZE);
4913 }
4914}
ab2c86b9 4915
b9d7bcad 4916void frontend_keypress(void *handle)
ab2c86b9 4917{
4918 /*
4919 * Keypress termination in non-Close-On-Exit mode is not
4920 * currently supported in PuTTY proper, because the window
4921 * always has a perfectly good Close button anyway. So we do
4922 * nothing here.
4923 */
4924 return;
4925}
fbf6cb3b 4926
4927int from_backend(void *frontend, int is_stderr, const char *data, int len)
4928{
4929 return term_data(term, is_stderr, data, len);
4930}
c44bf5bd 4931
4932void agent_schedule_callback(void (*callback)(void *, void *, int),
4933 void *callback_ctx, void *data, int len)
4934{
4935 struct agent_callback *c = snew(struct agent_callback);
4936 c->callback = callback;
4937 c->callback_ctx = callback_ctx;
4938 c->data = data;
4939 c->len = len;
4940 PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c);
4941}