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