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