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