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