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