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