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