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