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