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