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