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