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