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