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