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