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