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