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