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