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