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