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