request_resize() needs to call term_size() to tell the terminal emulator
[u/mdw/putty] / window.c
CommitLineData
374330e2 1#include <windows.h>
49bad831 2#include <imm.h>
374330e2 3#include <commctrl.h>
a7419ea4 4#include <richedit.h>
f0a70059 5#include <mmsystem.h>
4d331a77 6#ifndef AUTO_WINSOCK
7#ifdef WINSOCK_TWO
8#include <winsock2.h>
9#else
374330e2 10#include <winsock.h>
4d331a77 11#endif
12#endif
a401e5f3 13
9976dbbc 14#ifndef NO_MULTIMON
1ba99d2c 15#if WINVER < 0x0500
16#define COMPILE_MULTIMON_STUBS
17#include <multimon.h>
18#endif
9976dbbc 19#endif
1ba99d2c 20
374330e2 21#include <stdio.h>
22#include <stdlib.h>
1d470ad2 23#include <ctype.h>
ec55b220 24#include <time.h>
a7419ea4 25#include <assert.h>
374330e2 26
32874aea 27#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 28#include "putty.h"
887035a5 29#include "terminal.h"
8c3cd914 30#include "winstuff.h"
d5859615 31#include "storage.h"
374330e2 32#include "win_res.h"
33
6833a413 34#define IDM_SHOWLOG 0x0010
35#define IDM_NEWSESS 0x0020
36#define IDM_DUPSESS 0x0030
37#define IDM_RECONF 0x0040
38#define IDM_CLRSB 0x0050
39#define IDM_RESET 0x0060
40#define IDM_TEL_AYT 0x0070
41#define IDM_TEL_BRK 0x0080
42#define IDM_TEL_SYNCH 0x0090
43#define IDM_TEL_EC 0x00a0
44#define IDM_TEL_EL 0x00b0
45#define IDM_TEL_GA 0x00c0
46#define IDM_TEL_NOP 0x00d0
47#define IDM_TEL_ABORT 0x00e0
48#define IDM_TEL_AO 0x00f0
49#define IDM_TEL_IP 0x0100
50#define IDM_TEL_SUSP 0x0110
51#define IDM_TEL_EOR 0x0120
52#define IDM_TEL_EOF 0x0130
70133c0e 53#define IDM_HELP 0x0140
54#define IDM_ABOUT 0x0150
55#define IDM_SAVEDSESS 0x0160
56#define IDM_COPYALL 0x0170
57#define IDM_FULLSCREEN 0x0180
6833a413 58
32874aea 59#define IDM_SESSLGP 0x0250 /* log type printable */
60#define IDM_SESSLGA 0x0260 /* log type all chars */
61#define IDM_SESSLGE 0x0270 /* log end */
6833a413 62#define IDM_SAVED_MIN 0x1000
63#define IDM_SAVED_MAX 0x2000
374330e2 64
59ad2c03 65#define WM_IGNORE_CLIP (WM_XUSER + 2)
1ba99d2c 66#define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
374330e2 67
3cf144db 68/* Needed for Chinese support and apparently not always defined. */
69#ifndef VK_PROCESSKEY
70#define VK_PROCESSKEY 0xE5
71#endif
72
3f0f9388 73/* Mouse wheel support. */
5eaf1e06 74#ifndef WM_MOUSEWHEEL
3f0f9388 75#define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
76#endif
77#ifndef WHEEL_DELTA
78#define WHEEL_DELTA 120
5eaf1e06 79#endif
80
32874aea 81static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
82static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
83 unsigned char *output);
374330e2 84static void cfgtopalette(void);
85static void init_palette(void);
5a73255e 86static void init_fonts(int, int);
4eeb7d09 87static void another_font(int);
88static void deinit_fonts(void);
a5ce2e9f 89static void set_input_locale(HKL);
374330e2 90
1ba99d2c 91static int is_full_screen(void);
92static void make_full_screen(void);
93static void clear_full_screen(void);
94static void flip_full_screen(void);
95
5a73255e 96/* Window layout information */
97static void reset_window(int);
a401e5f3 98static int extra_width, extra_height;
5a73255e 99static int font_width, font_height, font_dualwidth;
100static int offset_width, offset_height;
101static int was_zoomed = 0;
102static int prev_rows, prev_cols;
103
59ad2c03 104static int pending_netevent = 0;
105static WPARAM pend_netevent_wParam = 0;
106static LPARAM pend_netevent_lParam = 0;
107static void enact_pending_netevent(void);
f8a28d1f 108static void flash_window(int mode);
c1da5066 109static void sys_cursor_update(void);
b9d7bcad 110static int is_shift_pressed(void);
d0f1bcb4 111static int get_fullscreen_rect(RECT * ss);
59ad2c03 112
ec55b220 113static time_t last_movement = 0;
114
c1da5066 115static int caret_x = -1, caret_y = -1;
116
0b4f0bc0 117static int kbd_codepage;
118
b9d7bcad 119static void *ldisc;
6b78788a 120static Backend *back;
121static void *backhandle;
b9d7bcad 122
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 */
a59221b9 1785 back->reconfig(backhandle, &cfg);
86916870 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 }
153580da 3293 if (wParam == VK_PRIOR && shift_state == 2) {
3294 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3295 return 0;
3296 }
c9def1b8 3297 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 3298 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3299 return 0;
3300 }
153580da 3301 if (wParam == VK_NEXT && shift_state == 2) {
3302 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3303 return 0;
3304 }
32874aea 3305 if (wParam == VK_INSERT && shift_state == 1) {
887035a5 3306 term_do_paste(term);
32874aea 3307 return 0;
3308 }
c9def1b8 3309 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 3310 return -1;
c9def1b8 3311 }
3312 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 3313 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3314 return -1;
c9def1b8 3315 }
2cae8529 3316 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3317 (cfg.resize_action != RESIZE_DISABLED)) {
0ed2f48e 3318 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3319 flip_full_screen();
8f57d753 3320 return -1;
3321 }
ec55b220 3322 /* Control-Numlock for app-keypad mode switch */
3323 if (wParam == VK_PAUSE && shift_state == 2) {
887035a5 3324 term->app_keypad_keys ^= 1;
ec55b220 3325 return 0;
3326 }
374330e2 3327
c9def1b8 3328 /* Nethack keypad */
3329 if (cfg.nethack_keypad && !left_alt) {
32874aea 3330 switch (wParam) {
3331 case VK_NUMPAD1:
3332 *p++ = shift_state ? 'B' : 'b';
3333 return p - output;
3334 case VK_NUMPAD2:
3335 *p++ = shift_state ? 'J' : 'j';
3336 return p - output;
3337 case VK_NUMPAD3:
3338 *p++ = shift_state ? 'N' : 'n';
3339 return p - output;
3340 case VK_NUMPAD4:
3341 *p++ = shift_state ? 'H' : 'h';
3342 return p - output;
3343 case VK_NUMPAD5:
3344 *p++ = shift_state ? '.' : '.';
3345 return p - output;
3346 case VK_NUMPAD6:
3347 *p++ = shift_state ? 'L' : 'l';
3348 return p - output;
3349 case VK_NUMPAD7:
3350 *p++ = shift_state ? 'Y' : 'y';
3351 return p - output;
3352 case VK_NUMPAD8:
3353 *p++ = shift_state ? 'K' : 'k';
3354 return p - output;
3355 case VK_NUMPAD9:
3356 *p++ = shift_state ? 'U' : 'u';
3357 return p - output;
3358 }
c9def1b8 3359 }
3360
3361 /* Application Keypad */
3362 if (!left_alt) {
32874aea 3363 int xkey = 0;
3364
3365 if (cfg.funky_type == 3 ||
3366 (cfg.funky_type <= 1 &&
887035a5 3367 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
32874aea 3368 case VK_EXECUTE:
3369 xkey = 'P';
3370 break;
3371 case VK_DIVIDE:
3372 xkey = 'Q';
3373 break;
3374 case VK_MULTIPLY:
3375 xkey = 'R';
3376 break;
3377 case VK_SUBTRACT:
3378 xkey = 'S';
3379 break;
3380 }
887035a5 3381 if (term->app_keypad_keys && !cfg.no_applic_k)
32874aea 3382 switch (wParam) {
3383 case VK_NUMPAD0:
3384 xkey = 'p';
3385 break;
3386 case VK_NUMPAD1:
3387 xkey = 'q';
3388 break;
3389 case VK_NUMPAD2:
3390 xkey = 'r';
3391 break;
3392 case VK_NUMPAD3:
3393 xkey = 's';
3394 break;
3395 case VK_NUMPAD4:
3396 xkey = 't';
3397 break;
3398 case VK_NUMPAD5:
3399 xkey = 'u';
3400 break;
3401 case VK_NUMPAD6:
3402 xkey = 'v';
3403 break;
3404 case VK_NUMPAD7:
3405 xkey = 'w';
3406 break;
3407 case VK_NUMPAD8:
3408 xkey = 'x';
3409 break;
3410 case VK_NUMPAD9:
3411 xkey = 'y';
3412 break;
3413
3414 case VK_DECIMAL:
3415 xkey = 'n';
3416 break;
3417 case VK_ADD:
3418 if (cfg.funky_type == 2) {
3419 if (shift_state)
3420 xkey = 'l';
3421 else
3422 xkey = 'k';
3423 } else if (shift_state)
3424 xkey = 'm';
c9def1b8 3425 else
32874aea 3426 xkey = 'l';
3427 break;
3428
3429 case VK_DIVIDE:
3430 if (cfg.funky_type == 2)
3431 xkey = 'o';
3432 break;
3433 case VK_MULTIPLY:
3434 if (cfg.funky_type == 2)
3435 xkey = 'j';
3436 break;
3437 case VK_SUBTRACT:
3438 if (cfg.funky_type == 2)
3439 xkey = 'm';
3440 break;
3441
3442 case VK_RETURN:
3443 if (HIWORD(lParam) & KF_EXTENDED)
3444 xkey = 'M';
3445 break;
c9def1b8 3446 }
32874aea 3447 if (xkey) {
887035a5 3448 if (term->vt52_mode) {
32874aea 3449 if (xkey >= 'P' && xkey <= 'S')
3450 p += sprintf((char *) p, "\x1B%c", xkey);
3451 else
3452 p += sprintf((char *) p, "\x1B?%c", xkey);
3453 } else
3454 p += sprintf((char *) p, "\x1BO%c", xkey);
3455 return p - output;
c9def1b8 3456 }
3457 }
3458
32874aea 3459 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3460 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3461 *p++ = 0;
3462 return -2;
c9def1b8 3463 }
e8e8d6e2 3464 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3465 /* We do the opposite of what is configured */
3466 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3467 *p++ = 0;
3468 return -2;
3469 }
32874aea 3470 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3471 *p++ = 0x1B;
3472 *p++ = '[';
3473 *p++ = 'Z';
3474 return p - output;
c9def1b8 3475 }
32874aea 3476 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3477 *p++ = 0;
3478 return p - output;
c9def1b8 3479 }
32874aea 3480 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3481 *p++ = 160;
3482 return p - output;
c9def1b8 3483 }
32874aea 3484 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3485 *p++ = 3;
a5f3e637 3486 *p++ = 0;
3487 return -2;
c9def1b8 3488 }
32874aea 3489 if (wParam == VK_PAUSE) { /* Break/Pause */
3490 *p++ = 26;
3491 *p++ = 0;
3492 return -2;
95bbe1ae 3493 }
c9def1b8 3494 /* Control-2 to Control-8 are special */
32874aea 3495 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3496 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3497 return p - output;
3498 }
237f2b6e 3499 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
c9def1b8 3500 *p++ = 0x1F;
3501 return p - output;
3502 }
3503 if (shift_state == 2 && wParam == 0xDF) {
3504 *p++ = 0x1C;
3505 return p - output;
3506 }
887035a5 3507 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
32874aea 3508 *p++ = '\r';
3509 *p++ = '\n';
c9def1b8 3510 return p - output;
3511 }
374330e2 3512
c5e9c988 3513 /*
c9def1b8 3514 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3515 * for integer decimal nn.)
3516 *
3517 * We also deal with the weird ones here. Linux VCs replace F1
3518 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3519 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3520 * respectively.
c5e9c988 3521 */
c9def1b8 3522 code = 0;
3523 switch (wParam) {
32874aea 3524 case VK_F1:
3525 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3526 break;
3527 case VK_F2:
3528 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3529 break;
3530 case VK_F3:
3531 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3532 break;
3533 case VK_F4:
3534 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3535 break;
3536 case VK_F5:
3537 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3538 break;
3539 case VK_F6:
3540 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3541 break;
3542 case VK_F7:
3543 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3544 break;
3545 case VK_F8:
3546 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3547 break;
3548 case VK_F9:
3549 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3550 break;
3551 case VK_F10:
3552 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3553 break;
3554 case VK_F11:
3555 code = 23;
3556 break;
3557 case VK_F12:
3558 code = 24;
3559 break;
3560 case VK_F13:
3561 code = 25;
3562 break;
3563 case VK_F14:
3564 code = 26;
3565 break;
3566 case VK_F15:
3567 code = 28;
3568 break;
3569 case VK_F16:
3570 code = 29;
3571 break;
3572 case VK_F17:
3573 code = 31;
3574 break;
3575 case VK_F18:
3576 code = 32;
3577 break;
3578 case VK_F19:
3579 code = 33;
3580 break;
3581 case VK_F20:
3582 code = 34;
3583 break;
dfca2656 3584 }
3585 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3586 case VK_HOME:
3587 code = 1;
3588 break;
3589 case VK_INSERT:
3590 code = 2;
3591 break;
3592 case VK_DELETE:
3593 code = 3;
3594 break;
3595 case VK_END:
3596 code = 4;
3597 break;
3598 case VK_PRIOR:
3599 code = 5;
3600 break;
3601 case VK_NEXT:
3602 code = 6;
3603 break;
374330e2 3604 }
ec55b220 3605 /* Reorder edit keys to physical order */
32874aea 3606 if (cfg.funky_type == 3 && code <= 6)
3607 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3608
887035a5 3609 if (term->vt52_mode && code > 0 && code <= 6) {
32874aea 3610 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3611 return p - output;
3612 }
3613
9bc81a2c 3614 if (cfg.funky_type == 5 && /* SCO function keys */
3615 code >= 11 && code <= 34) {
e24b1972 3616 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3617 int index = 0;
3618 switch (wParam) {
3619 case VK_F1: index = 0; break;
3620 case VK_F2: index = 1; break;
3621 case VK_F3: index = 2; break;
3622 case VK_F4: index = 3; break;
3623 case VK_F5: index = 4; break;
3624 case VK_F6: index = 5; break;
3625 case VK_F7: index = 6; break;
3626 case VK_F8: index = 7; break;
3627 case VK_F9: index = 8; break;
3628 case VK_F10: index = 9; break;
3629 case VK_F11: index = 10; break;
3630 case VK_F12: index = 11; break;
3631 }
3632 if (keystate[VK_SHIFT] & 0x80) index += 12;
3633 if (keystate[VK_CONTROL] & 0x80) index += 24;
3634 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3635 return p - output;
3636 }
9bc81a2c 3637 if (cfg.funky_type == 5 && /* SCO small keypad */
3638 code >= 1 && code <= 6) {
3639 char codes[] = "HL.FIG";
3640 if (code == 3) {
3641 *p++ = '\x7F';
3642 } else {
3643 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3644 }
3645 return p - output;
3646 }
887035a5 3647 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
f37caa11 3648 int offt = 0;
32874aea 3649 if (code > 15)
3650 offt++;
3651 if (code > 21)
3652 offt++;
887035a5 3653 if (term->vt52_mode)
32874aea 3654 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3655 else
32874aea 3656 p +=
3657 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3658 return p - output;
3659 }
c9def1b8 3660 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3661 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3662 return p - output;
3663 }
3664 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
887035a5 3665 if (term->vt52_mode)
32874aea 3666 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3667 else
32874aea 3668 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3669 return p - output;
3670 }
3671 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3672 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3673 return p - output;
3674 }
3675 if (code) {
32874aea 3676 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3677 return p - output;
374330e2 3678 }
45dabbc5 3679
c9def1b8 3680 /*
3681 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3682 * some reason seems to send VK_CLEAR to Windows...).
3683 */
3684 {
3685 char xkey = 0;
3686 switch (wParam) {
32874aea 3687 case VK_UP:
3688 xkey = 'A';
3689 break;
3690 case VK_DOWN:
3691 xkey = 'B';
3692 break;
3693 case VK_RIGHT:
3694 xkey = 'C';
3695 break;
3696 case VK_LEFT:
3697 xkey = 'D';
3698 break;
3699 case VK_CLEAR:
3700 xkey = 'G';
3701 break;
c9def1b8 3702 }
32874aea 3703 if (xkey) {
887035a5 3704 if (term->vt52_mode)
32874aea 3705 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3706 else {
887035a5 3707 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3a953121 3708#if 0
3709 /*
3710 * RDB: VT100 & VT102 manuals both state the
3711 * app cursor keys only work if the app keypad
3712 * is on.
3713 *
3714 * SGT: That may well be true, but xterm
3715 * disagrees and so does at least one
3716 * application, so I've #if'ed this out and the
3717 * behaviour is back to PuTTY's original: app
3718 * cursor and app keypad are independently
3719 * switchable modes. If anyone complains about
3720 * _this_ I'll have to put in a configurable
3721 * option.
e864f84f 3722 */
887035a5 3723 if (!term->app_keypad_keys)
e864f84f 3724 app_flg = 0;
3a953121 3725#endif
e864f84f 3726 /* Useful mapping of Ctrl-arrows */
3727 if (shift_state == 2)
3728 app_flg = !app_flg;
3729
3730 if (app_flg)
3731 p += sprintf((char *) p, "\x1BO%c", xkey);
3732 else
3733 p += sprintf((char *) p, "\x1B[%c", xkey);
3734 }
c9def1b8 3735 return p - output;
3736 }
3737 }
0c50ef57 3738
3739 /*
3740 * Finally, deal with Return ourselves. (Win95 seems to
3741 * foul it up when Alt is pressed, for some reason.)
3742 */
32874aea 3743 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3744 *p++ = 0x0D;
a5f3e637 3745 *p++ = 0;
3746 return -2;
0c50ef57 3747 }
4eeb7d09 3748
3749 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3750 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3751 else
3752 alt_sum = 0;
67c339f7 3753 }
374330e2 3754
c9def1b8 3755 /* Okay we've done everything interesting; let windows deal with
3756 * the boring stuff */
3757 {
a9c02454 3758 BOOL capsOn=0;
3759
3760 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3761 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3762 capsOn= !left_alt;
3763 keystate[VK_CAPITAL] = 0;
3764 }
3765
00e3ba0f 3766 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3767#ifdef SHOW_TOASCII_RESULT
3768 if (r == 1 && !key_down) {
3769 if (alt_sum) {
887035a5 3770 if (in_utf(term) || dbcs_screenfont)
4eeb7d09 3771 debug((", (U+%04x)", alt_sum));
3772 else
3773 debug((", LCH(%d)", alt_sum));
3774 } else {
3775 debug((", ACH(%d)", keys[0]));
3776 }
3777 } else if (r > 0) {
3778 int r1;
3779 debug((", ASC("));
3780 for (r1 = 0; r1 < r; r1++) {
3781 debug(("%s%d", r1 ? "," : "", keys[r1]));
3782 }
3783 debug((")"));
3784 }
3785#endif
32874aea 3786 if (r > 0) {
4eeb7d09 3787 WCHAR keybuf;
256cb87c 3788
3789 /*
3790 * Interrupt an ongoing paste. I'm not sure this is
3791 * sensible, but for the moment it's preferable to
3792 * having to faff about buffering things.
3793 */
887035a5 3794 term_nopaste(term);
256cb87c 3795
c9def1b8 3796 p = output;
32874aea 3797 for (i = 0; i < r; i++) {
3798 unsigned char ch = (unsigned char) keys[i];
14963b8f 3799
32874aea 3800 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3801 compose_char = ch;
32874aea 3802 compose_state++;
c9def1b8 3803 continue;
3804 }
32874aea 3805 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3806 int nc;
3807 compose_state = 0;
3808
32874aea 3809 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3810 MessageBeep(MB_ICONHAND);
c9def1b8 3811 return 0;
3812 }
4eeb7d09 3813 keybuf = nc;
887035a5 3814 term_seen_key_event(term);
b9d7bcad 3815 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 3816 continue;
c9def1b8 3817 }
374330e2 3818
c9def1b8 3819 compose_state = 0;
374330e2 3820
4eeb7d09 3821 if (!key_down) {
3822 if (alt_sum) {
887035a5 3823 if (in_utf(term) || dbcs_screenfont) {
4eeb7d09 3824 keybuf = alt_sum;
887035a5 3825 term_seen_key_event(term);
b9d7bcad 3826 luni_send(ldisc, &keybuf, 1, 1);
4eeb7d09 3827 } else {
3828 ch = (char) alt_sum;
5471d09a 3829 /*
3830 * We need not bother about stdin
3831 * backlogs here, because in GUI PuTTY
3832 * we can't do anything about it
3833 * anyway; there's no means of asking
3834 * Windows to hold off on KEYDOWN
3835 * messages. We _have_ to buffer
3836 * everything we're sent.
3837 */
887035a5 3838 term_seen_key_event(term);
b9d7bcad 3839 ldisc_send(ldisc, &ch, 1, 1);
4eeb7d09 3840 }
3841 alt_sum = 0;
3842 } else
887035a5 3843 term_seen_key_event(term);
b9d7bcad 3844 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
4eeb7d09 3845 } else {
a9c02454 3846 if(capsOn && ch < 0x80) {
3847 WCHAR cbuf[2];
3848 cbuf[0] = 27;
3849 cbuf[1] = xlat_uskbd2cyrllic(ch);
887035a5 3850 term_seen_key_event(term);
b9d7bcad 3851 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3852 } else {
3853 char cbuf[2];
3854 cbuf[0] = '\033';
3855 cbuf[1] = ch;
887035a5 3856 term_seen_key_event(term);
b9d7bcad 3857 lpage_send(ldisc, kbd_codepage,
3858 cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3859 }
c9def1b8 3860 }
bca9517a 3861 show_mouseptr(0);
c9def1b8 3862 }
374330e2 3863
c9def1b8 3864 /* This is so the ALT-Numpad and dead keys work correctly. */
3865 keys[0] = 0;
3866
32874aea 3867 return p - output;
c9def1b8 3868 }
159eba53 3869 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3870 if (!left_alt)
3871 keys[0] = 0;
4eeb7d09 3872 /* If we will be using alt_sum fix the 256s */
887035a5 3873 else if (keys[0] && (in_utf(term) || dbcs_screenfont))
4eeb7d09 3874 keys[0] = 10;
374330e2 3875 }
3876
dfca2656 3877 /*
3878 * ALT alone may or may not want to bring up the System menu.
3879 * If it's not meant to, we return 0 on presses or releases of
3880 * ALT, to show that we've swallowed the keystroke. Otherwise
3881 * we return -1, which means Windows will give the keystroke
3882 * its default handling (i.e. bring up the System menu).
3883 */
3884 if (wParam == VK_MENU && !cfg.alt_only)
3885 return 0;
374330e2 3886
c9def1b8 3887 return -1;
374330e2 3888}
3889
a8327734 3890void request_paste(void *frontend)
e6346999 3891{
3892 /*
3893 * In Windows, pasting is synchronous: we can read the
3894 * clipboard with no difficulty, so request_paste() can just go
3895 * ahead and paste.
3896 */
887035a5 3897 term_do_paste(term);
e6346999 3898}
3899
a8327734 3900void set_title(void *frontend, char *title)
32874aea 3901{
3902 sfree(window_name);
3903 window_name = smalloc(1 + strlen(title));
3904 strcpy(window_name, title);
37508af4 3905 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3906 SetWindowText(hwnd, title);
374330e2 3907}
3908
a8327734 3909void set_icon(void *frontend, char *title)
32874aea 3910{
3911 sfree(icon_name);
3912 icon_name = smalloc(1 + strlen(title));
3913 strcpy(icon_name, title);
37508af4 3914 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3915 SetWindowText(hwnd, title);
374330e2 3916}
3917
a8327734 3918void set_sbar(void *frontend, int total, int start, int page)
32874aea 3919{
374330e2 3920 SCROLLINFO si;
c9def1b8 3921
1ba99d2c 3922 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
32874aea 3923 return;
c9def1b8 3924
374330e2 3925 si.cbSize = sizeof(si);
c9def1b8 3926 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3927 si.nMin = 0;
3928 si.nMax = total - 1;
3929 si.nPage = page;
3930 si.nPos = start;
c1f5f956 3931 if (hwnd)
32874aea 3932 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3933}
3934
a8327734 3935Context get_ctx(void *frontend)
32874aea 3936{
374330e2 3937 HDC hdc;
3938 if (hwnd) {
32874aea 3939 hdc = GetDC(hwnd);
374330e2 3940 if (hdc && pal)
32874aea 3941 SelectPalette(hdc, pal, FALSE);
374330e2 3942 return hdc;
3943 } else
3944 return NULL;
3945}
3946
32874aea 3947void free_ctx(Context ctx)
3948{
3949 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3950 ReleaseDC(hwnd, ctx);
374330e2 3951}
3952
32874aea 3953static void real_palette_set(int n, int r, int g, int b)
3954{
374330e2 3955 if (pal) {
3956 logpal->palPalEntry[n].peRed = r;
3957 logpal->palPalEntry[n].peGreen = g;
3958 logpal->palPalEntry[n].peBlue = b;
3959 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3960 colours[n] = PALETTERGB(r, g, b);
32874aea 3961 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3962 } else
3963 colours[n] = RGB(r, g, b);
3964}
3965
a8327734 3966void palette_set(void *frontend, int n, int r, int g, int b)
32874aea 3967{
374330e2 3968 static const int first[21] = {
3969 0, 2, 4, 6, 8, 10, 12, 14,
3970 1, 3, 5, 7, 9, 11, 13, 15,
3971 16, 17, 18, 20, 22
3972 };
32874aea 3973 real_palette_set(first[n], r, g, b);
374330e2 3974 if (first[n] >= 18)
32874aea 3975 real_palette_set(first[n] + 1, r, g, b);
374330e2 3976 if (pal) {
a8327734 3977 HDC hdc = get_ctx(frontend);
32874aea 3978 UnrealizeObject(pal);
3979 RealizePalette(hdc);
3980 free_ctx(hdc);
374330e2 3981 }
3982}
3983
a8327734 3984void palette_reset(void *frontend)
32874aea 3985{
374330e2 3986 int i;
3987
3988 for (i = 0; i < NCOLOURS; i++) {
3989 if (pal) {
3990 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3991 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3992 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3993 logpal->palPalEntry[i].peFlags = 0;
3994 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3995 defpal[i].rgbtGreen,
3996 defpal[i].rgbtBlue);
3997 } else
3998 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3999 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 4000 }
4001
4002 if (pal) {
4003 HDC hdc;
32874aea 4004 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
a8327734 4005 hdc = get_ctx(frontend);
32874aea 4006 RealizePalette(hdc);
4007 free_ctx(hdc);
374330e2 4008 }
4009}
4010
a8327734 4011void write_aclip(void *frontend, char *data, int len, int must_deselect)
32874aea 4012{
374330e2 4013 HGLOBAL clipdata;
4014 void *lock;
4015
32874aea 4016 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 4017 if (!clipdata)
4018 return;
32874aea 4019 lock = GlobalLock(clipdata);
374330e2 4020 if (!lock)
4021 return;
32874aea 4022 memcpy(lock, data, len);
4023 ((unsigned char *) lock)[len] = 0;
4024 GlobalUnlock(clipdata);
374330e2 4025
f0df44da 4026 if (!must_deselect)
32874aea 4027 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 4028
32874aea 4029 if (OpenClipboard(hwnd)) {
374330e2 4030 EmptyClipboard();
32874aea 4031 SetClipboardData(CF_TEXT, clipdata);
374330e2 4032 CloseClipboard();
4033 } else
32874aea 4034 GlobalFree(clipdata);
f0df44da 4035
4036 if (!must_deselect)
32874aea 4037 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 4038}
4039
4eeb7d09 4040/*
4041 * Note: unlike write_aclip() this will not append a nul.
4042 */
a8327734 4043void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4eeb7d09 4044{
a7419ea4 4045 HGLOBAL clipdata, clipdata2, clipdata3;
4eeb7d09 4046 int len2;
a7419ea4 4047 void *lock, *lock2, *lock3;
4eeb7d09 4048
4049 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4050
4051 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4052 len * sizeof(wchar_t));
4053 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4054
0e5e7f46 4055 if (!clipdata || !clipdata2) {
4eeb7d09 4056 if (clipdata)
4057 GlobalFree(clipdata);
4058 if (clipdata2)
4059 GlobalFree(clipdata2);
4060 return;
4061 }
4062 if (!(lock = GlobalLock(clipdata)))
4063 return;
4064 if (!(lock2 = GlobalLock(clipdata2)))
4065 return;
4066
4067 memcpy(lock, data, len * sizeof(wchar_t));
4068 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4069
a7419ea4 4070 if (cfg.rtf_paste) {
4071 wchar_t unitab[256];
4072 char *rtf = NULL;
4073 unsigned char *tdata = (unsigned char *)lock2;
4074 wchar_t *udata = (wchar_t *)lock;
4075 int rtflen = 0, uindex = 0, tindex = 0;
4076 int rtfsize = 0;
4077 int multilen, blen, alen, totallen, i;
4078 char before[16], after[4];
4079
4080 get_unitab(CP_ACP, unitab, 0);
4081
4082 rtfsize = 100 + strlen(cfg.font);
4083 rtf = smalloc(rtfsize);
4084 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4085 GetACP(), cfg.font);
4086 rtflen = strlen(rtf);
4087
4088 /*
4089 * We want to construct a piece of RTF that specifies the
4090 * same Unicode text. To do this we will read back in
4091 * parallel from the Unicode data in `udata' and the
4092 * non-Unicode data in `tdata'. For each character in
4093 * `tdata' which becomes the right thing in `udata' when
4094 * looked up in `unitab', we just copy straight over from
4095 * tdata. For each one that doesn't, we must WCToMB it
4096 * individually and produce a \u escape sequence.
4097 *
4098 * It would probably be more robust to just bite the bullet
4099 * and WCToMB each individual Unicode character one by one,
4100 * then MBToWC each one back to see if it was an accurate
4101 * translation; but that strikes me as a horrifying number
4102 * of Windows API calls so I want to see if this faster way
4103 * will work. If it screws up badly we can always revert to
4104 * the simple and slow way.
4105 */
4106 while (tindex < len2 && uindex < len &&
4107 tdata[tindex] && udata[uindex]) {
4108 if (tindex + 1 < len2 &&
4109 tdata[tindex] == '\r' &&
4110 tdata[tindex+1] == '\n') {
4111 tindex++;
4112 uindex++;
4113 }
4114 if (unitab[tdata[tindex]] == udata[uindex]) {
4115 multilen = 1;
4116 before[0] = '\0';
4117 after[0] = '\0';
4118 blen = alen = 0;
4119 } else {
4120 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4121 NULL, 0, NULL, NULL);
4122 if (multilen != 1) {
c3b032a6 4123 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4124 udata[uindex]);
a7419ea4 4125 alen = 1; strcpy(after, "}");
4126 } else {
4127 blen = sprintf(before, "\\u%d", udata[uindex]);
4128 alen = 0; after[0] = '\0';
4129 }
4130 }
4131 assert(tindex + multilen <= len2);
4132 totallen = blen + alen;
4133 for (i = 0; i < multilen; i++) {
4134 if (tdata[tindex+i] == '\\' ||
4135 tdata[tindex+i] == '{' ||
4136 tdata[tindex+i] == '}')
4137 totallen += 2;
4138 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4139 totallen += 6; /* \par\r\n */
4140 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4141 totallen += 4;
4142 else
4143 totallen++;
4144 }
4145
4146 if (rtfsize < rtflen + totallen + 3) {
4147 rtfsize = rtflen + totallen + 512;
4148 rtf = srealloc(rtf, rtfsize);
4149 }
4150
4151 strcpy(rtf + rtflen, before); rtflen += blen;
4152 for (i = 0; i < multilen; i++) {
4153 if (tdata[tindex+i] == '\\' ||
4154 tdata[tindex+i] == '{' ||
4155 tdata[tindex+i] == '}') {
4156 rtf[rtflen++] = '\\';
4157 rtf[rtflen++] = tdata[tindex+i];
4158 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4159 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4160 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4161 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4162 } else {
4163 rtf[rtflen++] = tdata[tindex+i];
4164 }
4165 }
4166 strcpy(rtf + rtflen, after); rtflen += alen;
4167
4168 tindex += multilen;
4169 uindex++;
4170 }
4171
4172 strcpy(rtf + rtflen, "}");
4173 rtflen += 2;
4174
4175 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4176 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4177 strcpy(lock3, rtf);
4178 GlobalUnlock(clipdata3);
4179 }
4180 sfree(rtf);
4181 } else
4182 clipdata3 = NULL;
4183
4eeb7d09 4184 GlobalUnlock(clipdata);
4185 GlobalUnlock(clipdata2);
4186
4187 if (!must_deselect)
4188 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4189
4190 if (OpenClipboard(hwnd)) {
4191 EmptyClipboard();
4192 SetClipboardData(CF_UNICODETEXT, clipdata);
4193 SetClipboardData(CF_TEXT, clipdata2);
a7419ea4 4194 if (clipdata3)
4195 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4eeb7d09 4196 CloseClipboard();
4197 } else {
4198 GlobalFree(clipdata);
4199 GlobalFree(clipdata2);
4200 }
4201
4202 if (!must_deselect)
4203 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4204}
4205
a8327734 4206void get_clip(void *frontend, wchar_t ** p, int *len)
32874aea 4207{
374330e2 4208 static HGLOBAL clipdata = NULL;
4eeb7d09 4209 static wchar_t *converted = 0;
4210 wchar_t *p2;
374330e2 4211
4eeb7d09 4212 if (converted) {
4213 sfree(converted);
4214 converted = 0;
4215 }
374330e2 4216 if (!p) {
4217 if (clipdata)
32874aea 4218 GlobalUnlock(clipdata);
374330e2 4219 clipdata = NULL;
4220 return;
4eeb7d09 4221 } else if (OpenClipboard(NULL)) {
2d466ffd 4222 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 4223 CloseClipboard();
4eeb7d09 4224 *p = GlobalLock(clipdata);
4225 if (*p) {
4226 for (p2 = *p; *p2; p2++);
4227 *len = p2 - *p;
4228 return;
374330e2 4229 }
2d466ffd 4230 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 4231 char *s;
4232 int i;
4233 CloseClipboard();
4234 s = GlobalLock(clipdata);
4235 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4236 *p = converted = smalloc(i * sizeof(wchar_t));
4237 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4238 *len = i - 1;
4239 return;
4240 } else
4241 CloseClipboard();
374330e2 4242 }
4243
4244 *p = NULL;
4245 *len = 0;
4246}
4247
4eeb7d09 4248#if 0
374330e2 4249/*
4250 * Move `lines' lines from position `from' to position `to' in the
4251 * window.
4252 */
a8327734 4253void optimised_move(void *frontend, int to, int from, int lines)
32874aea 4254{
374330e2 4255 RECT r;
f67b4e85 4256 int min, max;
374330e2 4257
4258 min = (to < from ? to : from);
4259 max = to + from - min;
374330e2 4260
5a73255e 4261 r.left = offset_width;
887035a5 4262 r.right = offset_width + term->cols * font_width;
5a73255e 4263 r.top = offset_height + min * font_height;
4264 r.bottom = offset_height + (max + lines) * font_height;
32874aea 4265 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 4266}
4eeb7d09 4267#endif
374330e2 4268
4269/*
4270 * Print a message box and perform a fatal exit.
4271 */
32874aea 4272void fatalbox(char *fmt, ...)
4273{
374330e2 4274 va_list ap;
4275 char stuff[200];
4276
4277 va_start(ap, fmt);
4278 vsprintf(stuff, fmt, ap);
4279 va_end(ap);
4280 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
93b581bd 4281 cleanup_exit(1);
374330e2 4282}
4283
4284/*
1709795f 4285 * Print a modal (Really Bad) message box and perform a fatal exit.
4286 */
4287void modalfatalbox(char *fmt, ...)
4288{
4289 va_list ap;
4290 char stuff[200];
4291
4292 va_start(ap, fmt);
4293 vsprintf(stuff, fmt, ap);
4294 va_end(ap);
4295 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4296 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4297 cleanup_exit(1);
4298}
4299
4300/*
f8a28d1f 4301 * Manage window caption / taskbar flashing, if enabled.
4302 * 0 = stop, 1 = maintain, 2 = start
4303 */
4304static void flash_window(int mode)
4305{
4306 static long last_flash = 0;
4307 static int flashing = 0;
4308 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4309 /* stop */
4310 if (flashing) {
4311 FlashWindow(hwnd, FALSE);
4312 flashing = 0;
4313 }
4314
4315 } else if (mode == 2) {
4316 /* start */
4317 if (!flashing) {
4318 last_flash = GetTickCount();
4319 flashing = 1;
4320 FlashWindow(hwnd, TRUE);
4321 }
4322
4323 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4324 /* maintain */
4325 if (flashing) {
4326 long now = GetTickCount();
4327 long fdiff = now - last_flash;
4328 if (fdiff < 0 || fdiff > 450) {
4329 last_flash = now;
4330 FlashWindow(hwnd, TRUE); /* toggle */
4331 }
4332 }
4333 }
4334}
4335
4336/*
374330e2 4337 * Beep.
4338 */
a8327734 4339void beep(void *frontend, int mode)
32874aea 4340{
03169ad0 4341 if (mode == BELL_DEFAULT) {
eb04402e 4342 /*
4343 * For MessageBeep style bells, we want to be careful of
4344 * timing, because they don't have the nice property of
4345 * PlaySound bells that each one cancels the previous
4346 * active one. So we limit the rate to one per 50ms or so.
4347 */
4348 static long lastbeep = 0;
d51cdf1e 4349 long beepdiff;
eb04402e 4350
d51cdf1e 4351 beepdiff = GetTickCount() - lastbeep;
eb04402e 4352 if (beepdiff >= 0 && beepdiff < 50)
4353 return;
156686ef 4354 MessageBeep(MB_OK);
d51cdf1e 4355 /*
4356 * The above MessageBeep call takes time, so we record the
4357 * time _after_ it finishes rather than before it starts.
4358 */
4359 lastbeep = GetTickCount();
03169ad0 4360 } else if (mode == BELL_WAVEFILE) {
4361 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 4362 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 4363 sprintf(buf, "Unable to play sound file\n%s\n"
4364 "Using default sound instead", cfg.bell_wavefile);
32874aea 4365 MessageBox(hwnd, buf, "PuTTY Sound Error",
4366 MB_OK | MB_ICONEXCLAMATION);
03169ad0 4367 cfg.beep = BELL_DEFAULT;
4368 }
4369 }
f8a28d1f 4370 /* Otherwise, either visual bell or disabled; do nothing here */
887035a5 4371 if (!term->has_focus) {
f8a28d1f 4372 flash_window(2); /* start */
4373 }
374330e2 4374}
8f57d753 4375
4376/*
68f9b3d9 4377 * Minimise or restore the window in response to a server-side
4378 * request.
4379 */
a8327734 4380void set_iconic(void *frontend, int iconic)
68f9b3d9 4381{
4382 if (IsIconic(hwnd)) {
4383 if (!iconic)
4384 ShowWindow(hwnd, SW_RESTORE);
4385 } else {
4386 if (iconic)
4387 ShowWindow(hwnd, SW_MINIMIZE);
4388 }
4389}
4390
4391/*
4392 * Move the window in response to a server-side request.
4393 */
a8327734 4394void move_window(void *frontend, int x, int y)
68f9b3d9 4395{
d1e0a352 4396 if (cfg.resize_action == RESIZE_DISABLED ||
4397 cfg.resize_action == RESIZE_FONT ||
4398 IsZoomed(hwnd))
4399 return;
4400
68f9b3d9 4401 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4402}
4403
4404/*
4405 * Move the window to the top or bottom of the z-order in response
4406 * to a server-side request.
4407 */
a8327734 4408void set_zorder(void *frontend, int top)
68f9b3d9 4409{
1ba99d2c 4410 if (cfg.alwaysontop)
68f9b3d9 4411 return; /* ignore */
4412 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4413 SWP_NOMOVE | SWP_NOSIZE);
4414}
4415
4416/*
4417 * Refresh the window in response to a server-side request.
4418 */
a8327734 4419void refresh_window(void *frontend)
68f9b3d9 4420{
4421 InvalidateRect(hwnd, NULL, TRUE);
4422}
4423
4424/*
4425 * Maximise or restore the window in response to a server-side
4426 * request.
4427 */
a8327734 4428void set_zoomed(void *frontend, int zoomed)
68f9b3d9 4429{
1ba99d2c 4430 if (IsZoomed(hwnd)) {
4431 if (!zoomed)
4432 ShowWindow(hwnd, SW_RESTORE);
68f9b3d9 4433 } else {
4434 if (zoomed)
4435 ShowWindow(hwnd, SW_MAXIMIZE);
4436 }
4437}
4438
4439/*
4440 * Report whether the window is iconic, for terminal reports.
4441 */
a8327734 4442int is_iconic(void *frontend)
68f9b3d9 4443{
4444 return IsIconic(hwnd);
4445}
4446
4447/*
4448 * Report the window's position, for terminal reports.
4449 */
a8327734 4450void get_window_pos(void *frontend, int *x, int *y)
68f9b3d9 4451{
4452 RECT r;
4453 GetWindowRect(hwnd, &r);
4454 *x = r.left;
4455 *y = r.top;
4456}
4457
4458/*
4459 * Report the window's pixel size, for terminal reports.
4460 */
a8327734 4461void get_window_pixels(void *frontend, int *x, int *y)
68f9b3d9 4462{
4463 RECT r;
4464 GetWindowRect(hwnd, &r);
4465 *x = r.right - r.left;
4466 *y = r.bottom - r.top;
4467}
4468
4469/*
4470 * Return the window or icon title.
4471 */
a8327734 4472char *get_window_title(void *frontend, int icon)
68f9b3d9 4473{
4474 return icon ? icon_name : window_name;
4475}
1ba99d2c 4476
4477/*
4478 * See if we're in full-screen mode.
4479 */
4480int is_full_screen()
4481{
4482 if (!IsZoomed(hwnd))
4483 return FALSE;
4484 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4485 return FALSE;
4486 return TRUE;
4487}
4488
d0f1bcb4 4489/* Get the rect/size of a full screen window using the nearest available
4490 * monitor in multimon systems; default to something sensible if only
4491 * one monitor is present. */
4492static int get_fullscreen_rect(RECT * ss)
4493{
4494#ifdef MONITOR_DEFAULTTONEAREST
4495 HMONITOR mon;
4496 MONITORINFO mi;
4497 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4498 mi.cbSize = sizeof(mi);
4499 GetMonitorInfo(mon, &mi);
4500
4501 /* structure copy */
4502 *ss = mi.rcMonitor;
4503 return TRUE;
4504#else
4505/* could also use code like this:
4506 ss->left = ss->top = 0;
4507 ss->right = GetSystemMetrics(SM_CXSCREEN);
4508 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4509*/
e6f8202f 4510 return GetClientRect(GetDesktopWindow(), ss);
d0f1bcb4 4511#endif
4512}
4513
4514
1ba99d2c 4515/*
4516 * Go full-screen. This should only be called when we are already
4517 * maximised.
4518 */
4519void make_full_screen()
4520{
4521 DWORD style;
d0f1bcb4 4522 RECT ss;
1ba99d2c 4523
4524 assert(IsZoomed(hwnd));
4525
d0f1bcb4 4526 if (is_full_screen())
4527 return;
4528
1ba99d2c 4529 /* Remove the window furniture. */
4530 style = GetWindowLong(hwnd, GWL_STYLE);
4531 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4532 if (cfg.scrollbar_in_fullscreen)
4533 style |= WS_VSCROLL;
4534 else
4535 style &= ~WS_VSCROLL;
4536 SetWindowLong(hwnd, GWL_STYLE, style);
4537
4538 /* Resize ourselves to exactly cover the nearest monitor. */
d0f1bcb4 4539 get_fullscreen_rect(&ss);
4540 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4541 ss.right - ss.left,
4542 ss.bottom - ss.top,
4543 SWP_FRAMECHANGED);
1ba99d2c 4544
4545 /* Tick the menu item in the System menu. */
4546 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4547 MF_CHECKED);
4548}
4549
4550/*
4551 * Clear the full-screen attributes.
4552 */
4553void clear_full_screen()
4554{
4555 DWORD oldstyle, style;
4556
4557 /* Reinstate the window furniture. */
4558 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
811f10b3 4559 style |= WS_CAPTION | WS_BORDER;
4560 if (cfg.resize_action == RESIZE_DISABLED)
4561 style &= ~WS_THICKFRAME;
4562 else
4563 style |= WS_THICKFRAME;
1ba99d2c 4564 if (cfg.scrollbar)
4565 style |= WS_VSCROLL;
4566 else
4567 style &= ~WS_VSCROLL;
4568 if (style != oldstyle) {
4569 SetWindowLong(hwnd, GWL_STYLE, style);
4570 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4571 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4572 SWP_FRAMECHANGED);
4573 }
4574
4575 /* Untick the menu item in the System menu. */
4576 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4577 MF_UNCHECKED);
4578}
4579
4580/*
4581 * Toggle full-screen mode.
4582 */
4583void flip_full_screen()
4584{
4585 if (is_full_screen()) {
4586 ShowWindow(hwnd, SW_RESTORE);
4587 } else if (IsZoomed(hwnd)) {
4588 make_full_screen();
4589 } else {
4590 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4591 ShowWindow(hwnd, SW_MAXIMIZE);
4592 }
4593}
ab2c86b9 4594
b9d7bcad 4595void frontend_keypress(void *handle)
ab2c86b9 4596{
4597 /*
4598 * Keypress termination in non-Close-On-Exit mode is not
4599 * currently supported in PuTTY proper, because the window
4600 * always has a perfectly good Close button anyway. So we do
4601 * nothing here.
4602 */
4603 return;
4604}