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