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