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