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