Call ot_poll as appropriate.
[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
51470298 618 error = back->init((void *)term, &backhandle,
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
5a73255e 1785 /* Screen size changed ? */
1786 if (cfg.height != prev_cfg.height ||
1787 cfg.width != prev_cfg.width ||
1788 cfg.savelines != prev_cfg.savelines ||
0ed2f48e 1789 cfg.resize_action == RESIZE_FONT ||
811f10b3 1790 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
0ed2f48e 1791 cfg.resize_action == RESIZE_DISABLED)
887035a5 1792 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 1793
32874aea 1794 /* Enable or disable the scroll bar, etc */
1795 {
1796 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1797 LONG nexflag, exflag =
1798 GetWindowLong(hwnd, GWL_EXSTYLE);
1799
1800 nexflag = exflag;
5a73255e 1801 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 1802 if (cfg.alwaysontop) {
1803 nexflag |= WS_EX_TOPMOST;
1804 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1805 SWP_NOMOVE | SWP_NOSIZE);
1806 } else {
1807 nexflag &= ~(WS_EX_TOPMOST);
1808 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1809 SWP_NOMOVE | SWP_NOSIZE);
1810 }
1811 }
1812 if (cfg.sunken_edge)
1813 nexflag |= WS_EX_CLIENTEDGE;
1814 else
1815 nexflag &= ~(WS_EX_CLIENTEDGE);
1816
1817 nflg = flag;
1ba99d2c 1818 if (is_full_screen() ?
2cae8529 1819 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
32874aea 1820 nflg |= WS_VSCROLL;
1821 else
1822 nflg &= ~WS_VSCROLL;
811f10b3 1823
1824 if (cfg.resize_action == RESIZE_DISABLED ||
1825 is_full_screen())
1826 nflg &= ~WS_THICKFRAME;
1827 else
1828 nflg |= WS_THICKFRAME;
1829
ad5c93cc 1830 if (cfg.resize_action == RESIZE_DISABLED)
811f10b3 1831 nflg &= ~WS_MAXIMIZEBOX;
32874aea 1832 else
811f10b3 1833 nflg |= WS_MAXIMIZEBOX;
32874aea 1834
1835 if (nflg != flag || nexflag != exflag) {
32874aea 1836 if (nflg != flag)
1837 SetWindowLong(hwnd, GWL_STYLE, nflg);
1838 if (nexflag != exflag)
1839 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1840
32874aea 1841 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1842 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 1843 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1844 SWP_FRAMECHANGED);
c9def1b8 1845
5a73255e 1846 init_lvl = 2;
e44d78b6 1847 }
32874aea 1848 }
5a73255e 1849
e44d78b6 1850 /* Oops */
ad5c93cc 1851 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 1852 force_normal(hwnd);
5a73255e 1853 init_lvl = 2;
1854 }
1855
a8327734 1856 set_title(NULL, cfg.wintitle);
32874aea 1857 if (IsIconic(hwnd)) {
1858 SetWindowText(hwnd,
1859 cfg.win_name_always ? window_name :
1860 icon_name);
3da0b1d2 1861 }
5a73255e 1862
1863 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1864 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1865 cfg.fontisbold != prev_cfg.fontisbold ||
1866 cfg.fontheight != prev_cfg.fontheight ||
1867 cfg.fontcharset != prev_cfg.fontcharset ||
1868 cfg.vtmode != prev_cfg.vtmode ||
1869 cfg.bold_colour != prev_cfg.bold_colour ||
0ed2f48e 1870 cfg.resize_action == RESIZE_DISABLED ||
1871 cfg.resize_action == RESIZE_EITHER ||
1872 (cfg.resize_action != prev_cfg.resize_action))
5a73255e 1873 init_lvl = 2;
1874
1875 InvalidateRect(hwnd, NULL, TRUE);
1876 reset_window(init_lvl);
7732d38a 1877 net_pending_errors();
32874aea 1878 }
1879 break;
bc1235d4 1880 case IDM_COPYALL:
887035a5 1881 term_copyall(term);
bc1235d4 1882 break;
32874aea 1883 case IDM_CLRSB:
887035a5 1884 term_clrsb(term);
32874aea 1885 break;
1886 case IDM_RESET:
887035a5 1887 term_pwron(term);
b9d7bcad 1888 ldisc_send(ldisc, NULL, 0, 0);
32874aea 1889 break;
1890 case IDM_TEL_AYT:
51470298 1891 back->special(backhandle, TS_AYT);
7732d38a 1892 net_pending_errors();
32874aea 1893 break;
1894 case IDM_TEL_BRK:
51470298 1895 back->special(backhandle, TS_BRK);
7732d38a 1896 net_pending_errors();
32874aea 1897 break;
1898 case IDM_TEL_SYNCH:
51470298 1899 back->special(backhandle, TS_SYNCH);
7732d38a 1900 net_pending_errors();
32874aea 1901 break;
1902 case IDM_TEL_EC:
51470298 1903 back->special(backhandle, TS_EC);
7732d38a 1904 net_pending_errors();
32874aea 1905 break;
1906 case IDM_TEL_EL:
51470298 1907 back->special(backhandle, TS_EL);
7732d38a 1908 net_pending_errors();
32874aea 1909 break;
1910 case IDM_TEL_GA:
51470298 1911 back->special(backhandle, TS_GA);
7732d38a 1912 net_pending_errors();
32874aea 1913 break;
1914 case IDM_TEL_NOP:
51470298 1915 back->special(backhandle, TS_NOP);
7732d38a 1916 net_pending_errors();
32874aea 1917 break;
1918 case IDM_TEL_ABORT:
51470298 1919 back->special(backhandle, TS_ABORT);
7732d38a 1920 net_pending_errors();
32874aea 1921 break;
1922 case IDM_TEL_AO:
51470298 1923 back->special(backhandle, TS_AO);
7732d38a 1924 net_pending_errors();
32874aea 1925 break;
1926 case IDM_TEL_IP:
51470298 1927 back->special(backhandle, TS_IP);
7732d38a 1928 net_pending_errors();
32874aea 1929 break;
1930 case IDM_TEL_SUSP:
51470298 1931 back->special(backhandle, TS_SUSP);
7732d38a 1932 net_pending_errors();
32874aea 1933 break;
1934 case IDM_TEL_EOR:
51470298 1935 back->special(backhandle, TS_EOR);
7732d38a 1936 net_pending_errors();
32874aea 1937 break;
1938 case IDM_TEL_EOF:
51470298 1939 back->special(backhandle, TS_EOF);
7732d38a 1940 net_pending_errors();
32874aea 1941 break;
374330e2 1942 case IDM_ABOUT:
32874aea 1943 showabout(hwnd);
374330e2 1944 break;
70133c0e 1945 case IDM_HELP:
1946 WinHelp(hwnd, help_path,
1947 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1948 break;
0d6dcf38 1949 case SC_MOUSEMENU:
1950 /*
1951 * We get this if the System menu has been activated
1952 * using the mouse.
1953 */
1954 show_mouseptr(1);
1955 break;
dfca2656 1956 case SC_KEYMENU:
1957 /*
0d6dcf38 1958 * We get this if the System menu has been activated
1959 * using the keyboard. This might happen from within
1960 * TranslateKey, in which case it really wants to be
1961 * followed by a `space' character to actually _bring
1962 * the menu up_ rather than just sitting there in
1963 * `ready to appear' state.
dfca2656 1964 */
0d6dcf38 1965 show_mouseptr(1); /* make sure pointer is visible */
dfca2656 1966 if( lParam == 0 )
1967 PostMessage(hwnd, WM_CHAR, ' ', 0);
1968 break;
a401e5f3 1969 case IDM_FULLSCREEN:
1ba99d2c 1970 flip_full_screen();
1971 break;
32874aea 1972 default:
1973 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1974 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1975 }
374330e2 1976 }
1977 break;
37508af4 1978
1979#define X_POS(l) ((int)(short)LOWORD(l))
1980#define Y_POS(l) ((int)(short)HIWORD(l))
1981
5a73255e 1982#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1983#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
374330e2 1984 case WM_LBUTTONDOWN:
374330e2 1985 case WM_MBUTTONDOWN:
374330e2 1986 case WM_RBUTTONDOWN:
01c034ad 1987 case WM_LBUTTONUP:
1988 case WM_MBUTTONUP:
374330e2 1989 case WM_RBUTTONUP:
01c034ad 1990 {
1991 int button, press;
6908fed7 1992
01c034ad 1993 switch (message) {
32874aea 1994 case WM_LBUTTONDOWN:
1995 button = MBT_LEFT;
1996 press = 1;
1997 break;
1998 case WM_MBUTTONDOWN:
1999 button = MBT_MIDDLE;
2000 press = 1;
2001 break;
2002 case WM_RBUTTONDOWN:
2003 button = MBT_RIGHT;
2004 press = 1;
2005 break;
2006 case WM_LBUTTONUP:
2007 button = MBT_LEFT;
2008 press = 0;
2009 break;
2010 case WM_MBUTTONUP:
2011 button = MBT_MIDDLE;
2012 press = 0;
2013 break;
2014 case WM_RBUTTONUP:
2015 button = MBT_RIGHT;
2016 press = 0;
2017 break;
2d466ffd 2018 default:
2019 button = press = 0; /* shouldn't happen */
01c034ad 2020 }
2021 show_mouseptr(1);
2cae8529 2022 /*
2023 * Special case: in full-screen mode, if the left
2024 * button is clicked in the very top left corner of the
2025 * window, we put up the System menu instead of doing
2026 * selection.
2027 */
1ba99d2c 2028 if (is_full_screen() && press && button == MBT_LEFT &&
2cae8529 2029 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2030 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2031 return 0;
2032 }
01c034ad 2033 if (press) {
32874aea 2034 click(button,
2035 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
6908fed7 2036 wParam & MK_SHIFT, wParam & MK_CONTROL,
2037 is_alt_pressed());
01c034ad 2038 SetCapture(hwnd);
2039 } else {
887035a5 2040 term_mouse(term, button, MA_RELEASE,
32874aea 2041 TO_CHR_X(X_POS(lParam)),
2042 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 2043 wParam & MK_CONTROL, is_alt_pressed());
01c034ad 2044 ReleaseCapture();
2045 }
2046 }
374330e2 2047 return 0;
2048 case WM_MOUSEMOVE:
32874aea 2049 show_mouseptr(1);
374330e2 2050 /*
2051 * Add the mouse position and message time to the random
7d6ee6ff 2052 * number noise.
374330e2 2053 */
32874aea 2054 noise_ultralight(lParam);
374330e2 2055
319bfe5a 2056 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2057 GetCapture() == hwnd) {
374330e2 2058 Mouse_Button b;
2059 if (wParam & MK_LBUTTON)
6c6e7711 2060 b = MBT_LEFT;
374330e2 2061 else if (wParam & MK_MBUTTON)
6c6e7711 2062 b = MBT_MIDDLE;
374330e2 2063 else
6c6e7711 2064 b = MBT_RIGHT;
887035a5 2065 term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
32874aea 2066 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
6908fed7 2067 wParam & MK_CONTROL, is_alt_pressed());
374330e2 2068 }
374330e2 2069 return 0;
d318ef8e 2070 case WM_NCMOUSEMOVE:
2071 show_mouseptr(1);
32874aea 2072 noise_ultralight(lParam);
d318ef8e 2073 return 0;
374330e2 2074 case WM_IGNORE_CLIP:
2075 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2076 break;
2077 case WM_DESTROYCLIPBOARD:
2078 if (!ignore_clip)
887035a5 2079 term_deselect(term);
374330e2 2080 ignore_clip = FALSE;
2081 return 0;
2082 case WM_PAINT:
2083 {
2084 PAINTSTRUCT p;
32874aea 2085 HideCaret(hwnd);
2086 hdc = BeginPaint(hwnd, &p);
374330e2 2087 if (pal) {
32874aea 2088 SelectPalette(hdc, pal, TRUE);
2089 RealizePalette(hdc);
374330e2 2090 }
887035a5 2091 term_paint(term, hdc,
5a73255e 2092 (p.rcPaint.left-offset_width)/font_width,
2093 (p.rcPaint.top-offset_height)/font_height,
2094 (p.rcPaint.right-offset_width-1)/font_width,
c1b55581 2095 (p.rcPaint.bottom-offset_height-1)/font_height,
2096 is_alt_pressed());
5a73255e 2097
2098 if (p.fErase ||
2099 p.rcPaint.left < offset_width ||
2100 p.rcPaint.top < offset_height ||
887035a5 2101 p.rcPaint.right >= offset_width + font_width*term->cols ||
2102 p.rcPaint.bottom>= offset_height + font_height*term->rows)
5a73255e 2103 {
2104 HBRUSH fillcolour, oldbrush;
2105 HPEN edge, oldpen;
2106 fillcolour = CreateSolidBrush (
2107 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2108 oldbrush = SelectObject(hdc, fillcolour);
2109 edge = CreatePen(PS_SOLID, 0,
2110 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2111 oldpen = SelectObject(hdc, edge);
2112
bfa7a5db 2113 /*
2114 * Jordan Russell reports that this apparently
2115 * ineffectual IntersectClipRect() call masks a
2116 * Windows NT/2K bug causing strange display
2117 * problems when the PuTTY window is taller than
2118 * the primary monitor. It seems harmless enough...
2119 */
2120 IntersectClipRect(hdc,
2121 p.rcPaint.left, p.rcPaint.top,
2122 p.rcPaint.right, p.rcPaint.bottom);
2123
5a73255e 2124 ExcludeClipRect(hdc,
2125 offset_width, offset_height,
887035a5 2126 offset_width+font_width*term->cols,
2127 offset_height+font_height*term->rows);
5a73255e 2128
2129 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2130 p.rcPaint.right, p.rcPaint.bottom);
2131
2132 // SelectClipRgn(hdc, NULL);
2133
2134 SelectObject(hdc, oldbrush);
2135 DeleteObject(fillcolour);
2136 SelectObject(hdc, oldpen);
2137 DeleteObject(edge);
2138 }
32874aea 2139 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2140 SelectObject(hdc, GetStockObject(WHITE_PEN));
2141 EndPaint(hwnd, &p);
2142 ShowCaret(hwnd);
374330e2 2143 }
2144 return 0;
2145 case WM_NETEVENT:
59ad2c03 2146 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2147 * but the only one that's likely to try to overload us is FD_READ.
2148 * This means buffering just one is fine.
2149 */
2150 if (pending_netevent)
2151 enact_pending_netevent();
2152
2153 pending_netevent = TRUE;
32874aea 2154 pend_netevent_wParam = wParam;
2155 pend_netevent_lParam = lParam;
3ad9d396 2156 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2157 enact_pending_netevent();
2158
ec55b220 2159 time(&last_movement);
374330e2 2160 return 0;
2161 case WM_SETFOCUS:
887035a5 2162 term->has_focus = TRUE;
32874aea 2163 CreateCaret(hwnd, caretbm, font_width, font_height);
2164 ShowCaret(hwnd);
f8a28d1f 2165 flash_window(0); /* stop */
32874aea 2166 compose_state = 0;
887035a5 2167 term_out(term);
2168 term_update(term);
374330e2 2169 break;
2170 case WM_KILLFOCUS:
32874aea 2171 show_mouseptr(1);
887035a5 2172 term->has_focus = FALSE;
32874aea 2173 DestroyCaret();
c1da5066 2174 caret_x = caret_y = -1; /* ensure caret is replaced next time */
887035a5 2175 term_out(term);
2176 term_update(term);
374330e2 2177 break;
73251d5d 2178 case WM_ENTERSIZEMOVE:
5a73255e 2179#ifdef RDB_DEBUG_PATCH
2180 debug((27, "WM_ENTERSIZEMOVE"));
2181#endif
32874aea 2182 EnableSizeTip(1);
2183 resizing = TRUE;
3ad8c6db 2184 need_backend_resize = FALSE;
32874aea 2185 break;
73251d5d 2186 case WM_EXITSIZEMOVE:
32874aea 2187 EnableSizeTip(0);
2188 resizing = FALSE;
5a73255e 2189#ifdef RDB_DEBUG_PATCH
2190 debug((27, "WM_EXITSIZEMOVE"));
2191#endif
2192 if (need_backend_resize) {
887035a5 2193 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 2194 InvalidateRect(hwnd, NULL, TRUE);
2195 }
32874aea 2196 break;
374330e2 2197 case WM_SIZING:
5a73255e 2198 /*
2199 * This does two jobs:
2200 * 1) Keep the sizetip uptodate
2201 * 2) Make sure the window size is _stepped_ in units of the font size.
2202 */
c1b55581 2203 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
374330e2 2204 int width, height, w, h, ew, eh;
32874aea 2205 LPRECT r = (LPRECT) lParam;
374330e2 2206
0ed2f48e 2207 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
887035a5 2208 (cfg.height != term->rows || cfg.width != term->cols )) {
5a73255e 2209 /*
0ed2f48e 2210 * Great! It seems that both the terminal size and the
2211 * font size have been changed and the user is now dragging.
2212 *
2213 * It will now be difficult to get back to the configured
2214 * font size!
2215 *
2216 * This would be easier but it seems to be too confusing.
2217
887035a5 2218 term_size(term, cfg.height, cfg.width, cfg.savelines);
5a73255e 2219 reset_window(2);
0ed2f48e 2220 */
887035a5 2221 cfg.height=term->rows; cfg.width=term->cols;
0ed2f48e 2222
5a73255e 2223 InvalidateRect(hwnd, NULL, TRUE);
2224 need_backend_resize = TRUE;
2225 }
2226
374330e2 2227 width = r->right - r->left - extra_width;
2228 height = r->bottom - r->top - extra_height;
32874aea 2229 w = (width + font_width / 2) / font_width;
2230 if (w < 1)
2231 w = 1;
2232 h = (height + font_height / 2) / font_height;
2233 if (h < 1)
2234 h = 1;
2235 UpdateSizeTip(hwnd, w, h);
374330e2 2236 ew = width - w * font_width;
2237 eh = height - h * font_height;
2238 if (ew != 0) {
2239 if (wParam == WMSZ_LEFT ||
32874aea 2240 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 2241 r->left += ew;
2242 else
2243 r->right -= ew;
2244 }
2245 if (eh != 0) {
2246 if (wParam == WMSZ_TOP ||
32874aea 2247 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 2248 r->top += eh;
2249 else
2250 r->bottom -= eh;
2251 }
2252 if (ew || eh)
2253 return 1;
2254 else
2255 return 0;
5a73255e 2256 } else {
2257 int width, height, w, h, rv = 0;
2258 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2259 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2260 LPRECT r = (LPRECT) lParam;
2261
2262 width = r->right - r->left - ex_width;
2263 height = r->bottom - r->top - ex_height;
2264
887035a5 2265 w = (width + term->cols/2)/term->cols;
2266 h = (height + term->rows/2)/term->rows;
2267 if ( r->right != r->left + w*term->cols + ex_width)
5a73255e 2268 rv = 1;
2269
2270 if (wParam == WMSZ_LEFT ||
2271 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
887035a5 2272 r->left = r->right - w*term->cols - ex_width;
5a73255e 2273 else
887035a5 2274 r->right = r->left + w*term->cols + ex_width;
5a73255e 2275
887035a5 2276 if (r->bottom != r->top + h*term->rows + ex_height)
5a73255e 2277 rv = 1;
2278
2279 if (wParam == WMSZ_TOP ||
2280 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
887035a5 2281 r->top = r->bottom - h*term->rows - ex_height;
5a73255e 2282 else
887035a5 2283 r->bottom = r->top + h*term->rows + ex_height;
5a73255e 2284
2285 return rv;
374330e2 2286 }
32874aea 2287 /* break; (never reached) */
1ba99d2c 2288 case WM_FULLSCR_ON_MAX:
2289 fullscr_on_max = TRUE;
2290 break;
c1da5066 2291 case WM_MOVE:
2292 sys_cursor_update();
2293 break;
374330e2 2294 case WM_SIZE:
5a73255e 2295#ifdef RDB_DEBUG_PATCH
2296 debug((27, "WM_SIZE %s (%d,%d)",
2297 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2298 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2299 (wParam == SIZE_RESTORED && resizing) ? "to":
2300 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2301 "...",
2302 LOWORD(lParam), HIWORD(lParam)));
2303#endif
0ed2f48e 2304 if (wParam == SIZE_MINIMIZED)
32874aea 2305 SetWindowText(hwnd,
2306 cfg.win_name_always ? window_name : icon_name);
374330e2 2307 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2308 SetWindowText(hwnd, window_name);
811f10b3 2309 if (wParam == SIZE_RESTORED)
2310 clear_full_screen();
2311 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
811f10b3 2312 fullscr_on_max = FALSE;
d0f1bcb4 2313 make_full_screen();
811f10b3 2314 }
5a73255e 2315
ad5c93cc 2316 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2317 /* A resize, well it better be a minimize. */
2318 reset_window(-1);
2319 } else {
2320
1a6f78fe 2321 int width, height, w, h;
374330e2 2322
2323 width = LOWORD(lParam);
2324 height = HIWORD(lParam);
5a73255e 2325
2326 if (!resizing) {
0ed2f48e 2327 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
5a73255e 2328 was_zoomed = 1;
887035a5 2329 prev_rows = term->rows;
2330 prev_cols = term->cols;
0ed2f48e 2331 if (cfg.resize_action == RESIZE_TERM) {
5a73255e 2332 w = width / font_width;
2333 if (w < 1) w = 1;
2334 h = height / font_height;
2335 if (h < 1) h = 1;
2336
887035a5 2337 term_size(term, h, w, cfg.savelines);
5a73255e 2338 }
2339 reset_window(0);
2340 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2341 was_zoomed = 0;
0ed2f48e 2342 if (cfg.resize_action == RESIZE_TERM)
887035a5 2343 term_size(term, prev_rows, prev_cols, cfg.savelines);
0ed2f48e 2344 if (cfg.resize_action != RESIZE_FONT)
2345 reset_window(2);
2346 else
2347 reset_window(0);
5a73255e 2348 }
2349 /* This is an unexpected resize, these will normally happen
2350 * if the window is too large. Probably either the user
2351 * selected a huge font or the screen size has changed.
2352 *
2353 * This is also called with minimize.
32874aea 2354 */
5a73255e 2355 else reset_window(-1);
2356 }
2357
2358 /*
2359 * Don't call back->size in mid-resize. (To prevent
2360 * massive numbers of resize events getting sent
2361 * down the connection during an NT opaque drag.)
2362 */
2363 if (resizing) {
c1b55581 2364 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
3ad8c6db 2365 need_backend_resize = TRUE;
5a73255e 2366 w = (width-cfg.window_border*2) / font_width;
2367 if (w < 1) w = 1;
2368 h = (height-cfg.window_border*2) / font_height;
2369 if (h < 1) h = 1;
2370
e44d78b6 2371 cfg.height = h;
2372 cfg.width = w;
5a73255e 2373 } else
2374 reset_window(0);
374330e2 2375 }
2376 }
c1da5066 2377 sys_cursor_update();
374330e2 2378 return 0;
2379 case WM_VSCROLL:
2380 switch (LOWORD(wParam)) {
32874aea 2381 case SB_BOTTOM:
887035a5 2382 term_scroll(term, -1, 0);
32874aea 2383 break;
2384 case SB_TOP:
887035a5 2385 term_scroll(term, +1, 0);
32874aea 2386 break;
2387 case SB_LINEDOWN:
887035a5 2388 term_scroll(term, 0, +1);
32874aea 2389 break;
2390 case SB_LINEUP:
887035a5 2391 term_scroll(term, 0, -1);
32874aea 2392 break;
2393 case SB_PAGEDOWN:
887035a5 2394 term_scroll(term, 0, +term->rows / 2);
32874aea 2395 break;
2396 case SB_PAGEUP:
887035a5 2397 term_scroll(term, 0, -term->rows / 2);
32874aea 2398 break;
2399 case SB_THUMBPOSITION:
2400 case SB_THUMBTRACK:
887035a5 2401 term_scroll(term, 1, HIWORD(wParam));
32874aea 2402 break;
2403 }
2404 break;
2405 case WM_PALETTECHANGED:
374330e2 2406 if ((HWND) wParam != hwnd && pal != NULL) {
a8327734 2407 HDC hdc = get_ctx(NULL);
374330e2 2408 if (hdc) {
32874aea 2409 if (RealizePalette(hdc) > 0)
2410 UpdateColors(hdc);
2411 free_ctx(hdc);
374330e2 2412 }
2413 }
2414 break;
2415 case WM_QUERYNEWPALETTE:
2416 if (pal != NULL) {
a8327734 2417 HDC hdc = get_ctx(NULL);
374330e2 2418 if (hdc) {
32874aea 2419 if (RealizePalette(hdc) > 0)
2420 UpdateColors(hdc);
2421 free_ctx(hdc);
374330e2 2422 return TRUE;
2423 }
2424 }
2425 return FALSE;
2426 case WM_KEYDOWN:
2427 case WM_SYSKEYDOWN:
c9def1b8 2428 case WM_KEYUP:
2429 case WM_SYSKEYUP:
374330e2 2430 /*
2431 * Add the scan code and keypress timing to the random
7d6ee6ff 2432 * number noise.
374330e2 2433 */
32874aea 2434 noise_ultralight(lParam);
374330e2 2435
2436 /*
2437 * We don't do TranslateMessage since it disassociates the
2438 * resulting CHAR message from the KEYDOWN that sparked it,
2439 * which we occasionally don't want. Instead, we process
2440 * KEYDOWN, and call the Win32 translator functions so that
2441 * we get the translations under _our_ control.
2442 */
2443 {
2444 unsigned char buf[20];
2445 int len;
2446
32874aea 2447 if (wParam == VK_PROCESSKEY) {
3cf144db 2448 MSG m;
32874aea 2449 m.hwnd = hwnd;
2450 m.message = WM_KEYDOWN;
2451 m.wParam = wParam;
2452 m.lParam = lParam & 0xdfff;
2453 TranslateMessage(&m);
2454 } else {
2455 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2456 if (len == -1)
32874aea 2457 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2458
b2a1eade 2459 if (len != 0) {
bca9517a 2460 /*
256cb87c 2461 * Interrupt an ongoing paste. I'm not sure
2462 * this is sensible, but for the moment it's
2463 * preferable to having to faff about buffering
2464 * things.
2465 */
887035a5 2466 term_nopaste(term);
256cb87c 2467
2468 /*
bca9517a 2469 * We need not bother about stdin backlogs
2470 * here, because in GUI PuTTY we can't do
2471 * anything about it anyway; there's no means
2472 * of asking Windows to hold off on KEYDOWN
2473 * messages. We _have_ to buffer everything
2474 * we're sent.
2475 */
887035a5 2476 term_seen_key_event(term);
b9d7bcad 2477 ldisc_send(ldisc, buf, len, 1);
32874aea 2478 show_mouseptr(0);
bca9517a 2479 }
3cf144db 2480 }
374330e2 2481 }
7732d38a 2482 net_pending_errors();
374330e2 2483 return 0;
4eeb7d09 2484 case WM_INPUTLANGCHANGE:
a5ce2e9f 2485 /* wParam == Font number */
2486 /* lParam == Locale */
2487 set_input_locale((HKL)lParam);
c1da5066 2488 sys_cursor_update();
4eeb7d09 2489 break;
71f6951d 2490 case WM_IME_NOTIFY:
2491 if(wParam == IMN_SETOPENSTATUS) {
2492 HIMC hImc = ImmGetContext(hwnd);
2493 ImmSetCompositionFont(hImc, &lfont);
2494 ImmReleaseContext(hwnd, hImc);
2495 return 0;
2496 }
2497 break;
88485e4d 2498 case WM_IME_COMPOSITION:
2499 {
2500 HIMC hIMC;
2501 int n;
2502 char *buff;
a3cfd0c8 2503
88485e4d 2504 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2505 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2506
2507 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2508 break; /* fall back to DefWindowProc */
2509
2510 hIMC = ImmGetContext(hwnd);
2511 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2512
2513 if (n > 0) {
a3cfd0c8 2514 int i;
88485e4d 2515 buff = (char*) smalloc(n);
2516 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
a3cfd0c8 2517 /*
2518 * Jaeyoun Chung reports that Korean character
2519 * input doesn't work correctly if we do a single
2520 * luni_send() covering the whole of buff. So
2521 * instead we luni_send the characters one by one.
2522 */
887035a5 2523 term_seen_key_event(term);
2cb50250 2524 for (i = 0; i < n; i += 2) {
b9d7bcad 2525 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2cb50250 2526 }
88485e4d 2527 free(buff);
2528 }
2529 ImmReleaseContext(hwnd, hIMC);
2530 return 1;
2531 }
2532
4eeb7d09 2533 case WM_IME_CHAR:
2534 if (wParam & 0xFF00) {
3cf144db 2535 unsigned char buf[2];
2536
2537 buf[1] = wParam;
2538 buf[0] = wParam >> 8;
887035a5 2539 term_seen_key_event(term);
b9d7bcad 2540 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
4eeb7d09 2541 } else {
2542 char c = (unsigned char) wParam;
887035a5 2543 term_seen_key_event(term);
b9d7bcad 2544 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
3cf144db 2545 }
4eeb7d09 2546 return (0);
374330e2 2547 case WM_CHAR:
2548 case WM_SYSCHAR:
2549 /*
2550 * Nevertheless, we are prepared to deal with WM_CHAR
2551 * messages, should they crop up. So if someone wants to
2552 * post the things to us as part of a macro manoeuvre,
2553 * we're ready to cope.
2554 */
32874aea 2555 {
4eeb7d09 2556 char c = (unsigned char)wParam;
887035a5 2557 term_seen_key_event(term);
b9d7bcad 2558 lpage_send(ldisc, CP_ACP, &c, 1, 1);
374330e2 2559 }
2560 return 0;
32874aea 2561 case WM_SETCURSOR:
ca3ab3ee 2562 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2563 SetCursor(LoadCursor(NULL, IDC_ARROW));
2564 return TRUE;
2565 }
011b0e33 2566 default:
16837214 2567 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
c1b55581 2568 int shift_pressed=0, control_pressed=0;
011b0e33 2569
2570 if (message == WM_MOUSEWHEEL) {
2571 wheel_accumulator += (short)HIWORD(wParam);
2572 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2573 control_pressed=LOWORD(wParam) & MK_CONTROL;
2574 } else {
2575 BYTE keys[256];
2576 wheel_accumulator += (int)wParam;
2577 if (GetKeyboardState(keys)!=0) {
2578 shift_pressed=keys[VK_SHIFT]&0x80;
2579 control_pressed=keys[VK_CONTROL]&0x80;
2580 }
2581 }
2582
2583 /* process events when the threshold is reached */
2584 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2585 int b;
2586
2587 /* reduce amount for next time */
2588 if (wheel_accumulator > 0) {
2589 b = MBT_WHEEL_UP;
2590 wheel_accumulator -= WHEEL_DELTA;
2591 } else if (wheel_accumulator < 0) {
2592 b = MBT_WHEEL_DOWN;
2593 wheel_accumulator += WHEEL_DELTA;
2594 } else
2595 break;
2596
2597 if (send_raw_mouse &&
2598 !(cfg.mouse_override && shift_pressed)) {
2599 /* send a mouse-down followed by a mouse up */
887035a5 2600 term_mouse(term, b,
011b0e33 2601 MA_CLICK,
2602 TO_CHR_X(X_POS(lParam)),
2603 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2604 control_pressed, is_alt_pressed());
887035a5 2605 term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
011b0e33 2606 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2607 control_pressed, is_alt_pressed());
2608 } else {
2609 /* trigger a scroll */
887035a5 2610 term_scroll(term, 0,
2611 b == MBT_WHEEL_UP ?
2612 -term->rows / 2 : term->rows / 2);
011b0e33 2613 }
2614 }
2615 return 0;
2616 }
374330e2 2617 }
2618
32874aea 2619 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2620}
2621
2622/*
ec8679e9 2623 * Move the system caret. (We maintain one, even though it's
2624 * invisible, for the benefit of blind people: apparently some
2625 * helper software tracks the system caret, so we should arrange to
2626 * have one.)
2627 */
a8327734 2628void sys_cursor(void *frontend, int x, int y)
32874aea 2629{
c1da5066 2630 int cx, cy;
2631
887035a5 2632 if (!term->has_focus) return;
c1da5066 2633
2634 /*
2635 * Avoid gratuitously re-updating the cursor position and IMM
2636 * window if there's no actual change required.
2637 */
2638 cx = x * font_width + offset_width;
2639 cy = y * font_height + offset_height;
2640 if (cx == caret_x && cy == caret_y)
2641 return;
2642 caret_x = cx;
2643 caret_y = cy;
2644
2645 sys_cursor_update();
2646}
2647
2648static void sys_cursor_update(void)
2649{
88485e4d 2650 COMPOSITIONFORM cf;
2651 HIMC hIMC;
2652
887035a5 2653 if (!term->has_focus) return;
c1da5066 2654
2655 if (caret_x < 0 || caret_y < 0)
2656 return;
2657
2658 SetCaretPos(caret_x, caret_y);
88485e4d 2659
2660 /* IMM calls on Win98 and beyond only */
2661 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2662
2663 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2664 osVersion.dwMinorVersion == 0) return; /* 95 */
2665
2666 /* we should have the IMM functions */
2667 hIMC = ImmGetContext(hwnd);
2668 cf.dwStyle = CFS_POINT;
c1da5066 2669 cf.ptCurrentPos.x = caret_x;
2670 cf.ptCurrentPos.y = caret_y;
88485e4d 2671 ImmSetCompositionWindow(hIMC, &cf);
2672
2673 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2674}
2675
2676/*
374330e2 2677 * Draw a line of text in the window, at given character
2678 * coordinates, in given attributes.
2679 *
2680 * We are allowed to fiddle with the contents of `text'.
2681 */
32874aea 2682void do_text(Context ctx, int x, int y, char *text, int len,
2683 unsigned long attr, int lattr)
2684{
374330e2 2685 COLORREF fg, bg, t;
2686 int nfg, nbg, nfont;
2687 HDC hdc = ctx;
59ad2c03 2688 RECT line_box;
2689 int force_manual_underline = 0;
32874aea 2690 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2691 int char_width = fnt_width;
2692 int text_adjust = 0;
2693 static int *IpDx = 0, IpDxLEN = 0;
2694
2695 if (attr & ATTR_WIDE)
2696 char_width *= 2;
59ad2c03 2697
4eeb7d09 2698 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2699 int i;
32874aea 2700 if (len > IpDxLEN) {
59ad2c03 2701 sfree(IpDx);
32874aea 2702 IpDx = smalloc((len + 16) * sizeof(int));
2703 IpDxLEN = (len + 16);
59ad2c03 2704 }
32874aea 2705 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2706 IpDx[i] = char_width;
59ad2c03 2707 }
374330e2 2708
5a73255e 2709 /* Only want the left half of double width lines */
887035a5 2710 if (lattr != LATTR_NORM && x*2 >= term->cols)
5a73255e 2711 return;
2712
c9def1b8 2713 x *= fnt_width;
374330e2 2714 y *= font_height;
5a73255e 2715 x += offset_width;
2716 y += offset_height;
374330e2 2717
887035a5 2718 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
4eeb7d09 2719 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2720 attr ^= ATTR_CUR_XOR;
2721 }
2722
2723 nfont = 0;
4eeb7d09 2724 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2725 /* Assume a poorman font is borken in other ways too. */
2726 lattr = LATTR_WIDE;
2727 } else
2728 switch (lattr) {
2729 case LATTR_NORM:
2730 break;
2731 case LATTR_WIDE:
2732 nfont |= FONT_WIDE;
374330e2 2733 break;
4eeb7d09 2734 default:
2735 nfont |= FONT_WIDE + FONT_HIGH;
2736 break;
2737 }
5a73255e 2738 if (attr & ATTR_NARROW)
2739 nfont |= FONT_NARROW;
4eeb7d09 2740
2741 /* Special hack for the VT100 linedraw glyphs. */
2742 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2743 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2744 switch ((unsigned char) (text[0])) {
2745 case 0xBA:
2746 text_adjust = -2 * font_height / 5;
2747 break;
2748 case 0xBB:
2749 text_adjust = -1 * font_height / 5;
2750 break;
2751 case 0xBC:
2752 text_adjust = font_height / 5;
2753 break;
2754 case 0xBD:
2755 text_adjust = 2 * font_height / 5;
32874aea 2756 break;
59ad2c03 2757 }
4eeb7d09 2758 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2759 text_adjust *= 2;
2760 attr &= ~CSET_MASK;
2761 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2762 attr |= (unitab_xterm['q'] & CSET_MASK);
2763 if (attr & ATTR_UNDER) {
2764 attr &= ~ATTR_UNDER;
2765 force_manual_underline = 1;
2766 }
374330e2 2767 }
2768 }
2769
4eeb7d09 2770 /* Anything left as an original character set is unprintable. */
2771 if (DIRECT_CHAR(attr)) {
2772 attr &= ~CSET_MASK;
2773 attr |= 0xFF00;
5a73255e 2774 memset(text, 0xFD, len);
4eeb7d09 2775 }
2776
2777 /* OEM CP */
2778 if ((attr & CSET_MASK) == ATTR_OEMCP)
2779 nfont |= FONT_OEM;
2780
374330e2 2781 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2782 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2783 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2784 nfont |= FONT_BOLD;
2785 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2786 nfont |= FONT_UNDERLINE;
4eeb7d09 2787 another_font(nfont);
32874aea 2788 if (!fonts[nfont]) {
2789 if (nfont & FONT_UNDERLINE)
59ad2c03 2790 force_manual_underline = 1;
2791 /* Don't do the same for manual bold, it could be bad news. */
2792
32874aea 2793 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2794 }
4eeb7d09 2795 another_font(nfont);
2796 if (!fonts[nfont])
2797 nfont = FONT_NORMAL;
374330e2 2798 if (attr & ATTR_REVERSE) {
32874aea 2799 t = nfg;
2800 nfg = nbg;
2801 nbg = t;
374330e2 2802 }
2803 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2804 nfg++;
59ad2c03 2805 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2806 nbg++;
374330e2 2807 fg = colours[nfg];
2808 bg = colours[nbg];
32874aea 2809 SelectObject(hdc, fonts[nfont]);
2810 SetTextColor(hdc, fg);
2811 SetBkColor(hdc, bg);
2812 SetBkMode(hdc, OPAQUE);
2813 line_box.left = x;
2814 line_box.top = y;
4eeb7d09 2815 line_box.right = x + char_width * len;
32874aea 2816 line_box.bottom = y + font_height;
4eeb7d09 2817
5a73255e 2818 /* Only want the left half of double width lines */
887035a5 2819 if (line_box.right > font_width*term->cols+offset_width)
2820 line_box.right = font_width*term->cols+offset_width;
5a73255e 2821
4eeb7d09 2822 /* We're using a private area for direct to font. (512 chars.) */
2823 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2824 /* Ho Hum, dbcs fonts are a PITA! */
2825 /* To display on W9x I have to convert to UCS */
2826 static wchar_t *uni_buf = 0;
2827 static int uni_len = 0;
5a73255e 2828 int nlen, mptr;
4eeb7d09 2829 if (len > uni_len) {
2830 sfree(uni_buf);
2831 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2832 }
4eeb7d09 2833
5a73255e 2834 for(nlen = mptr = 0; mptr<len; mptr++) {
2835 uni_buf[nlen] = 0xFFFD;
2836 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2837 IpDx[nlen] += char_width;
2838 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2839 text+mptr, 2, uni_buf+nlen, 1);
2840 mptr++;
2841 }
2842 else
2843 {
2844 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2845 text+mptr, 1, uni_buf+nlen, 1);
2846 }
2847 nlen++;
2848 }
4eeb7d09 2849 if (nlen <= 0)
2850 return; /* Eeek! */
2851
2852 ExtTextOutW(hdc, x,
2853 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2854 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2855 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2856 SetBkMode(hdc, TRANSPARENT);
2857 ExtTextOutW(hdc, x - 1,
2858 y - font_height * (lattr ==
2859 LATTR_BOT) + text_adjust,
5a73255e 2860 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2861 }
5a73255e 2862
2863 IpDx[0] = -1;
4eeb7d09 2864 } else if (DIRECT_FONT(attr)) {
2865 ExtTextOut(hdc, x,
2866 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2867 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2868 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2869 SetBkMode(hdc, TRANSPARENT);
2870
2871 /* GRR: This draws the character outside it's box and can leave
2872 * 'droppings' even with the clip box! I suppose I could loop it
2873 * one character at a time ... yuk.
2874 *
2875 * Or ... I could do a test print with "W", and use +1 or -1 for this
2876 * shift depending on if the leftmost column is blank...
2877 */
2878 ExtTextOut(hdc, x - 1,
2879 y - font_height * (lattr ==
2880 LATTR_BOT) + text_adjust,
2881 ETO_CLIPPED, &line_box, text, len, IpDx);
2882 }
2883 } else {
2884 /* And 'normal' unicode characters */
2885 static WCHAR *wbuf = NULL;
2886 static int wlen = 0;
2887 int i;
2888 if (wlen < len) {
2889 sfree(wbuf);
2890 wlen = len;
2891 wbuf = smalloc(wlen * sizeof(WCHAR));
2892 }
2893 for (i = 0; i < len; i++)
2894 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2895
2896 ExtTextOutW(hdc, x,
2897 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2898 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2899
2900 /* And the shadow bold hack. */
5a73255e 2901 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2902 SetBkMode(hdc, TRANSPARENT);
2903 ExtTextOutW(hdc, x - 1,
2904 y - font_height * (lattr ==
2905 LATTR_BOT) + text_adjust,
2906 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2907 }
374330e2 2908 }
4eeb7d09 2909 if (lattr != LATTR_TOP && (force_manual_underline ||
2910 (und_mode == UND_LINE
2911 && (attr & ATTR_UNDER)))) {
32874aea 2912 HPEN oldpen;
4eeb7d09 2913 int dec = descent;
2914 if (lattr == LATTR_BOT)
2915 dec = dec * 2 - font_height;
2916
32874aea 2917 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2918 MoveToEx(hdc, x, y + dec, NULL);
2919 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2920 oldpen = SelectObject(hdc, oldpen);
2921 DeleteObject(oldpen);
374330e2 2922 }
4eeb7d09 2923}
2924
2925void do_cursor(Context ctx, int x, int y, char *text, int len,
2926 unsigned long attr, int lattr)
2927{
2928
2929 int fnt_width;
2930 int char_width;
2931 HDC hdc = ctx;
2932 int ctype = cfg.cursor_type;
2933
887035a5 2934 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
4eeb7d09 2935 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2936 do_text(ctx, x, y, text, len, attr, lattr);
2937 return;
2938 }
2939 ctype = 2;
2940 attr |= TATTR_RIGHTCURS;
2941 }
2942
2943 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2944 if (attr & ATTR_WIDE)
2945 char_width *= 2;
2946 x *= fnt_width;
2947 y *= font_height;
5a73255e 2948 x += offset_width;
2949 y += offset_height;
4eeb7d09 2950
887035a5 2951 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
374330e2 2952 POINT pts[5];
32874aea 2953 HPEN oldpen;
374330e2 2954 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2955 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2956 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2957 pts[1].y = pts[2].y = y + font_height - 1;
2958 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2959 Polyline(hdc, pts, 5);
2960 oldpen = SelectObject(hdc, oldpen);
2961 DeleteObject(oldpen);
4eeb7d09 2962 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2963 int startx, starty, dx, dy, length, i;
4eeb7d09 2964 if (ctype == 1) {
32874aea 2965 startx = x;
2966 starty = y + descent;
2967 dx = 1;
2968 dy = 0;
4eeb7d09 2969 length = char_width;
32874aea 2970 } else {
4e30ff69 2971 int xadjust = 0;
4eeb7d09 2972 if (attr & TATTR_RIGHTCURS)
2973 xadjust = char_width - 1;
32874aea 2974 startx = x + xadjust;
2975 starty = y;
2976 dx = 0;
2977 dy = 1;
2978 length = font_height;
2979 }
4eeb7d09 2980 if (attr & TATTR_ACTCURS) {
32874aea 2981 HPEN oldpen;
2982 oldpen =
2983 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2984 MoveToEx(hdc, startx, starty, NULL);
2985 LineTo(hdc, startx + dx * length, starty + dy * length);
2986 oldpen = SelectObject(hdc, oldpen);
2987 DeleteObject(oldpen);
2988 } else {
2989 for (i = 0; i < length; i++) {
2990 if (i % 2 == 0) {
2991 SetPixel(hdc, startx, starty, colours[23]);
2992 }
2993 startx += dx;
2994 starty += dy;
2995 }
2996 }
4e30ff69 2997 }
374330e2 2998}
2999
5a73255e 3000/* This function gets the actual width of a character in the normal font.
3001 */
2102eb8a 3002int char_width(Context ctx, int uc) {
5a73255e 3003 HDC hdc = ctx;
3004 int ibuf = 0;
3005
3006 /* If the font max is the same as the font ave width then this
3007 * function is a no-op.
3008 */
3009 if (!font_dualwidth) return 1;
3010
3011 switch (uc & CSET_MASK) {
3012 case ATTR_ASCII:
3013 uc = unitab_line[uc & 0xFF];
3014 break;
3015 case ATTR_LINEDRW:
3016 uc = unitab_xterm[uc & 0xFF];
3017 break;
3018 case ATTR_SCOACS:
3019 uc = unitab_scoacs[uc & 0xFF];
3020 break;
3021 }
3022 if (DIRECT_FONT(uc)) {
3023 if (dbcs_screenfont) return 1;
3024
3025 /* Speedup, I know of no font where ascii is the wrong width */
3026 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3027 return 1;
3028
3029 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3030 SelectObject(hdc, fonts[FONT_NORMAL]);
3031 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3032 another_font(FONT_OEM);
3033 if (!fonts[FONT_OEM]) return 0;
3034
3035 SelectObject(hdc, fonts[FONT_OEM]);
3036 } else
3037 return 0;
3038
3039 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3040 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3041 return 0;
3042 } else {
3043 /* Speedup, I know of no font where ascii is the wrong width */
3044 if (uc >= ' ' && uc <= '~') return 1;
3045
3046 SelectObject(hdc, fonts[FONT_NORMAL]);
3047 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3048 /* Okay that one worked */ ;
3049 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3050 /* This should work on 9x too, but it's "less accurate" */ ;
3051 else
3052 return 0;
3053 }
3054
3055 ibuf += font_width / 2 -1;
3056 ibuf /= font_width;
3057
3058 return ibuf;
3059}
3060
374330e2 3061/*
c9def1b8 3062 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3063 * codes. Returns number of bytes used or zero to drop the message
3064 * or -1 to forward the message to windows.
374330e2 3065 */
3cf144db 3066static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 3067 unsigned char *output)
3068{
374330e2 3069 BYTE keystate[256];
32874aea 3070 int scan, left_alt = 0, key_down, shift_state;
3071 int r, i, code;
3072 unsigned char *p = output;
4eeb7d09 3073 static int alt_sum = 0;
374330e2 3074
00e3ba0f 3075 HKL kbd_layout = GetKeyboardLayout(0);
3076
0c50ef57 3077 static WORD keys[3];
0c50ef57 3078 static int compose_char = 0;
3079 static WPARAM compose_key = 0;
32874aea 3080
c9def1b8 3081 r = GetKeyboardState(keystate);
32874aea 3082 if (!r)
3083 memset(keystate, 0, sizeof(keystate));
3084 else {
ec55b220 3085#if 0
4eeb7d09 3086#define SHOW_TOASCII_RESULT
32874aea 3087 { /* Tell us all about key events */
3088 static BYTE oldstate[256];
3089 static int first = 1;
3090 static int scan;
3091 int ch;
3092 if (first)
3093 memcpy(oldstate, keystate, sizeof(oldstate));
3094 first = 0;
3095
3096 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3097 debug(("+"));
3098 } else if ((HIWORD(lParam) & KF_UP)
3099 && scan == (HIWORD(lParam) & 0xFF)) {
3100 debug((". U"));
3101 } else {
3102 debug((".\n"));
3103 if (wParam >= VK_F1 && wParam <= VK_F20)
3104 debug(("K_F%d", wParam + 1 - VK_F1));
3105 else
3106 switch (wParam) {
3107 case VK_SHIFT:
3108 debug(("SHIFT"));
3109 break;
3110 case VK_CONTROL:
3111 debug(("CTRL"));
3112 break;
3113 case VK_MENU:
3114 debug(("ALT"));
3115 break;
3116 default:
3117 debug(("VK_%02x", wParam));
3118 }
3119 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3120 debug(("*"));
3121 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3122
3123 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3124 if (ch >= ' ' && ch <= '~')
3125 debug((", '%c'", ch));
3126 else if (ch)
3127 debug((", $%02x", ch));
3128
3129 if (keys[0])
3130 debug((", KB0=%02x", keys[0]));
3131 if (keys[1])
3132 debug((", KB1=%02x", keys[1]));
3133 if (keys[2])
3134 debug((", KB2=%02x", keys[2]));
3135
3136 if ((keystate[VK_SHIFT] & 0x80) != 0)
3137 debug((", S"));
3138 if ((keystate[VK_CONTROL] & 0x80) != 0)
3139 debug((", C"));
3140 if ((HIWORD(lParam) & KF_EXTENDED))
3141 debug((", E"));
3142 if ((HIWORD(lParam) & KF_UP))
3143 debug((", U"));
3144 }
3145
3146 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3147 else if ((HIWORD(lParam) & KF_UP))
3148 oldstate[wParam & 0xFF] ^= 0x80;
3149 else
3150 oldstate[wParam & 0xFF] ^= 0x81;
3151
3152 for (ch = 0; ch < 256; ch++)
3153 if (oldstate[ch] != keystate[ch])
3154 debug((", M%02x=%02x", ch, keystate[ch]));
3155
3156 memcpy(oldstate, keystate, sizeof(oldstate));
3157 }
ec55b220 3158#endif
3159
32874aea 3160 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 3161 keystate[VK_RMENU] = keystate[VK_MENU];
3162 }
3163
c9def1b8 3164
3165 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 3166 if ((cfg.funky_type == 3 ||
887035a5 3167 (cfg.funky_type <= 1 && term->app_keypad_keys &&
3168 !cfg.no_applic_k))
32874aea 3169 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 3170
3171 wParam = VK_EXECUTE;
3172
3173 /* UnToggle NUMLock */
32874aea 3174 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3175 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 3176 }
3177
3178 /* And write back the 'adjusted' state */
32874aea 3179 SetKeyboardState(keystate);
c9def1b8 3180 }
3181
3182 /* Disable Auto repeat if required */
887035a5 3183 if (term->repeat_off &&
3184 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
32874aea 3185 return 0;
c9def1b8 3186
32874aea 3187 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 3188 left_alt = 1;
3189
32874aea 3190 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 3191
95bbe1ae 3192 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 3193 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 3194 if (cfg.ctrlaltkeys)
3195 keystate[VK_MENU] = 0;
3196 else {
3197 keystate[VK_RMENU] = 0x80;
3198 left_alt = 0;
3199 }
3200 }
c9def1b8 3201
3202 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 3203 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3204 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 3205
95bbe1ae 3206 /* Note if AltGr was pressed and if it was used as a compose key */
3207 if (!compose_state) {
159eba53 3208 compose_key = 0x100;
95bbe1ae 3209 if (cfg.compose_key) {
32874aea 3210 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 3211 compose_key = wParam;
3212 }
3213 if (wParam == VK_APPS)
3214 compose_key = wParam;
3215 }
3216
32874aea 3217 if (wParam == compose_key) {
3218 if (compose_state == 0
3219 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3220 1;
3221 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 3222 compose_state = 2;
3223 else
3224 compose_state = 0;
32874aea 3225 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 3226 compose_state = 0;
3227
32874aea 3228 if (compose_state > 1 && left_alt)
3229 compose_state = 0;
67c339f7 3230
c9def1b8 3231 /* Sanitize the number pad if not using a PC NumPad */
887035a5 3232 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
32874aea 3233 && cfg.funky_type != 2)
3234 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3235 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 3236 int nParam = 0;
32874aea 3237 switch (wParam) {
3238 case VK_INSERT:
3239 nParam = VK_NUMPAD0;
3240 break;
3241 case VK_END:
3242 nParam = VK_NUMPAD1;
3243 break;
3244 case VK_DOWN:
3245 nParam = VK_NUMPAD2;
3246 break;
3247 case VK_NEXT:
3248 nParam = VK_NUMPAD3;
3249 break;
3250 case VK_LEFT:
3251 nParam = VK_NUMPAD4;
3252 break;
3253 case VK_CLEAR:
3254 nParam = VK_NUMPAD5;
3255 break;
3256 case VK_RIGHT:
3257 nParam = VK_NUMPAD6;
3258 break;
3259 case VK_HOME:
3260 nParam = VK_NUMPAD7;
3261 break;
3262 case VK_UP:
3263 nParam = VK_NUMPAD8;
3264 break;
3265 case VK_PRIOR:
3266 nParam = VK_NUMPAD9;
3267 break;
3268 case VK_DELETE:
3269 nParam = VK_DECIMAL;
3270 break;
c9def1b8 3271 }
32874aea 3272 if (nParam) {
3273 if (keystate[VK_NUMLOCK] & 1)
3274 shift_state |= 1;
c9def1b8 3275 wParam = nParam;
3276 }
25d39ef6 3277 }
3278 }
3279
c9def1b8 3280 /* If a key is pressed and AltGr is not active */
32874aea 3281 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3282 /* Okay, prepare for most alts then ... */
3283 if (left_alt)
3284 *p++ = '\033';
374330e2 3285
c9def1b8 3286 /* Lets see if it's a pattern we know all about ... */
3287 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 3288 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3289 return 0;
c9def1b8 3290 }
3291 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3292 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3293 return 0;
3294 }
3295 if (wParam == VK_INSERT && shift_state == 1) {
887035a5 3296 term_do_paste(term);
32874aea 3297 return 0;
3298 }
c9def1b8 3299 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3300 return -1;
c9def1b8 3301 }
3302 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3303 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3304 return -1;
c9def1b8 3305 }
2cae8529 3306 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3307 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3308 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3309 flip_full_screen();
8f57d753 3310 return -1;
3311 }
ec55b220 3312 /* Control-Numlock for app-keypad mode switch */
3313 if (wParam == VK_PAUSE && shift_state == 2) {
887035a5 3314 term->app_keypad_keys ^= 1;
ec55b220 3315 return 0;
3316 }
374330e2 3317
c9def1b8 3318 /* Nethack keypad */
3319 if (cfg.nethack_keypad && !left_alt) {
32874aea 3320 switch (wParam) {
3321 case VK_NUMPAD1:
3322 *p++ = shift_state ? 'B' : 'b';
3323 return p - output;
3324 case VK_NUMPAD2:
3325 *p++ = shift_state ? 'J' : 'j';
3326 return p - output;
3327 case VK_NUMPAD3:
3328 *p++ = shift_state ? 'N' : 'n';
3329 return p - output;
3330 case VK_NUMPAD4:
3331 *p++ = shift_state ? 'H' : 'h';
3332 return p - output;
3333 case VK_NUMPAD5:
3334 *p++ = shift_state ? '.' : '.';
3335 return p - output;
3336 case VK_NUMPAD6:
3337 *p++ = shift_state ? 'L' : 'l';
3338 return p - output;
3339 case VK_NUMPAD7:
3340 *p++ = shift_state ? 'Y' : 'y';
3341 return p - output;
3342 case VK_NUMPAD8:
3343 *p++ = shift_state ? 'K' : 'k';
3344 return p - output;
3345 case VK_NUMPAD9:
3346 *p++ = shift_state ? 'U' : 'u';
3347 return p - output;
3348 }
c9def1b8 3349 }
3350
3351 /* Application Keypad */
3352 if (!left_alt) {
32874aea 3353 int xkey = 0;
3354
3355 if (cfg.funky_type == 3 ||
3356 (cfg.funky_type <= 1 &&
887035a5 3357 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
32874aea 3358 case VK_EXECUTE:
3359 xkey = 'P';
3360 break;
3361 case VK_DIVIDE:
3362 xkey = 'Q';
3363 break;
3364 case VK_MULTIPLY:
3365 xkey = 'R';
3366 break;
3367 case VK_SUBTRACT:
3368 xkey = 'S';
3369 break;
3370 }
887035a5 3371 if (term->app_keypad_keys && !cfg.no_applic_k)
32874aea 3372 switch (wParam) {
3373 case VK_NUMPAD0:
3374 xkey = 'p';
3375 break;
3376 case VK_NUMPAD1:
3377 xkey = 'q';
3378 break;
3379 case VK_NUMPAD2:
3380 xkey = 'r';
3381 break;
3382 case VK_NUMPAD3:
3383 xkey = 's';
3384 break;
3385 case VK_NUMPAD4:
3386 xkey = 't';
3387 break;
3388 case VK_NUMPAD5:
3389 xkey = 'u';
3390 break;
3391 case VK_NUMPAD6:
3392 xkey = 'v';
3393 break;
3394 case VK_NUMPAD7:
3395 xkey = 'w';
3396 break;
3397 case VK_NUMPAD8:
3398 xkey = 'x';
3399 break;
3400 case VK_NUMPAD9:
3401 xkey = 'y';
3402 break;
3403
3404 case VK_DECIMAL:
3405 xkey = 'n';
3406 break;
3407 case VK_ADD:
3408 if (cfg.funky_type == 2) {
3409 if (shift_state)
3410 xkey = 'l';
3411 else
3412 xkey = 'k';
3413 } else if (shift_state)
3414 xkey = 'm';
c9def1b8 3415 else
32874aea 3416 xkey = 'l';
3417 break;
3418
3419 case VK_DIVIDE:
3420 if (cfg.funky_type == 2)
3421 xkey = 'o';
3422 break;
3423 case VK_MULTIPLY:
3424 if (cfg.funky_type == 2)
3425 xkey = 'j';
3426 break;
3427 case VK_SUBTRACT:
3428 if (cfg.funky_type == 2)
3429 xkey = 'm';
3430 break;
3431
3432 case VK_RETURN:
3433 if (HIWORD(lParam) & KF_EXTENDED)
3434 xkey = 'M';
3435 break;
c9def1b8 3436 }
32874aea 3437 if (xkey) {
887035a5 3438 if (term->vt52_mode) {
32874aea 3439 if (xkey >= 'P' && xkey <= 'S')
3440 p += sprintf((char *) p, "\x1B%c", xkey);
3441 else
3442 p += sprintf((char *) p, "\x1B?%c", xkey);
3443 } else
3444 p += sprintf((char *) p, "\x1BO%c", xkey);
3445 return p - output;
c9def1b8 3446 }
3447 }
3448
32874aea 3449 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3450 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3451 *p++ = 0;
3452 return -2;
c9def1b8 3453 }
e8e8d6e2 3454 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3455 /* We do the opposite of what is configured */
3456 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3457 *p++ = 0;
3458 return -2;
3459 }
32874aea 3460 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3461 *p++ = 0x1B;
3462 *p++ = '[';
3463 *p++ = 'Z';
3464 return p - output;
c9def1b8 3465 }
32874aea 3466 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3467 *p++ = 0;
3468 return p - output;
c9def1b8 3469 }
32874aea 3470 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3471 *p++ = 160;
3472 return p - output;
c9def1b8 3473 }
32874aea 3474 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3475 *p++ = 3;
a5f3e637 3476 *p++ = 0;
3477 return -2;
c9def1b8 3478 }
32874aea 3479 if (wParam == VK_PAUSE) { /* Break/Pause */
3480 *p++ = 26;
3481 *p++ = 0;
3482 return -2;
95bbe1ae 3483 }
c9def1b8 3484 /* Control-2 to Control-8 are special */
32874aea 3485 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3486 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3487 return p - output;
3488 }
3489 if (shift_state == 2 && wParam == 0xBD) {
3490 *p++ = 0x1F;
3491 return p - output;
3492 }
3493 if (shift_state == 2 && wParam == 0xDF) {
3494 *p++ = 0x1C;
3495 return p - output;
3496 }
887035a5 3497 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
32874aea 3498 *p++ = '\r';
3499 *p++ = '\n';
c9def1b8 3500 return p - output;
3501 }
374330e2 3502
c5e9c988 3503 /*
c9def1b8 3504 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3505 * for integer decimal nn.)
3506 *
3507 * We also deal with the weird ones here. Linux VCs replace F1
3508 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3509 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3510 * respectively.
c5e9c988 3511 */
c9def1b8 3512 code = 0;
3513 switch (wParam) {
32874aea 3514 case VK_F1:
3515 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3516 break;
3517 case VK_F2:
3518 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3519 break;
3520 case VK_F3:
3521 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3522 break;
3523 case VK_F4:
3524 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3525 break;
3526 case VK_F5:
3527 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3528 break;
3529 case VK_F6:
3530 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3531 break;
3532 case VK_F7:
3533 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3534 break;
3535 case VK_F8:
3536 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3537 break;
3538 case VK_F9:
3539 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3540 break;
3541 case VK_F10:
3542 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3543 break;
3544 case VK_F11:
3545 code = 23;
3546 break;
3547 case VK_F12:
3548 code = 24;
3549 break;
3550 case VK_F13:
3551 code = 25;
3552 break;
3553 case VK_F14:
3554 code = 26;
3555 break;
3556 case VK_F15:
3557 code = 28;
3558 break;
3559 case VK_F16:
3560 code = 29;
3561 break;
3562 case VK_F17:
3563 code = 31;
3564 break;
3565 case VK_F18:
3566 code = 32;
3567 break;
3568 case VK_F19:
3569 code = 33;
3570 break;
3571 case VK_F20:
3572 code = 34;
3573 break;
dfca2656 3574 }
3575 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3576 case VK_HOME:
3577 code = 1;
3578 break;
3579 case VK_INSERT:
3580 code = 2;
3581 break;
3582 case VK_DELETE:
3583 code = 3;
3584 break;
3585 case VK_END:
3586 code = 4;
3587 break;
3588 case VK_PRIOR:
3589 code = 5;
3590 break;
3591 case VK_NEXT:
3592 code = 6;
3593 break;
374330e2 3594 }
ec55b220 3595 /* Reorder edit keys to physical order */
32874aea 3596 if (cfg.funky_type == 3 && code <= 6)
3597 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3598
887035a5 3599 if (term->vt52_mode && code > 0 && code <= 6) {
32874aea 3600 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3601 return p - output;
3602 }
3603
9bc81a2c 3604 if (cfg.funky_type == 5 && /* SCO function keys */
3605 code >= 11 && code <= 34) {
e24b1972 3606 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3607 int index = 0;
3608 switch (wParam) {
3609 case VK_F1: index = 0; break;
3610 case VK_F2: index = 1; break;
3611 case VK_F3: index = 2; break;
3612 case VK_F4: index = 3; break;
3613 case VK_F5: index = 4; break;
3614 case VK_F6: index = 5; break;
3615 case VK_F7: index = 6; break;
3616 case VK_F8: index = 7; break;
3617 case VK_F9: index = 8; break;
3618 case VK_F10: index = 9; break;
3619 case VK_F11: index = 10; break;
3620 case VK_F12: index = 11; break;
3621 }
3622 if (keystate[VK_SHIFT] & 0x80) index += 12;
3623 if (keystate[VK_CONTROL] & 0x80) index += 24;
3624 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3625 return p - output;
3626 }
9bc81a2c 3627 if (cfg.funky_type == 5 && /* SCO small keypad */
3628 code >= 1 && code <= 6) {
3629 char codes[] = "HL.FIG";
3630 if (code == 3) {
3631 *p++ = '\x7F';
3632 } else {
3633 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3634 }
3635 return p - output;
3636 }
887035a5 3637 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
f37caa11 3638 int offt = 0;
32874aea 3639 if (code > 15)
3640 offt++;
3641 if (code > 21)
3642 offt++;
887035a5 3643 if (term->vt52_mode)
32874aea 3644 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3645 else
32874aea 3646 p +=
3647 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3648 return p - output;
3649 }
c9def1b8 3650 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3651 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3652 return p - output;
3653 }
3654 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
887035a5 3655 if (term->vt52_mode)
32874aea 3656 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3657 else
32874aea 3658 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3659 return p - output;
3660 }
3661 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3662 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3663 return p - output;
3664 }
3665 if (code) {
32874aea 3666 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3667 return p - output;
374330e2 3668 }
45dabbc5 3669
c9def1b8 3670 /*
3671 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3672 * some reason seems to send VK_CLEAR to Windows...).
3673 */
3674 {
3675 char xkey = 0;
3676 switch (wParam) {
32874aea 3677 case VK_UP:
3678 xkey = 'A';
3679 break;
3680 case VK_DOWN:
3681 xkey = 'B';
3682 break;
3683 case VK_RIGHT:
3684 xkey = 'C';
3685 break;
3686 case VK_LEFT:
3687 xkey = 'D';
3688 break;
3689 case VK_CLEAR:
3690 xkey = 'G';
3691 break;
c9def1b8 3692 }
32874aea 3693 if (xkey) {
887035a5 3694 if (term->vt52_mode)
32874aea 3695 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3696 else {
887035a5 3697 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3a953121 3698#if 0
3699 /*
3700 * RDB: VT100 & VT102 manuals both state the
3701 * app cursor keys only work if the app keypad
3702 * is on.
3703 *
3704 * SGT: That may well be true, but xterm
3705 * disagrees and so does at least one
3706 * application, so I've #if'ed this out and the
3707 * behaviour is back to PuTTY's original: app
3708 * cursor and app keypad are independently
3709 * switchable modes. If anyone complains about
3710 * _this_ I'll have to put in a configurable
3711 * option.
e864f84f 3712 */
887035a5 3713 if (!term->app_keypad_keys)
e864f84f 3714 app_flg = 0;
3a953121 3715#endif
e864f84f 3716 /* Useful mapping of Ctrl-arrows */
3717 if (shift_state == 2)
3718 app_flg = !app_flg;
3719
3720 if (app_flg)
3721 p += sprintf((char *) p, "\x1BO%c", xkey);
3722 else
3723 p += sprintf((char *) p, "\x1B[%c", xkey);
3724 }
c9def1b8 3725 return p - output;
3726 }
3727 }
0c50ef57 3728
3729 /*
3730 * Finally, deal with Return ourselves. (Win95 seems to
3731 * foul it up when Alt is pressed, for some reason.)
3732 */
32874aea 3733 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3734 *p++ = 0x0D;
a5f3e637 3735 *p++ = 0;
3736 return -2;
0c50ef57 3737 }
4eeb7d09 3738
3739 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3740 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3741 else
3742 alt_sum = 0;
67c339f7 3743 }
374330e2 3744
c9def1b8 3745 /* Okay we've done everything interesting; let windows deal with
3746 * the boring stuff */
3747 {
a9c02454 3748 BOOL capsOn=0;
3749
3750 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3751 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3752 capsOn= !left_alt;
3753 keystate[VK_CAPITAL] = 0;
3754 }
3755
00e3ba0f 3756 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3757#ifdef SHOW_TOASCII_RESULT
3758 if (r == 1 && !key_down) {
3759 if (alt_sum) {
887035a5 3760 if (in_utf(term) || dbcs_screenfont)
4eeb7d09 3761 debug((", (U+%04x)", alt_sum));
3762 else
3763 debug((", LCH(%d)", alt_sum));
3764 } else {
3765 debug((", ACH(%d)", keys[0]));
3766 }
3767 } else if (r > 0) {
3768 int r1;
3769 debug((", ASC("));
3770 for (r1 = 0; r1 < r; r1++) {
3771 debug(("%s%d", r1 ? "," : "", keys[r1]));
3772 }
3773 debug((")"));
3774 }
3775#endif
32874aea 3776 if (r > 0) {
4eeb7d09 3777 WCHAR keybuf;
256cb87c 3778
3779 /*
3780 * Interrupt an ongoing paste. I'm not sure this is
3781 * sensible, but for the moment it's preferable to
3782 * having to faff about buffering things.
3783 */
887035a5 3784 term_nopaste(term);
256cb87c 3785
c9def1b8 3786 p = output;
32874aea 3787 for (i = 0; i < r; i++) {
3788 unsigned char ch = (unsigned char) keys[i];
14963b8f 3789
32874aea 3790 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3791 compose_char = ch;
32874aea 3792 compose_state++;
c9def1b8 3793 continue;
3794 }
32874aea 3795 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3796 int nc;
3797 compose_state = 0;
3798
32874aea 3799 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3800 MessageBeep(MB_ICONHAND);
c9def1b8 3801 return 0;
3802 }
4eeb7d09 3803 keybuf = nc;
887035a5 3804 term_seen_key_event(term);
b9d7bcad 3805 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 3806 continue;
c9def1b8 3807 }
374330e2 3808
c9def1b8 3809 compose_state = 0;
374330e2 3810
4eeb7d09 3811 if (!key_down) {
3812 if (alt_sum) {
887035a5 3813 if (in_utf(term) || dbcs_screenfont) {
4eeb7d09 3814 keybuf = alt_sum;
887035a5 3815 term_seen_key_event(term);
b9d7bcad 3816 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 3817 } else {
3818 ch = (char) alt_sum;
5471d09a 3819 /*
3820 * We need not bother about stdin
3821 * backlogs here, because in GUI PuTTY
3822 * we can't do anything about it
3823 * anyway; there's no means of asking
3824 * Windows to hold off on KEYDOWN
3825 * messages. We _have_ to buffer
3826 * everything we're sent.
3827 */
887035a5 3828 term_seen_key_event(term);
b9d7bcad 3829 ldisc_send(ldisc, &ch, 1, 1);
4eeb7d09 3830 }
3831 alt_sum = 0;
3832 } else
887035a5 3833 term_seen_key_event(term);
b9d7bcad 3834 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
4eeb7d09 3835 } else {
a9c02454 3836 if(capsOn && ch < 0x80) {
3837 WCHAR cbuf[2];
3838 cbuf[0] = 27;
3839 cbuf[1] = xlat_uskbd2cyrllic(ch);
887035a5 3840 term_seen_key_event(term);
b9d7bcad 3841 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3842 } else {
3843 char cbuf[2];
3844 cbuf[0] = '\033';
3845 cbuf[1] = ch;
887035a5 3846 term_seen_key_event(term);
b9d7bcad 3847 lpage_send(ldisc, kbd_codepage,
3848 cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3849 }
c9def1b8 3850 }
bca9517a 3851 show_mouseptr(0);
c9def1b8 3852 }
374330e2 3853
c9def1b8 3854 /* This is so the ALT-Numpad and dead keys work correctly. */
3855 keys[0] = 0;
3856
32874aea 3857 return p - output;
c9def1b8 3858 }
159eba53 3859 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3860 if (!left_alt)
3861 keys[0] = 0;
4eeb7d09 3862 /* If we will be using alt_sum fix the 256s */
887035a5 3863 else if (keys[0] && (in_utf(term) || dbcs_screenfont))
4eeb7d09 3864 keys[0] = 10;
374330e2 3865 }
3866
dfca2656 3867 /*
3868 * ALT alone may or may not want to bring up the System menu.
3869 * If it's not meant to, we return 0 on presses or releases of
3870 * ALT, to show that we've swallowed the keystroke. Otherwise
3871 * we return -1, which means Windows will give the keystroke
3872 * its default handling (i.e. bring up the System menu).
3873 */
3874 if (wParam == VK_MENU && !cfg.alt_only)
3875 return 0;
374330e2 3876
c9def1b8 3877 return -1;
374330e2 3878}
3879
a8327734 3880void request_paste(void *frontend)
e6346999 3881{
3882 /*
3883 * In Windows, pasting is synchronous: we can read the
3884 * clipboard with no difficulty, so request_paste() can just go
3885 * ahead and paste.
3886 */
887035a5 3887 term_do_paste(term);
e6346999 3888}
3889
a8327734 3890void set_title(void *frontend, char *title)
32874aea 3891{
3892 sfree(window_name);
3893 window_name = smalloc(1 + strlen(title));
3894 strcpy(window_name, title);
37508af4 3895 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3896 SetWindowText(hwnd, title);
374330e2 3897}
3898
a8327734 3899void set_icon(void *frontend, char *title)
32874aea 3900{
3901 sfree(icon_name);
3902 icon_name = smalloc(1 + strlen(title));
3903 strcpy(icon_name, title);
37508af4 3904 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3905 SetWindowText(hwnd, title);
374330e2 3906}
3907
a8327734 3908void set_sbar(void *frontend, int total, int start, int page)
32874aea 3909{
374330e2 3910 SCROLLINFO si;
c9def1b8 3911
1ba99d2c 3912 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 3913 return;
c9def1b8 3914
374330e2 3915 si.cbSize = sizeof(si);
c9def1b8 3916 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3917 si.nMin = 0;
3918 si.nMax = total - 1;
3919 si.nPage = page;
3920 si.nPos = start;
c1f5f956 3921 if (hwnd)
32874aea 3922 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3923}
3924
a8327734 3925Context get_ctx(void *frontend)
32874aea 3926{
374330e2 3927 HDC hdc;
3928 if (hwnd) {
32874aea 3929 hdc = GetDC(hwnd);
374330e2 3930 if (hdc && pal)
32874aea 3931 SelectPalette(hdc, pal, FALSE);
374330e2 3932 return hdc;
3933 } else
3934 return NULL;
3935}
3936
32874aea 3937void free_ctx(Context ctx)
3938{
3939 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3940 ReleaseDC(hwnd, ctx);
374330e2 3941}
3942
32874aea 3943static void real_palette_set(int n, int r, int g, int b)
3944{
374330e2 3945 if (pal) {
3946 logpal->palPalEntry[n].peRed = r;
3947 logpal->palPalEntry[n].peGreen = g;
3948 logpal->palPalEntry[n].peBlue = b;
3949 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3950 colours[n] = PALETTERGB(r, g, b);
32874aea 3951 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3952 } else
3953 colours[n] = RGB(r, g, b);
3954}
3955
a8327734 3956void palette_set(void *frontend, int n, int r, int g, int b)
32874aea 3957{
374330e2 3958 static const int first[21] = {
3959 0, 2, 4, 6, 8, 10, 12, 14,
3960 1, 3, 5, 7, 9, 11, 13, 15,
3961 16, 17, 18, 20, 22
3962 };
32874aea 3963 real_palette_set(first[n], r, g, b);
374330e2 3964 if (first[n] >= 18)
32874aea 3965 real_palette_set(first[n] + 1, r, g, b);
374330e2 3966 if (pal) {
a8327734 3967 HDC hdc = get_ctx(frontend);
32874aea 3968 UnrealizeObject(pal);
3969 RealizePalette(hdc);
3970 free_ctx(hdc);
374330e2 3971 }
3972}
3973
a8327734 3974void palette_reset(void *frontend)
32874aea 3975{
374330e2 3976 int i;
3977
3978 for (i = 0; i < NCOLOURS; i++) {
3979 if (pal) {
3980 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3981 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3982 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3983 logpal->palPalEntry[i].peFlags = 0;
3984 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3985 defpal[i].rgbtGreen,
3986 defpal[i].rgbtBlue);
3987 } else
3988 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3989 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3990 }
3991
3992 if (pal) {
3993 HDC hdc;
32874aea 3994 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
a8327734 3995 hdc = get_ctx(frontend);
32874aea 3996 RealizePalette(hdc);
3997 free_ctx(hdc);
374330e2 3998 }
3999}
4000
a8327734 4001void write_aclip(void *frontend, char *data, int len, int must_deselect)
32874aea 4002{
374330e2 4003 HGLOBAL clipdata;
4004 void *lock;
4005
32874aea 4006 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 4007 if (!clipdata)
4008 return;
32874aea 4009 lock = GlobalLock(clipdata);
374330e2 4010 if (!lock)
4011 return;
32874aea 4012 memcpy(lock, data, len);
4013 ((unsigned char *) lock)[len] = 0;
4014 GlobalUnlock(clipdata);
374330e2 4015
f0df44da 4016 if (!must_deselect)
32874aea 4017 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 4018
32874aea 4019 if (OpenClipboard(hwnd)) {
374330e2 4020 EmptyClipboard();
32874aea 4021 SetClipboardData(CF_TEXT, clipdata);
374330e2 4022 CloseClipboard();
4023 } else
32874aea 4024 GlobalFree(clipdata);
f0df44da 4025
4026 if (!must_deselect)
32874aea 4027 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 4028}
4029
4eeb7d09 4030/*
4031 * Note: unlike write_aclip() this will not append a nul.
4032 */
a8327734 4033void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4eeb7d09 4034{
a7419ea4 4035 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 4036 int len2;
a7419ea4 4037 void *lock, *lock2, *lock3;
4eeb7d09 4038
4039 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4040
4041 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4042 len * sizeof(wchar_t));
4043 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4044
0e5e7f46 4045 if (!clipdata || !clipdata2) {
4eeb7d09 4046 if (clipdata)
4047 GlobalFree(clipdata);
4048 if (clipdata2)
4049 GlobalFree(clipdata2);
4050 return;
4051 }
4052 if (!(lock = GlobalLock(clipdata)))
4053 return;
4054 if (!(lock2 = GlobalLock(clipdata2)))
4055 return;
4056
4057 memcpy(lock, data, len * sizeof(wchar_t));
4058 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4059
a7419ea4 4060 if (cfg.rtf_paste) {
4061 wchar_t unitab[256];
4062 char *rtf = NULL;
4063 unsigned char *tdata = (unsigned char *)lock2;
4064 wchar_t *udata = (wchar_t *)lock;
4065 int rtflen = 0, uindex = 0, tindex = 0;
4066 int rtfsize = 0;
4067 int multilen, blen, alen, totallen, i;
4068 char before[16], after[4];
4069
4070 get_unitab(CP_ACP, unitab, 0);
4071
4072 rtfsize = 100 + strlen(cfg.font);
4073 rtf = smalloc(rtfsize);
4074 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4075 GetACP(), cfg.font);
4076 rtflen = strlen(rtf);
4077
4078 /*
4079 * We want to construct a piece of RTF that specifies the
4080 * same Unicode text. To do this we will read back in
4081 * parallel from the Unicode data in `udata' and the
4082 * non-Unicode data in `tdata'. For each character in
4083 * `tdata' which becomes the right thing in `udata' when
4084 * looked up in `unitab', we just copy straight over from
4085 * tdata. For each one that doesn't, we must WCToMB it
4086 * individually and produce a \u escape sequence.
4087 *
4088 * It would probably be more robust to just bite the bullet
4089 * and WCToMB each individual Unicode character one by one,
4090 * then MBToWC each one back to see if it was an accurate
4091 * translation; but that strikes me as a horrifying number
4092 * of Windows API calls so I want to see if this faster way
4093 * will work. If it screws up badly we can always revert to
4094 * the simple and slow way.
4095 */
4096 while (tindex < len2 && uindex < len &&
4097 tdata[tindex] && udata[uindex]) {
4098 if (tindex + 1 < len2 &&
4099 tdata[tindex] == '\r' &&
4100 tdata[tindex+1] == '\n') {
4101 tindex++;
4102 uindex++;
4103 }
4104 if (unitab[tdata[tindex]] == udata[uindex]) {
4105 multilen = 1;
4106 before[0] = '\0';
4107 after[0] = '\0';
4108 blen = alen = 0;
4109 } else {
4110 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4111 NULL, 0, NULL, NULL);
4112 if (multilen != 1) {
c3b032a6 4113 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4114 udata[uindex]);
a7419ea4 4115 alen = 1; strcpy(after, "}");
4116 } else {
4117 blen = sprintf(before, "\\u%d", udata[uindex]);
4118 alen = 0; after[0] = '\0';
4119 }
4120 }
4121 assert(tindex + multilen <= len2);
4122 totallen = blen + alen;
4123 for (i = 0; i < multilen; i++) {
4124 if (tdata[tindex+i] == '\\' ||
4125 tdata[tindex+i] == '{' ||
4126 tdata[tindex+i] == '}')
4127 totallen += 2;
4128 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4129 totallen += 6; /* \par\r\n */
4130 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4131 totallen += 4;
4132 else
4133 totallen++;
4134 }
4135
4136 if (rtfsize < rtflen + totallen + 3) {
4137 rtfsize = rtflen + totallen + 512;
4138 rtf = srealloc(rtf, rtfsize);
4139 }
4140
4141 strcpy(rtf + rtflen, before); rtflen += blen;
4142 for (i = 0; i < multilen; i++) {
4143 if (tdata[tindex+i] == '\\' ||
4144 tdata[tindex+i] == '{' ||
4145 tdata[tindex+i] == '}') {
4146 rtf[rtflen++] = '\\';
4147 rtf[rtflen++] = tdata[tindex+i];
4148 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4149 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4150 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4151 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4152 } else {
4153 rtf[rtflen++] = tdata[tindex+i];
4154 }
4155 }
4156 strcpy(rtf + rtflen, after); rtflen += alen;
4157
4158 tindex += multilen;
4159 uindex++;
4160 }
4161
4162 strcpy(rtf + rtflen, "}");
4163 rtflen += 2;
4164
4165 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4166 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4167 strcpy(lock3, rtf);
4168 GlobalUnlock(clipdata3);
4169 }
4170 sfree(rtf);
4171 } else
4172 clipdata3 = NULL;
4173
4eeb7d09 4174 GlobalUnlock(clipdata);
4175 GlobalUnlock(clipdata2);
4176
4177 if (!must_deselect)
4178 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4179
4180 if (OpenClipboard(hwnd)) {
4181 EmptyClipboard();
4182 SetClipboardData(CF_UNICODETEXT, clipdata);
4183 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4184 if (clipdata3)
4185 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4186 CloseClipboard();
4187 } else {
4188 GlobalFree(clipdata);
4189 GlobalFree(clipdata2);
4190 }
4191
4192 if (!must_deselect)
4193 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4194}
4195
a8327734 4196void get_clip(void *frontend, wchar_t ** p, int *len)
32874aea 4197{
374330e2 4198 static HGLOBAL clipdata = NULL;
4eeb7d09 4199 static wchar_t *converted = 0;
4200 wchar_t *p2;
374330e2 4201
4eeb7d09 4202 if (converted) {
4203 sfree(converted);
4204 converted = 0;
4205 }
374330e2 4206 if (!p) {
4207 if (clipdata)
32874aea 4208 GlobalUnlock(clipdata);
374330e2 4209 clipdata = NULL;
4210 return;
4eeb7d09 4211 } else if (OpenClipboard(NULL)) {
2d466ffd 4212 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4213 CloseClipboard();
4eeb7d09 4214 *p = GlobalLock(clipdata);
4215 if (*p) {
4216 for (p2 = *p; *p2; p2++);
4217 *len = p2 - *p;
4218 return;
374330e2 4219 }
2d466ffd 4220 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4221 char *s;
4222 int i;
4223 CloseClipboard();
4224 s = GlobalLock(clipdata);
4225 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4226 *p = converted = smalloc(i * sizeof(wchar_t));
4227 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4228 *len = i - 1;
4229 return;
4230 } else
4231 CloseClipboard();
374330e2 4232 }
4233
4234 *p = NULL;
4235 *len = 0;
4236}
4237
4eeb7d09 4238#if 0
374330e2 4239/*
4240 * Move `lines' lines from position `from' to position `to' in the
4241 * window.
4242 */
a8327734 4243void optimised_move(void *frontend, int to, int from, int lines)
32874aea 4244{
374330e2 4245 RECT r;
f67b4e85 4246 int min, max;
374330e2 4247
4248 min = (to < from ? to : from);
4249 max = to + from - min;
374330e2 4250
5a73255e 4251 r.left = offset_width;
887035a5 4252 r.right = offset_width + term->cols * font_width;
5a73255e 4253 r.top = offset_height + min * font_height;
4254 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4255 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4256}
4eeb7d09 4257#endif
374330e2 4258
4259/*
4260 * Print a message box and perform a fatal exit.
4261 */
32874aea 4262void fatalbox(char *fmt, ...)
4263{
374330e2 4264 va_list ap;
4265 char stuff[200];
4266
4267 va_start(ap, fmt);
4268 vsprintf(stuff, fmt, ap);
4269 va_end(ap);
4270 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
93b581bd 4271 cleanup_exit(1);
374330e2 4272}
4273
4274/*
1709795f 4275 * Print a modal (Really Bad) message box and perform a fatal exit.
4276 */
4277void modalfatalbox(char *fmt, ...)
4278{
4279 va_list ap;
4280 char stuff[200];
4281
4282 va_start(ap, fmt);
4283 vsprintf(stuff, fmt, ap);
4284 va_end(ap);
4285 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4286 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4287 cleanup_exit(1);
4288}
4289
4290/*
f8a28d1f 4291 * Manage window caption / taskbar flashing, if enabled.
4292 * 0 = stop, 1 = maintain, 2 = start
4293 */
4294static void flash_window(int mode)
4295{
4296 static long last_flash = 0;
4297 static int flashing = 0;
4298 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4299 /* stop */
4300 if (flashing) {
4301 FlashWindow(hwnd, FALSE);
4302 flashing = 0;
4303 }
4304
4305 } else if (mode == 2) {
4306 /* start */
4307 if (!flashing) {
4308 last_flash = GetTickCount();
4309 flashing = 1;
4310 FlashWindow(hwnd, TRUE);
4311 }
4312
4313 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4314 /* maintain */
4315 if (flashing) {
4316 long now = GetTickCount();
4317 long fdiff = now - last_flash;
4318 if (fdiff < 0 || fdiff > 450) {
4319 last_flash = now;
4320 FlashWindow(hwnd, TRUE); /* toggle */
4321 }
4322 }
4323 }
4324}
4325
4326/*
374330e2 4327 * Beep.
4328 */
a8327734 4329void beep(void *frontend, int mode)
32874aea 4330{
03169ad0 4331 if (mode == BELL_DEFAULT) {
eb04402e 4332 /*
4333 * For MessageBeep style bells, we want to be careful of
4334 * timing, because they don't have the nice property of
4335 * PlaySound bells that each one cancels the previous
4336 * active one. So we limit the rate to one per 50ms or so.
4337 */
4338 static long lastbeep = 0;
d51cdf1e 4339 long beepdiff;
eb04402e 4340
d51cdf1e 4341 beepdiff = GetTickCount() - lastbeep;
eb04402e 4342 if (beepdiff >= 0 && beepdiff < 50)
4343 return;
156686ef 4344 MessageBeep(MB_OK);
d51cdf1e 4345 /*
4346 * The above MessageBeep call takes time, so we record the
4347 * time _after_ it finishes rather than before it starts.
4348 */
4349 lastbeep = GetTickCount();
03169ad0 4350 } else if (mode == BELL_WAVEFILE) {
4351 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 4352 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 4353 sprintf(buf, "Unable to play sound file\n%s\n"
4354 "Using default sound instead", cfg.bell_wavefile);
32874aea 4355 MessageBox(hwnd, buf, "PuTTY Sound Error",
4356 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4357 cfg.beep = BELL_DEFAULT;
4358 }
4359 }
f8a28d1f 4360 /* Otherwise, either visual bell or disabled; do nothing here */
887035a5 4361 if (!term->has_focus) {
f8a28d1f 4362 flash_window(2); /* start */
4363 }
374330e2 4364}
8f57d753 4365
4366/*
68f9b3d9 4367 * Minimise or restore the window in response to a server-side
4368 * request.
4369 */
a8327734 4370void set_iconic(void *frontend, int iconic)
68f9b3d9 4371{
4372 if (IsIconic(hwnd)) {
4373 if (!iconic)
4374 ShowWindow(hwnd, SW_RESTORE);
4375 } else {
4376 if (iconic)
4377 ShowWindow(hwnd, SW_MINIMIZE);
4378 }
4379}
4380
4381/*
4382 * Move the window in response to a server-side request.
4383 */
a8327734 4384void move_window(void *frontend, int x, int y)
68f9b3d9 4385{
d1e0a352 4386 if (cfg.resize_action == RESIZE_DISABLED ||
4387 cfg.resize_action == RESIZE_FONT ||
4388 IsZoomed(hwnd))
4389 return;
4390
68f9b3d9 4391 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4392}
4393
4394/*
4395 * Move the window to the top or bottom of the z-order in response
4396 * to a server-side request.
4397 */
a8327734 4398void set_zorder(void *frontend, int top)
68f9b3d9 4399{
1ba99d2c 4400 if (cfg.alwaysontop)
68f9b3d9 4401 return; /* ignore */
4402 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4403 SWP_NOMOVE | SWP_NOSIZE);
4404}
4405
4406/*
4407 * Refresh the window in response to a server-side request.
4408 */
a8327734 4409void refresh_window(void *frontend)
68f9b3d9 4410{
4411 InvalidateRect(hwnd, NULL, TRUE);
4412}
4413
4414/*
4415 * Maximise or restore the window in response to a server-side
4416 * request.
4417 */
a8327734 4418void set_zoomed(void *frontend, int zoomed)
68f9b3d9 4419{
1ba99d2c 4420 if (IsZoomed(hwnd)) {
4421 if (!zoomed)
4422 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4423 } else {
4424 if (zoomed)
4425 ShowWindow(hwnd, SW_MAXIMIZE);
4426 }
4427}
4428
4429/*
4430 * Report whether the window is iconic, for terminal reports.
4431 */
a8327734 4432int is_iconic(void *frontend)
68f9b3d9 4433{
4434 return IsIconic(hwnd);
4435}
4436
4437/*
4438 * Report the window's position, for terminal reports.
4439 */
a8327734 4440void get_window_pos(void *frontend, int *x, int *y)
68f9b3d9 4441{
4442 RECT r;
4443 GetWindowRect(hwnd, &r);
4444 *x = r.left;
4445 *y = r.top;
4446}
4447
4448/*
4449 * Report the window's pixel size, for terminal reports.
4450 */
a8327734 4451void get_window_pixels(void *frontend, int *x, int *y)
68f9b3d9 4452{
4453 RECT r;
4454 GetWindowRect(hwnd, &r);
4455 *x = r.right - r.left;
4456 *y = r.bottom - r.top;
4457}
4458
4459/*
4460 * Return the window or icon title.
4461 */
a8327734 4462char *get_window_title(void *frontend, int icon)
68f9b3d9 4463{
4464 return icon ? icon_name : window_name;
4465}
1ba99d2c 4466
4467/*
4468 * See if we're in full-screen mode.
4469 */
4470int is_full_screen()
4471{
4472 if (!IsZoomed(hwnd))
4473 return FALSE;
4474 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4475 return FALSE;
4476 return TRUE;
4477}
4478
d0f1bcb4 4479/* Get the rect/size of a full screen window using the nearest available
4480 * monitor in multimon systems; default to something sensible if only
4481 * one monitor is present. */
4482static int get_fullscreen_rect(RECT * ss)
4483{
4484#ifdef MONITOR_DEFAULTTONEAREST
4485 HMONITOR mon;
4486 MONITORINFO mi;
4487 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4488 mi.cbSize = sizeof(mi);
4489 GetMonitorInfo(mon, &mi);
4490
4491 /* structure copy */
4492 *ss = mi.rcMonitor;
4493 return TRUE;
4494#else
4495/* could also use code like this:
4496 ss->left = ss->top = 0;
4497 ss->right = GetSystemMetrics(SM_CXSCREEN);
4498 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4499*/
e6f8202f 4500 return GetClientRect(GetDesktopWindow(), ss);
d0f1bcb4 4501#endif
4502}
4503
4504
1ba99d2c 4505/*
4506 * Go full-screen. This should only be called when we are already
4507 * maximised.
4508 */
4509void make_full_screen()
4510{
4511 DWORD style;
d0f1bcb4 4512 RECT ss;
1ba99d2c 4513
4514 assert(IsZoomed(hwnd));
4515
d0f1bcb4 4516 if (is_full_screen())
4517 return;
4518
1ba99d2c 4519 /* Remove the window furniture. */
4520 style = GetWindowLong(hwnd, GWL_STYLE);
4521 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4522 if (cfg.scrollbar_in_fullscreen)
4523 style |= WS_VSCROLL;
4524 else
4525 style &= ~WS_VSCROLL;
4526 SetWindowLong(hwnd, GWL_STYLE, style);
4527
4528 /* Resize ourselves to exactly cover the nearest monitor. */
d0f1bcb4 4529 get_fullscreen_rect(&ss);
4530 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4531 ss.right - ss.left,
4532 ss.bottom - ss.top,
4533 SWP_FRAMECHANGED);
1ba99d2c 4534
4535 /* Tick the menu item in the System menu. */
4536 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4537 MF_CHECKED);
4538}
4539
4540/*
4541 * Clear the full-screen attributes.
4542 */
4543void clear_full_screen()
4544{
4545 DWORD oldstyle, style;
4546
4547 /* Reinstate the window furniture. */
4548 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4549 style |= WS_CAPTION | WS_BORDER;
4550 if (cfg.resize_action == RESIZE_DISABLED)
4551 style &= ~WS_THICKFRAME;
4552 else
4553 style |= WS_THICKFRAME;
1ba99d2c 4554 if (cfg.scrollbar)
4555 style |= WS_VSCROLL;
4556 else
4557 style &= ~WS_VSCROLL;
4558 if (style != oldstyle) {
4559 SetWindowLong(hwnd, GWL_STYLE, style);
4560 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4561 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4562 SWP_FRAMECHANGED);
4563 }
4564
4565 /* Untick the menu item in the System menu. */
4566 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4567 MF_UNCHECKED);
4568}
4569
4570/*
4571 * Toggle full-screen mode.
4572 */
4573void flip_full_screen()
4574{
4575 if (is_full_screen()) {
4576 ShowWindow(hwnd, SW_RESTORE);
4577 } else if (IsZoomed(hwnd)) {
4578 make_full_screen();
4579 } else {
4580 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4581 ShowWindow(hwnd, SW_MAXIMIZE);
4582 }
4583}
ab2c86b9 4584
b9d7bcad 4585void frontend_keypress(void *handle)
ab2c86b9 4586{
4587 /*
4588 * Keypress termination in non-Close-On-Exit mode is not
4589 * currently supported in PuTTY proper, because the window
4590 * always has a perfectly good Close button anyway. So we do
4591 * nothing here.
4592 */
4593 return;
4594}