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