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