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