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