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