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