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