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