Fix gratuitous assertion failure in Plink (ssh1_throttle_count was
[sgt/putty] / window.c
CommitLineData
374330e2 1#include <windows.h>
49bad831 2#include <imm.h>
374330e2 3#include <commctrl.h>
f0a70059 4#include <mmsystem.h>
4d331a77 5#ifndef AUTO_WINSOCK
6#ifdef WINSOCK_TWO
7#include <winsock2.h>
8#else
374330e2 9#include <winsock.h>
4d331a77 10#endif
11#endif
374330e2 12#include <stdio.h>
13#include <stdlib.h>
1d470ad2 14#include <ctype.h>
ec55b220 15#include <time.h>
374330e2 16
32874aea 17#define PUTTY_DO_GLOBALS /* actually _define_ globals */
374330e2 18#include "putty.h"
8c3cd914 19#include "winstuff.h"
d5859615 20#include "storage.h"
374330e2 21#include "win_res.h"
22
6833a413 23#define IDM_SHOWLOG 0x0010
24#define IDM_NEWSESS 0x0020
25#define IDM_DUPSESS 0x0030
26#define IDM_RECONF 0x0040
27#define IDM_CLRSB 0x0050
28#define IDM_RESET 0x0060
29#define IDM_TEL_AYT 0x0070
30#define IDM_TEL_BRK 0x0080
31#define IDM_TEL_SYNCH 0x0090
32#define IDM_TEL_EC 0x00a0
33#define IDM_TEL_EL 0x00b0
34#define IDM_TEL_GA 0x00c0
35#define IDM_TEL_NOP 0x00d0
36#define IDM_TEL_ABORT 0x00e0
37#define IDM_TEL_AO 0x00f0
38#define IDM_TEL_IP 0x0100
39#define IDM_TEL_SUSP 0x0110
40#define IDM_TEL_EOR 0x0120
41#define IDM_TEL_EOF 0x0130
42#define IDM_ABOUT 0x0140
43#define IDM_SAVEDSESS 0x0150
bc1235d4 44#define IDM_COPYALL 0x0160
6833a413 45
32874aea 46#define IDM_SESSLGP 0x0250 /* log type printable */
47#define IDM_SESSLGA 0x0260 /* log type all chars */
48#define IDM_SESSLGE 0x0270 /* log end */
6833a413 49#define IDM_SAVED_MIN 0x1000
50#define IDM_SAVED_MAX 0x2000
374330e2 51
59ad2c03 52#define WM_IGNORE_SIZE (WM_XUSER + 1)
53#define WM_IGNORE_CLIP (WM_XUSER + 2)
374330e2 54
3cf144db 55/* Needed for Chinese support and apparently not always defined. */
56#ifndef VK_PROCESSKEY
57#define VK_PROCESSKEY 0xE5
58#endif
59
5eaf1e06 60/* Needed for mouse wheel support and not defined in earlier SDKs. */
61#ifndef WM_MOUSEWHEEL
62#define WM_MOUSEWHEEL 0x020A
63#endif
64
32874aea 65static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
66static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
67 unsigned char *output);
374330e2 68static void cfgtopalette(void);
69static void init_palette(void);
59ad2c03 70static void init_fonts(int);
4eeb7d09 71static void another_font(int);
72static void deinit_fonts(void);
374330e2 73
74static int extra_width, extra_height;
75
59ad2c03 76static int pending_netevent = 0;
77static WPARAM pend_netevent_wParam = 0;
78static LPARAM pend_netevent_lParam = 0;
79static void enact_pending_netevent(void);
f8a28d1f 80static void flash_window(int mode);
59ad2c03 81
ec55b220 82static time_t last_movement = 0;
83
374330e2 84#define FONT_NORMAL 0
85#define FONT_BOLD 1
86#define FONT_UNDERLINE 2
87#define FONT_BOLDUND 3
4eeb7d09 88#define FONT_WIDE 0x04
89#define FONT_HIGH 0x08
90#define FONT_NARROW 0x10
91#define FONT_OEM 0x20
92#define FONT_OEMBOLD 0x21
93#define FONT_OEMUND 0x22
94#define FONT_OEMBOLDUND 0x23
95#define FONT_MSGOTHIC 0x40
96#define FONT_MINGLIU 0x60
97#define FONT_GULIMCHE 0x80
98#define FONT_MAXNO 0x8F
99#define FONT_SHIFT 5
100static HFONT fonts[FONT_MAXNO];
101static int fontflag[FONT_MAXNO];
374330e2 102static enum {
103 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
104} bold_mode;
105static enum {
106 UND_LINE, UND_FONT
107} und_mode;
108static int descent;
109
110#define NCOLOURS 24
111static COLORREF colours[NCOLOURS];
112static HPALETTE pal;
113static LPLOGPALETTE logpal;
114static RGBTRIPLE defpal[NCOLOURS];
115
116static HWND hwnd;
117
934c0b7a 118static HBITMAP caretbm;
119
374330e2 120static int dbltime, lasttime, lastact;
121static Mouse_Button lastbtn;
122
01c034ad 123/* this allows xterm-style mouse handling. */
124static int send_raw_mouse = 0;
125static int wheel_accumulator = 0;
126
374330e2 127static char *window_name, *icon_name;
128
03f23ad6 129static int compose_state = 0;
130
1ece9da7 131static OSVERSIONINFO osVersion;
88485e4d 132
0965bee0 133/* Dummy routine, only required in plink. */
32874aea 134void ldisc_update(int echo, int edit)
135{
136}
6f34e365 137
32874aea 138int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
139{
374330e2 140 static char appname[] = "PuTTY";
141 WORD winsock_ver;
142 WSADATA wsadata;
143 WNDCLASS wndclass;
144 MSG msg;
145 int guess_width, guess_height;
146
8c3cd914 147 hinst = inst;
67779be7 148 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
73251d5d 149
374330e2 150 winsock_ver = MAKEWORD(1, 1);
151 if (WSAStartup(winsock_ver, &wsadata)) {
152 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
153 MB_OK | MB_ICONEXCLAMATION);
154 return 1;
155 }
156 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
157 MessageBox(NULL, "WinSock version is incompatible with 1.1",
158 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
159 WSACleanup();
160 return 1;
161 }
162 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
8df7a775 163 sk_init();
374330e2 164
165 InitCommonControls();
166
301b66db 167 /* Ensure a Maximize setting in Explorer doesn't maximise the
168 * config box. */
169 defuse_showwindow();
170
88485e4d 171 {
172 ZeroMemory(&osVersion, sizeof(osVersion));
536840e6 173 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
1ece9da7 174 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
175 MessageBox(NULL, "Windows refuses to report a version",
176 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
177 return 1;
178 }
88485e4d 179 }
180
374330e2 181 /*
182 * Process the command line.
183 */
184 {
185 char *p;
186
e277c42d 187 default_protocol = DEFAULT_PROTOCOL;
188 default_port = DEFAULT_PORT;
e1c8e0ed 189 cfg.logtype = LGTYP_NONE;
e277c42d 190
a9422f39 191 do_defaults(NULL, &cfg);
374330e2 192
193 p = cmdline;
32874aea 194 while (*p && isspace(*p))
195 p++;
374330e2 196
197 /*
e277c42d 198 * Process command line options first. Yes, this can be
199 * done better, and it will be as soon as I have the
200 * energy...
201 */
202 while (*p == '-') {
203 char *q = p + strcspn(p, " \t");
204 p++;
205 if (q == p + 3 &&
206 tolower(p[0]) == 's' &&
32874aea 207 tolower(p[1]) == 's' && tolower(p[2]) == 'h') {
e277c42d 208 default_protocol = cfg.protocol = PROT_SSH;
209 default_port = cfg.port = 22;
de3df031 210 } else if (q == p + 7 &&
32874aea 211 tolower(p[0]) == 'c' &&
212 tolower(p[1]) == 'l' &&
213 tolower(p[2]) == 'e' &&
214 tolower(p[3]) == 'a' &&
215 tolower(p[4]) == 'n' &&
216 tolower(p[5]) == 'u' && tolower(p[6]) == 'p') {
217 /*
218 * `putty -cleanup'. Remove all registry entries
219 * associated with PuTTY, and also find and delete
220 * the random seed file.
221 */
222 if (MessageBox(NULL,
223 "This procedure will remove ALL Registry\n"
224 "entries associated with PuTTY, and will\n"
225 "also remove the PuTTY random seed file.\n"
226 "\n"
227 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
228 "SESSIONS. Are you really sure you want\n"
229 "to continue?",
230 "PuTTY Warning",
231 MB_YESNO | MB_ICONWARNING) == IDYES) {
232 cleanup_all();
233 }
234 exit(0);
e277c42d 235 }
236 p = q + strspn(q, " \t");
237 }
238
239 /*
374330e2 240 * An initial @ means to activate a saved session.
241 */
242 if (*p == '@') {
f317611e 243 int i = strlen(p);
32874aea 244 while (i > 1 && isspace(p[i - 1]))
f317611e 245 i--;
246 p[i] = '\0';
32874aea 247 do_defaults(p + 1, &cfg);
374330e2 248 if (!*cfg.host && !do_config()) {
249 WSACleanup();
250 return 0;
251 }
252 } else if (*p == '&') {
253 /*
254 * An initial & means we've been given a command line
255 * containing the hex value of a HANDLE for a file
256 * mapping object, which we must then extract as a
257 * config.
258 */
259 HANDLE filemap;
260 Config *cp;
32874aea 261 if (sscanf(p + 1, "%p", &filemap) == 1 &&
374330e2 262 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
263 0, 0, sizeof(Config))) != NULL) {
264 cfg = *cp;
265 UnmapViewOfFile(cp);
266 CloseHandle(filemap);
267 } else if (!do_config()) {
268 WSACleanup();
269 return 0;
270 }
271 } else if (*p) {
272 char *q = p;
32874aea 273 /*
274 * If the hostname starts with "telnet:", set the
275 * protocol to Telnet and process the string as a
276 * Telnet URL.
277 */
278 if (!strncmp(q, "telnet:", 7)) {
279 char c;
280
281 q += 7;
70887be9 282 if (q[0] == '/' && q[1] == '/')
283 q += 2;
32874aea 284 cfg.protocol = PROT_TELNET;
285 p = q;
286 while (*p && *p != ':' && *p != '/')
287 p++;
288 c = *p;
289 if (*p)
290 *p++ = '\0';
291 if (c == ':')
292 cfg.port = atoi(p);
293 else
294 cfg.port = -1;
295 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
296 cfg.host[sizeof(cfg.host) - 1] = '\0';
297 } else {
298 while (*p && !isspace(*p))
299 p++;
300 if (*p)
301 *p++ = '\0';
302 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
303 cfg.host[sizeof(cfg.host) - 1] = '\0';
304 while (*p && isspace(*p))
305 p++;
306 if (*p)
307 cfg.port = atoi(p);
308 else
309 cfg.port = -1;
310 }
374330e2 311 } else {
312 if (!do_config()) {
313 WSACleanup();
314 return 0;
315 }
316 }
13eafebf 317
381b1876 318 /*
319 * Trim leading whitespace off the hostname if it's there.
320 */
321 {
322 int space = strspn(cfg.host, " \t");
323 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
324 }
325
13eafebf 326 /* See if host is of the form user@host */
327 if (cfg.host[0] != '\0') {
328 char *atsign = strchr(cfg.host, '@');
329 /* Make sure we're not overflowing the user field */
330 if (atsign) {
32874aea 331 if (atsign - cfg.host < sizeof cfg.username) {
332 strncpy(cfg.username, cfg.host, atsign - cfg.host);
333 cfg.username[atsign - cfg.host] = '\0';
13eafebf 334 }
32874aea 335 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
13eafebf 336 }
337 }
503ddb97 338
339 /*
340 * Trim a colon suffix off the hostname if it's there.
341 */
342 cfg.host[strcspn(cfg.host, ":")] = '\0';
374330e2 343 }
344
89ee5268 345 /*
346 * Select protocol. This is farmed out into a table in a
347 * separate file to enable an ssh-free variant.
348 */
349 {
32874aea 350 int i;
351 back = NULL;
352 for (i = 0; backends[i].backend != NULL; i++)
353 if (backends[i].protocol == cfg.protocol) {
354 back = backends[i].backend;
355 break;
356 }
357 if (back == NULL) {
358 MessageBox(NULL, "Unsupported protocol number found",
359 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
360 WSACleanup();
361 return 1;
362 }
89ee5268 363 }
5bc238bb 364
b278b14a 365 /* Check for invalid Port number (i.e. zero) */
366 if (cfg.port == 0) {
32874aea 367 MessageBox(NULL, "Invalid Port Number",
368 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
369 WSACleanup();
370 return 1;
b278b14a 371 }
372
374330e2 373 if (!prev) {
32874aea 374 wndclass.style = 0;
375 wndclass.lpfnWndProc = WndProc;
376 wndclass.cbClsExtra = 0;
377 wndclass.cbWndExtra = 0;
378 wndclass.hInstance = inst;
379 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
380 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
381 wndclass.hbrBackground = GetStockObject(BLACK_BRUSH);
382 wndclass.lpszMenuName = NULL;
374330e2 383 wndclass.lpszClassName = appname;
384
32874aea 385 RegisterClass(&wndclass);
374330e2 386 }
387
388 hwnd = NULL;
389
390 savelines = cfg.savelines;
391 term_init();
392
393 cfgtopalette();
394
395 /*
396 * Guess some defaults for the window size. This all gets
397 * updated later, so we don't really care too much. However, we
398 * do want the font width/height guesses to correspond to a
399 * large font rather than a small one...
400 */
32874aea 401
374330e2 402 font_width = 10;
403 font_height = 20;
404 extra_width = 25;
405 extra_height = 28;
32874aea 406 term_size(cfg.height, cfg.width, cfg.savelines);
374330e2 407 guess_width = extra_width + font_width * cols;
408 guess_height = extra_height + font_height * rows;
409 {
410 RECT r;
411 HWND w = GetDesktopWindow();
32874aea 412 GetWindowRect(w, &r);
374330e2 413 if (guess_width > r.right - r.left)
414 guess_width = r.right - r.left;
415 if (guess_height > r.bottom - r.top)
416 guess_height = r.bottom - r.top;
417 }
418
c9def1b8 419 {
32874aea 420 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
421 int exwinmode = 0;
422 if (!cfg.scrollbar)
423 winmode &= ~(WS_VSCROLL);
424 if (cfg.locksize)
425 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
426 if (cfg.alwaysontop)
427 exwinmode |= WS_EX_TOPMOST;
428 if (cfg.sunken_edge)
429 exwinmode |= WS_EX_CLIENTEDGE;
430 hwnd = CreateWindowEx(exwinmode, appname, appname,
431 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
432 guess_width, guess_height,
433 NULL, NULL, inst, NULL);
434 }
374330e2 435
436 /*
437 * Initialise the fonts, simultaneously correcting the guesses
438 * for font_{width,height}.
439 */
440 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
441 und_mode = UND_FONT;
59ad2c03 442 init_fonts(0);
374330e2 443
444 /*
445 * Correct the guesses for extra_{width,height}.
446 */
447 {
448 RECT cr, wr;
32874aea 449 GetWindowRect(hwnd, &wr);
450 GetClientRect(hwnd, &cr);
374330e2 451 extra_width = wr.right - wr.left - cr.right + cr.left;
452 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
453 }
454
455 /*
456 * Resize the window, now we know what size we _really_ want it
457 * to be.
458 */
459 guess_width = extra_width + font_width * cols;
460 guess_height = extra_height + font_height * rows;
32874aea 461 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
462 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
463 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
374330e2 464
465 /*
934c0b7a 466 * Set up a caret bitmap, with no content.
467 */
468 {
32874aea 469 char *bits;
470 int size = (font_width + 15) / 16 * 2 * font_height;
471 bits = smalloc(size);
472 memset(bits, 0, size);
473 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
474 sfree(bits);
934c0b7a 475 }
5b7ce734 476 CreateCaret(hwnd, caretbm, font_width, font_height);
934c0b7a 477
478 /*
374330e2 479 * Initialise the scroll bar.
480 */
481 {
482 SCROLLINFO si;
483
484 si.cbSize = sizeof(si);
c9def1b8 485 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 486 si.nMin = 0;
32874aea 487 si.nMax = rows - 1;
374330e2 488 si.nPage = rows;
489 si.nPos = 0;
32874aea 490 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
374330e2 491 }
492
493 /*
494 * Start up the telnet connection.
495 */
496 {
497 char *error;
9ca5da42 498 char msg[1024], *title;
374330e2 499 char *realhost;
500
32874aea 501 error = back->init(cfg.host, cfg.port, &realhost);
374330e2 502 if (error) {
582c0054 503 sprintf(msg, "Unable to open connection to\n"
32874aea 504 "%.800s\n" "%s", cfg.host, error);
374330e2 505 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
506 return 0;
507 }
508 window_name = icon_name = NULL;
32874aea 509 if (*cfg.wintitle) {
510 title = cfg.wintitle;
511 } else {
512 sprintf(msg, "%s - PuTTY", realhost);
513 title = msg;
514 }
6e1ebb76 515 sfree(realhost);
32874aea 516 set_title(title);
517 set_icon(title);
374330e2 518 }
519
d85548fe 520 session_closed = FALSE;
521
374330e2 522 /*
523 * Set up the input and output buffers.
524 */
c9def1b8 525 inbuf_head = 0;
374330e2 526 outbuf_reap = outbuf_head = 0;
527
528 /*
529 * Prepare the mouse handler.
530 */
531 lastact = MA_NOTHING;
01c034ad 532 lastbtn = MBT_NOTHING;
374330e2 533 dbltime = GetDoubleClickTime();
534
535 /*
536 * Set up the session-control options on the system menu.
537 */
538 {
32874aea 539 HMENU m = GetSystemMenu(hwnd, FALSE);
540 HMENU p, s;
0a4aa984 541 int i;
374330e2 542
32874aea 543 AppendMenu(m, MF_SEPARATOR, 0, 0);
374330e2 544 if (cfg.protocol == PROT_TELNET) {
545 p = CreateMenu();
32874aea 546 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
547 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
548 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
549 AppendMenu(p, MF_SEPARATOR, 0, 0);
550 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
551 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
552 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
553 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
554 AppendMenu(p, MF_SEPARATOR, 0, 0);
555 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
556 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
557 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
558 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
559 AppendMenu(p, MF_SEPARATOR, 0, 0);
560 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
561 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
562 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
563 "Telnet Command");
564 AppendMenu(m, MF_SEPARATOR, 0, 0);
565 }
566 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
567 AppendMenu(m, MF_SEPARATOR, 0, 0);
568 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
569 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
0a4aa984 570 s = CreateMenu();
571 get_sesslist(TRUE);
32874aea 572 for (i = 1; i < ((nsessions < 256) ? nsessions : 256); i++)
573 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
574 sessions[i]);
575 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
576 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
577 AppendMenu(m, MF_SEPARATOR, 0, 0);
578 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
579 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
580 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
581 AppendMenu(m, MF_SEPARATOR, 0, 0);
582 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
374330e2 583 }
584
585 /*
586 * Finally show the window!
587 */
32874aea 588 ShowWindow(hwnd, show);
f5eca4f8 589 SetForegroundWindow(hwnd);
374330e2 590
591 /*
e1c8e0ed 592 * Open the initial log file if there is one.
593 */
594 logfopen();
595
596 /*
374330e2 597 * Set the palette up.
598 */
599 pal = NULL;
600 logpal = NULL;
601 init_palette();
602
603 has_focus = (GetForegroundWindow() == hwnd);
32874aea 604 UpdateWindow(hwnd);
374330e2 605
32874aea 606 if (GetMessage(&msg, NULL, 0, 0) == 1) {
59ad2c03 607 int timer_id = 0, long_timer = 0;
608
ec55b220 609 while (msg.message != WM_QUIT) {
59ad2c03 610 /* Sometimes DispatchMessage calls routines that use their own
611 * GetMessage loop, setup this timer so we get some control back.
612 *
613 * Also call term_update() from the timer so that if the host
614 * is sending data flat out we still do redraws.
615 */
32874aea 616 if (timer_id && long_timer) {
59ad2c03 617 KillTimer(hwnd, timer_id);
618 long_timer = timer_id = 0;
619 }
32874aea 620 if (!timer_id)
59ad2c03 621 timer_id = SetTimer(hwnd, 1, 20, NULL);
32874aea 622 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
623 DispatchMessage(&msg);
59ad2c03 624
ec55b220 625 /* Make sure we blink everything that needs it. */
59ad2c03 626 term_blink(0);
627
c9def1b8 628 /* Send the paste buffer if there's anything to send */
629 term_paste();
630
59ad2c03 631 /* If there's nothing new in the queue then we can do everything
632 * we've delayed, reading the socket, writing, and repainting
633 * the window.
634 */
32874aea 635 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
ec55b220 636 continue;
59ad2c03 637
ec55b220 638 if (pending_netevent) {
639 enact_pending_netevent();
640
641 /* Force the cursor blink on */
642 term_blink(1);
643
32874aea 644 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
ec55b220 645 continue;
59ad2c03 646 }
ec55b220 647
648 /* Okay there is now nothing to do so we make sure the screen is
649 * completely up to date then tell windows to call us in a little
650 * while.
651 */
652 if (timer_id) {
653 KillTimer(hwnd, timer_id);
654 timer_id = 0;
655 }
32874aea 656 HideCaret(hwnd);
ec55b220 657 if (inbuf_head)
658 term_out();
659 term_update();
32874aea 660 ShowCaret(hwnd);
f8a28d1f 661
662 flash_window(1); /* maintain */
663
156686ef 664 if (in_vbell)
32874aea 665 /* Hmm, term_update didn't want to do an update too soon ... */
666 timer_id = SetTimer(hwnd, 1, 50, NULL);
156686ef 667 else if (!has_focus)
f8a28d1f 668 timer_id = SetTimer(hwnd, 1, 500, NULL);
ec55b220 669 else
32874aea 670 timer_id = SetTimer(hwnd, 1, 100, NULL);
ec55b220 671 long_timer = 1;
32874aea 672
ec55b220 673 /* There's no point rescanning everything in the message queue
156686ef 674 * so we do an apparently unnecessary wait here
ec55b220 675 */
676 WaitMessage();
32874aea 677 if (GetMessage(&msg, NULL, 0, 0) != 1)
ec55b220 678 break;
59ad2c03 679 }
374330e2 680 }
681
682 /*
683 * Clean up.
684 */
4eeb7d09 685 deinit_fonts();
374330e2 686 sfree(logpal);
687 if (pal)
688 DeleteObject(pal);
689 WSACleanup();
690
8f203108 691 if (cfg.protocol == PROT_SSH) {
374330e2 692 random_save_seed();
8f203108 693#ifdef MSCRYPTOAPI
694 crypto_wrapup();
695#endif
696 }
374330e2 697
698 return msg.wParam;
699}
700
701/*
8df7a775 702 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
703 */
32874aea 704char *do_select(SOCKET skt, int startup)
705{
8df7a775 706 int msg, events;
707 if (startup) {
708 msg = WM_NETEVENT;
d74d141c 709 events = FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT;
8df7a775 710 } else {
711 msg = events = 0;
712 }
713 if (!hwnd)
714 return "do_select(): internal error (hwnd==NULL)";
32874aea 715 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
716 switch (WSAGetLastError()) {
717 case WSAENETDOWN:
718 return "Network is down";
719 default:
720 return "WSAAsyncSelect(): unknown error";
721 }
8df7a775 722 }
723 return NULL;
724}
725
726/*
01c034ad 727 * set or clear the "raw mouse message" mode
728 */
729void set_raw_mouse_mode(int activate)
730{
731 send_raw_mouse = activate;
732 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
733}
734
735/*
8d5de777 736 * Print a message box and close the connection.
737 */
32874aea 738void connection_fatal(char *fmt, ...)
739{
8d5de777 740 va_list ap;
741 char stuff[200];
742
743 va_start(ap, fmt);
744 vsprintf(stuff, fmt, ap);
745 va_end(ap);
746 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
b41069ff 747 if (cfg.close_on_exit == COE_ALWAYS)
32874aea 748 PostQuitMessage(1);
8d5de777 749 else {
32874aea 750 session_closed = TRUE;
751 SetWindowText(hwnd, "PuTTY (inactive)");
8d5de777 752 }
753}
754
755/*
59ad2c03 756 * Actually do the job requested by a WM_NETEVENT
757 */
32874aea 758static void enact_pending_netevent(void)
759{
9dde0b46 760 static int reentering = 0;
8df7a775 761 extern int select_result(WPARAM, LPARAM);
762 int ret;
9dde0b46 763
764 if (reentering)
32874aea 765 return; /* don't unpend the pending */
9dde0b46 766
59ad2c03 767 pending_netevent = FALSE;
9dde0b46 768
769 reentering = 1;
32874aea 770 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
9dde0b46 771 reentering = 0;
59ad2c03 772
b41069ff 773 if (ret == 0 && !session_closed) {
32874aea 774 /* Abnormal exits will already have set session_closed and taken
775 * appropriate action. */
b41069ff 776 if (cfg.close_on_exit == COE_ALWAYS ||
32874aea 777 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
59ad2c03 778 else {
32874aea 779 session_closed = TRUE;
780 SetWindowText(hwnd, "PuTTY (inactive)");
781 MessageBox(hwnd, "Connection closed by remote host",
782 "PuTTY", MB_OK | MB_ICONINFORMATION);
59ad2c03 783 }
784 }
785}
786
787/*
374330e2 788 * Copy the colour palette from the configuration data into defpal.
789 * This is non-trivial because the colour indices are different.
790 */
32874aea 791static void cfgtopalette(void)
792{
374330e2 793 int i;
794 static const int ww[] = {
795 6, 7, 8, 9, 10, 11, 12, 13,
796 14, 15, 16, 17, 18, 19, 20, 21,
797 0, 1, 2, 3, 4, 4, 5, 5
798 };
799
32874aea 800 for (i = 0; i < 24; i++) {
374330e2 801 int w = ww[i];
802 defpal[i].rgbtRed = cfg.colours[w][0];
803 defpal[i].rgbtGreen = cfg.colours[w][1];
804 defpal[i].rgbtBlue = cfg.colours[w][2];
805 }
806}
807
808/*
809 * Set up the colour palette.
810 */
32874aea 811static void init_palette(void)
812{
374330e2 813 int i;
32874aea 814 HDC hdc = GetDC(hwnd);
374330e2 815 if (hdc) {
32874aea 816 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
374330e2 817 logpal = smalloc(sizeof(*logpal)
818 - sizeof(logpal->palPalEntry)
819 + NCOLOURS * sizeof(PALETTEENTRY));
820 logpal->palVersion = 0x300;
821 logpal->palNumEntries = NCOLOURS;
822 for (i = 0; i < NCOLOURS; i++) {
823 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
824 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
825 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
826 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
827 }
32874aea 828 pal = CreatePalette(logpal);
374330e2 829 if (pal) {
32874aea 830 SelectPalette(hdc, pal, FALSE);
831 RealizePalette(hdc);
832 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
374330e2 833 }
834 }
32874aea 835 ReleaseDC(hwnd, hdc);
374330e2 836 }
837 if (pal)
32874aea 838 for (i = 0; i < NCOLOURS; i++)
374330e2 839 colours[i] = PALETTERGB(defpal[i].rgbtRed,
840 defpal[i].rgbtGreen,
841 defpal[i].rgbtBlue);
842 else
32874aea 843 for (i = 0; i < NCOLOURS; i++)
374330e2 844 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 845 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 846}
847
848/*
4eeb7d09 849 * Initialise all the fonts we will need initially. There may be as many as
850 * three or as few as one. The other (poentially) twentyone fonts are done
851 * if/when they are needed.
852 *
853 * We also:
374330e2 854 *
855 * - check the font width and height, correcting our guesses if
856 * necessary.
857 *
858 * - verify that the bold font is the same width as the ordinary
859 * one, and engage shadow bolding if not.
860 *
861 * - verify that the underlined font is the same width as the
862 * ordinary one (manual underlining by means of line drawing can
863 * be done in a pinch).
374330e2 864 */
32874aea 865static void init_fonts(int pick_width)
866{
374330e2 867 TEXTMETRIC tm;
4eeb7d09 868 CPINFO cpinfo;
869 int fontsize[3];
97fc891e 870 int i;
374330e2 871 HDC hdc;
872 int fw_dontcare, fw_bold;
873
4eeb7d09 874 for (i = 0; i < FONT_MAXNO; i++)
374330e2 875 fonts[i] = NULL;
876
877 if (cfg.fontisbold) {
878 fw_dontcare = FW_BOLD;
5b80d07f 879 fw_bold = FW_HEAVY;
32874aea 880 } else {
374330e2 881 fw_dontcare = FW_DONTCARE;
882 fw_bold = FW_BOLD;
883 }
884
97fc891e 885 hdc = GetDC(hwnd);
886
887 font_height = cfg.fontheight;
3b2c664e 888 if (font_height > 0) {
32874aea 889 font_height =
890 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
3b2c664e 891 }
59ad2c03 892 font_width = pick_width;
97fc891e 893
374330e2 894#define f(i,c,w,u) \
97fc891e 895 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 896 c, OUT_DEFAULT_PRECIS, \
897 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
898 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 899
4eeb7d09 900 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 901
4eeb7d09 902 SelectObject(hdc, fonts[FONT_NORMAL]);
903 GetTextMetrics(hdc, &tm);
904 font_height = tm.tmHeight;
905 font_width = tm.tmAveCharWidth;
97fc891e 906
4eeb7d09 907 {
908 CHARSETINFO info;
909 DWORD cset = tm.tmCharSet;
910 memset(&info, 0xFF, sizeof(info));
97fc891e 911
4eeb7d09 912 /* !!! Yes the next line is right */
913 if (cset == OEM_CHARSET)
914 font_codepage = GetOEMCP();
915 else
916 if (TranslateCharsetInfo
917 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
918 info.ciACP;
919 else
920 font_codepage = -1;
32874aea 921
4eeb7d09 922 GetCPInfo(font_codepage, &cpinfo);
923 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
924 }
97fc891e 925
4eeb7d09 926 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 927
4eeb7d09 928 /*
929 * Some fonts, e.g. 9-pt Courier, draw their underlines
930 * outside their character cell. We successfully prevent
931 * screen corruption by clipping the text output, but then
932 * we lose the underline completely. Here we try to work
933 * out whether this is such a font, and if it is, we set a
934 * flag that causes underlines to be drawn by hand.
935 *
936 * Having tried other more sophisticated approaches (such
937 * as examining the TEXTMETRIC structure or requesting the
938 * height of a string), I think we'll do this the brute
939 * force way: we create a small bitmap, draw an underlined
940 * space on it, and test to see whether any pixels are
941 * foreground-coloured. (Since we expect the underline to
942 * go all the way across the character cell, we only search
943 * down a single column of the bitmap, half way across.)
944 */
945 {
946 HDC und_dc;
947 HBITMAP und_bm, und_oldbm;
948 int i, gotit;
949 COLORREF c;
950
951 und_dc = CreateCompatibleDC(hdc);
952 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
953 und_oldbm = SelectObject(und_dc, und_bm);
954 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
955 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
956 SetTextColor(und_dc, RGB(255, 255, 255));
957 SetBkColor(und_dc, RGB(0, 0, 0));
958 SetBkMode(und_dc, OPAQUE);
959 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
960 gotit = FALSE;
961 for (i = 0; i < font_height; i++) {
962 c = GetPixel(und_dc, font_width / 2, i);
963 if (c != RGB(0, 0, 0))
964 gotit = TRUE;
965 }
966 SelectObject(und_dc, und_oldbm);
967 DeleteObject(und_bm);
968 DeleteDC(und_dc);
969 if (!gotit) {
970 und_mode = UND_LINE;
971 DeleteObject(fonts[FONT_UNDERLINE]);
972 fonts[FONT_UNDERLINE] = 0;
32874aea 973 }
4eeb7d09 974 }
97fc891e 975
4eeb7d09 976 if (bold_mode == BOLD_FONT) {
977 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
374330e2 978 }
979#undef f
980
97fc891e 981 descent = tm.tmAscent + 1;
982 if (descent >= font_height)
983 descent = font_height - 1;
374330e2 984
4eeb7d09 985 for (i = 0; i < 3; i++) {
97fc891e 986 if (fonts[i]) {
32874aea 987 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
4eeb7d09 988 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
32874aea 989 else
4eeb7d09 990 fontsize[i] = -i;
32874aea 991 } else
4eeb7d09 992 fontsize[i] = -i;
374330e2 993 }
994
32874aea 995 ReleaseDC(hwnd, hdc);
374330e2 996
4eeb7d09 997 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
374330e2 998 und_mode = UND_LINE;
32874aea 999 DeleteObject(fonts[FONT_UNDERLINE]);
4eeb7d09 1000 fonts[FONT_UNDERLINE] = 0;
374330e2 1001 }
1002
4eeb7d09 1003 if (bold_mode == BOLD_FONT &&
1004 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
374330e2 1005 bold_mode = BOLD_SHADOW;
32874aea 1006 DeleteObject(fonts[FONT_BOLD]);
4eeb7d09 1007 fonts[FONT_BOLD] = 0;
374330e2 1008 }
4eeb7d09 1009 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1010
1011 init_ucs_tables();
1012}
1013
1014static void another_font(int fontno)
1015{
1016 int basefont;
1017 int fw_dontcare, fw_bold;
1018 int c, u, w, x;
1019 char *s;
1020
1021 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1022 return;
1023
1024 basefont = (fontno & ~(FONT_BOLDUND));
1025 if (basefont != fontno && !fontflag[basefont])
1026 another_font(basefont);
97fc891e 1027
4eeb7d09 1028 if (cfg.fontisbold) {
1029 fw_dontcare = FW_BOLD;
1030 fw_bold = FW_HEAVY;
1031 } else {
1032 fw_dontcare = FW_DONTCARE;
1033 fw_bold = FW_BOLD;
1034 }
1035
1036 c = cfg.fontcharset;
1037 w = fw_dontcare;
1038 u = FALSE;
1039 s = cfg.font;
1040 x = font_width;
1041
1042 if (fontno & FONT_WIDE)
1043 x *= 2;
1044 if (fontno & FONT_NARROW)
1045 x /= 2;
1046 if (fontno & FONT_OEM)
1047 c = OEM_CHARSET;
1048 if (fontno & FONT_BOLD)
1049 w = fw_bold;
1050 if (fontno & FONT_UNDERLINE)
1051 u = TRUE;
1052
1053 fonts[fontno] =
1054 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1055 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1056 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1057 FIXED_PITCH | FF_DONTCARE, s);
1058
1059 fontflag[fontno] = 1;
1060}
1061
1062static void deinit_fonts(void)
1063{
1064 int i;
1065 for (i = 0; i < FONT_MAXNO; i++) {
1066 if (fonts[i])
1067 DeleteObject(fonts[i]);
1068 fonts[i] = 0;
1069 fontflag[i] = 0;
374330e2 1070 }
1071}
1072
32874aea 1073void request_resize(int w, int h, int refont)
1074{
59ad2c03 1075 int width, height;
c9def1b8 1076
1077 /* If the window is maximized supress resizing attempts */
32874aea 1078 if (IsZoomed(hwnd))
1079 return;
1080
4eeb7d09 1081 if (refont && w != cols && (cols == 80 || cols == 132)) {
32874aea 1082 /* If font width too big for screen should we shrink the font more ? */
1083 if (w == 132)
1084 font_width = ((font_width * cols + w / 2) / w);
1085 else
59ad2c03 1086 font_width = 0;
4eeb7d09 1087 deinit_fonts();
32874aea 1088 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1089 und_mode = UND_FONT;
1090 init_fonts(font_width);
1091 } else {
1092 static int first_time = 1;
1093 static RECT ss;
1094
1095 switch (first_time) {
1096 case 1:
1097 /* Get the size of the screen */
1098 if (GetClientRect(GetDesktopWindow(), &ss))
1099 /* first_time = 0 */ ;
1100 else {
1101 first_time = 2;
1102 break;
1103 }
1104 case 0:
1105 /* Make sure the values are sane */
1106 width = (ss.right - ss.left - extra_width) / font_width;
1107 height = (ss.bottom - ss.top - extra_height) / font_height;
1108
1109 if (w > width)
1110 w = width;
1111 if (h > height)
1112 h = height;
1113 if (w < 15)
1114 w = 15;
1115 if (h < 1)
1116 w = 1;
1117 }
c9def1b8 1118 }
59ad2c03 1119
1120 width = extra_width + font_width * w;
1121 height = extra_height + font_height * h;
374330e2 1122
32874aea 1123 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1124 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1125 SWP_NOMOVE | SWP_NOZORDER);
374330e2 1126}
1127
32874aea 1128static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1129{
fdedf2c8 1130 int thistime = GetMessageTime();
1131
b90840c3 1132 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1133 lastbtn = MBT_NOTHING;
01c034ad 1134 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1135 return;
1136 }
1137
fdedf2c8 1138 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 1139 lastact = (lastact == MA_CLICK ? MA_2CLK :
1140 lastact == MA_2CLK ? MA_3CLK :
1141 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1142 } else {
1143 lastbtn = b;
1144 lastact = MA_CLICK;
1145 }
1146 if (lastact != MA_NOTHING)
32874aea 1147 term_mouse(b, lastact, x, y, shift, ctrl);
fdedf2c8 1148 lasttime = thistime;
374330e2 1149}
1150
01c034ad 1151/*
1152 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1153 * into a cooked one (SELECT, EXTEND, PASTE).
1154 */
32874aea 1155Mouse_Button translate_button(Mouse_Button button)
1156{
01c034ad 1157 if (button == MBT_LEFT)
1158 return MBT_SELECT;
1159 if (button == MBT_MIDDLE)
1160 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1161 if (button == MBT_RIGHT)
1162 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
2d466ffd 1163 return 0; /* shouldn't happen */
01c034ad 1164}
1165
32874aea 1166static void show_mouseptr(int show)
1167{
554c540d 1168 static int cursor_visible = 1;
32874aea 1169 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1170 show = 1;
554c540d 1171 if (cursor_visible && !show)
32874aea 1172 ShowCursor(FALSE);
554c540d 1173 else if (!cursor_visible && show)
32874aea 1174 ShowCursor(TRUE);
554c540d 1175 cursor_visible = show;
1176}
1177
32874aea 1178static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1179 WPARAM wParam, LPARAM lParam)
1180{
374330e2 1181 HDC hdc;
1182 static int ignore_size = FALSE;
1183 static int ignore_clip = FALSE;
1184 static int just_reconfigged = FALSE;
ffc31afe 1185 static int resizing = FALSE;
3ad8c6db 1186 static int need_backend_resize = FALSE;
e44d78b6 1187 static int defered_resize = FALSE;
374330e2 1188
1189 switch (message) {
59ad2c03 1190 case WM_TIMER:
1191 if (pending_netevent)
1192 enact_pending_netevent();
c9def1b8 1193 if (inbuf_head)
59ad2c03 1194 term_out();
32874aea 1195 noise_regular();
1196 HideCaret(hwnd);
59ad2c03 1197 term_update();
32874aea 1198 ShowCaret(hwnd);
1199 if (cfg.ping_interval > 0) {
1200 time_t now;
1201 time(&now);
1202 if (now - last_movement > cfg.ping_interval) {
1203 back->special(TS_PING);
1204 last_movement = now;
1205 }
1206 }
59ad2c03 1207 return 0;
374330e2 1208 case WM_CREATE:
1209 break;
68130d34 1210 case WM_CLOSE:
32874aea 1211 show_mouseptr(1);
d85548fe 1212 if (!cfg.warn_on_close || session_closed ||
32874aea 1213 MessageBox(hwnd,
1214 "Are you sure you want to close this session?",
68130d34 1215 "PuTTY Exit Confirmation",
1216 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1217 DestroyWindow(hwnd);
1218 return 0;
374330e2 1219 case WM_DESTROY:
32874aea 1220 show_mouseptr(1);
1221 PostQuitMessage(0);
374330e2 1222 return 0;
6833a413 1223 case WM_SYSCOMMAND:
1224 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1225 case IDM_SHOWLOG:
c5e9c988 1226 showeventlog(hwnd);
374330e2 1227 break;
1228 case IDM_NEWSESS:
1229 case IDM_DUPSESS:
6833a413 1230 case IDM_SAVEDSESS:
374330e2 1231 {
1232 char b[2048];
1233 char c[30], *cl;
e4e4cc7e 1234 int freecl = FALSE;
374330e2 1235 STARTUPINFO si;
1236 PROCESS_INFORMATION pi;
1237 HANDLE filemap = NULL;
1238
1239 if (wParam == IDM_DUPSESS) {
1240 /*
1241 * Allocate a file-mapping memory chunk for the
1242 * config structure.
1243 */
1244 SECURITY_ATTRIBUTES sa;
1245 Config *p;
1246
1247 sa.nLength = sizeof(sa);
1248 sa.lpSecurityDescriptor = NULL;
1249 sa.bInheritHandle = TRUE;
32874aea 1250 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1251 &sa,
1252 PAGE_READWRITE,
32874aea 1253 0, sizeof(Config), NULL);
374330e2 1254 if (filemap) {
32874aea 1255 p = (Config *) MapViewOfFile(filemap,
1256 FILE_MAP_WRITE,
1257 0, 0, sizeof(Config));
374330e2 1258 if (p) {
1259 *p = cfg; /* structure copy */
1260 UnmapViewOfFile(p);
1261 }
1262 }
1d470ad2 1263 sprintf(c, "putty &%p", filemap);
374330e2 1264 cl = c;
0a4aa984 1265 } else if (wParam == IDM_SAVEDSESS) {
32874aea 1266 char *session =
1267 sessions[(lParam - IDM_SAVED_MIN) / 16];
1268 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
e4e4cc7e 1269 if (!cl)
1270 cl = NULL; /* not a very important failure mode */
94e6450e 1271 else {
1272 sprintf(cl, "putty @%s", session);
1273 freecl = TRUE;
1274 }
374330e2 1275 } else
6833a413 1276 cl = NULL;
374330e2 1277
32874aea 1278 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1279 si.cb = sizeof(si);
1280 si.lpReserved = NULL;
1281 si.lpDesktop = NULL;
1282 si.lpTitle = NULL;
1283 si.dwFlags = 0;
1284 si.cbReserved2 = 0;
1285 si.lpReserved2 = NULL;
32874aea 1286 CreateProcess(b, cl, NULL, NULL, TRUE,
1287 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1288
1289 if (filemap)
1290 CloseHandle(filemap);
e4e4cc7e 1291 if (freecl)
dcbde236 1292 sfree(cl);
374330e2 1293 }
1294 break;
32874aea 1295 case IDM_RECONF:
1296 {
1297 int prev_alwaysontop = cfg.alwaysontop;
1298 int prev_sunken_edge = cfg.sunken_edge;
e1c8e0ed 1299 char oldlogfile[FILENAME_MAX];
1300 int oldlogtype;
3da0b1d2 1301 int need_setwpos = FALSE;
cbc3272b 1302 int old_fwidth, old_fheight;
e1c8e0ed 1303
1304 strcpy(oldlogfile, cfg.logfilename);
1305 oldlogtype = cfg.logtype;
cbc3272b 1306 old_fwidth = font_width;
1307 old_fheight = font_height;
32874aea 1308 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
e1c8e0ed 1309
32874aea 1310 if (!do_reconfig(hwnd))
1311 break;
e1c8e0ed 1312
1313 if (strcmp(oldlogfile, cfg.logfilename) ||
1314 oldlogtype != cfg.logtype) {
1315 logfclose(); /* reset logging */
1316 logfopen();
1317 }
1318
32874aea 1319 just_reconfigged = TRUE;
4eeb7d09 1320 deinit_fonts();
32874aea 1321 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1322 und_mode = UND_FONT;
1323 init_fonts(0);
1324 sfree(logpal);
1325 /*
1326 * Flush the line discipline's edit buffer in the
1327 * case where local editing has just been disabled.
1328 */
1329 ldisc_send(NULL, 0);
1330 if (pal)
1331 DeleteObject(pal);
1332 logpal = NULL;
1333 pal = NULL;
1334 cfgtopalette();
1335 init_palette();
1336
1337 /* Enable or disable the scroll bar, etc */
1338 {
1339 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1340 LONG nexflag, exflag =
1341 GetWindowLong(hwnd, GWL_EXSTYLE);
1342
1343 nexflag = exflag;
1344 if (cfg.alwaysontop != prev_alwaysontop) {
1345 if (cfg.alwaysontop) {
1346 nexflag |= WS_EX_TOPMOST;
1347 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1348 SWP_NOMOVE | SWP_NOSIZE);
1349 } else {
1350 nexflag &= ~(WS_EX_TOPMOST);
1351 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1352 SWP_NOMOVE | SWP_NOSIZE);
1353 }
1354 }
1355 if (cfg.sunken_edge)
1356 nexflag |= WS_EX_CLIENTEDGE;
1357 else
1358 nexflag &= ~(WS_EX_CLIENTEDGE);
1359
1360 nflg = flag;
1361 if (cfg.scrollbar)
1362 nflg |= WS_VSCROLL;
1363 else
1364 nflg &= ~WS_VSCROLL;
1365 if (cfg.locksize)
1366 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1367 else
1368 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1369
1370 if (nflg != flag || nexflag != exflag) {
1371 RECT cr, wr;
1372
1373 if (nflg != flag)
1374 SetWindowLong(hwnd, GWL_STYLE, nflg);
1375 if (nexflag != exflag)
1376 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1377
1378 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1379
1380 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1381 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1382 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1383 | SWP_FRAMECHANGED);
1384
1385 GetWindowRect(hwnd, &wr);
1386 GetClientRect(hwnd, &cr);
1387 extra_width =
1388 wr.right - wr.left - cr.right + cr.left;
1389 extra_height =
1390 wr.bottom - wr.top - cr.bottom + cr.top;
e44d78b6 1391 need_setwpos = TRUE;
32874aea 1392 }
1393 }
c9def1b8 1394
3da0b1d2 1395 if (cfg.height != rows ||
1396 cfg.width != cols ||
cbc3272b 1397 old_fwidth != font_width ||
1398 old_fheight != font_height ||
57d08f2f 1399 cfg.savelines != savelines ||
32874aea 1400 cfg.sunken_edge != prev_sunken_edge)
1401 need_setwpos = TRUE;
e44d78b6 1402
1403 if (IsZoomed(hwnd)) {
1404 int w, h;
1405 RECT cr;
1406 if (need_setwpos)
1407 defered_resize = TRUE;
1408
1409 GetClientRect(hwnd, &cr);
1410 w = cr.right - cr.left;
1411 h = cr.bottom - cr.top;
1412 w = w / font_width;
1413 if (w < 1)
1414 w = 1;
1415 h = h / font_height;
1416 if (h < 1)
1417 h = 1;
1418
1419 term_size(h, w, cfg.savelines);
1420 InvalidateRect(hwnd, NULL, TRUE);
1421 back->size();
1422 } else {
1423 term_size(cfg.height, cfg.width, cfg.savelines);
1424 InvalidateRect(hwnd, NULL, TRUE);
1425 if (need_setwpos) {
1426 SetWindowPos(hwnd, NULL, 0, 0,
1427 extra_width + font_width * cfg.width,
1428 extra_height +
1429 font_height * cfg.height,
1430 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1431 SWP_NOMOVE | SWP_NOZORDER);
1432 }
32874aea 1433 }
e44d78b6 1434 /* Oops */
1435 if (cfg.locksize && IsZoomed(hwnd))
1436 force_normal(hwnd);
32874aea 1437 set_title(cfg.wintitle);
1438 if (IsIconic(hwnd)) {
1439 SetWindowText(hwnd,
1440 cfg.win_name_always ? window_name :
1441 icon_name);
3da0b1d2 1442 }
32874aea 1443 }
1444 break;
bc1235d4 1445 case IDM_COPYALL:
1446 term_copyall();
1447 break;
32874aea 1448 case IDM_CLRSB:
1449 term_clrsb();
1450 break;
1451 case IDM_RESET:
1452 term_pwron();
1453 break;
1454 case IDM_TEL_AYT:
1455 back->special(TS_AYT);
1456 break;
1457 case IDM_TEL_BRK:
1458 back->special(TS_BRK);
1459 break;
1460 case IDM_TEL_SYNCH:
1461 back->special(TS_SYNCH);
1462 break;
1463 case IDM_TEL_EC:
1464 back->special(TS_EC);
1465 break;
1466 case IDM_TEL_EL:
1467 back->special(TS_EL);
1468 break;
1469 case IDM_TEL_GA:
1470 back->special(TS_GA);
1471 break;
1472 case IDM_TEL_NOP:
1473 back->special(TS_NOP);
1474 break;
1475 case IDM_TEL_ABORT:
1476 back->special(TS_ABORT);
1477 break;
1478 case IDM_TEL_AO:
1479 back->special(TS_AO);
1480 break;
1481 case IDM_TEL_IP:
1482 back->special(TS_IP);
1483 break;
1484 case IDM_TEL_SUSP:
1485 back->special(TS_SUSP);
1486 break;
1487 case IDM_TEL_EOR:
1488 back->special(TS_EOR);
1489 break;
1490 case IDM_TEL_EOF:
1491 back->special(TS_EOF);
1492 break;
374330e2 1493 case IDM_ABOUT:
32874aea 1494 showabout(hwnd);
374330e2 1495 break;
dfca2656 1496 case SC_KEYMENU:
1497 /*
1498 * We get this if the System menu has been activated.
1499 * This might happen from within TranslateKey, in which
1500 * case it really wants to be followed by a `space'
1501 * character to actually _bring the menu up_ rather
1502 * than just sitting there in `ready to appear' state.
1503 */
1504 if( lParam == 0 )
1505 PostMessage(hwnd, WM_CHAR, ' ', 0);
1506 break;
32874aea 1507 default:
1508 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1509 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1510 }
374330e2 1511 }
1512 break;
37508af4 1513
1514#define X_POS(l) ((int)(short)LOWORD(l))
1515#define Y_POS(l) ((int)(short)HIWORD(l))
1516
fdedf2c8 1517#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1518#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
01c034ad 1519#define WHEEL_DELTA 120
1520 case WM_MOUSEWHEEL:
1521 {
32874aea 1522 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1523 wParam = LOWORD(wParam);
1524
1525 /* process events when the threshold is reached */
1526 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1527 int b;
fdedf2c8 1528
01c034ad 1529 /* reduce amount for next time */
1530 if (wheel_accumulator > 0) {
1531 b = MBT_WHEEL_UP;
1532 wheel_accumulator -= WHEEL_DELTA;
32874aea 1533 } else if (wheel_accumulator < 0) {
01c034ad 1534 b = MBT_WHEEL_DOWN;
1535 wheel_accumulator += WHEEL_DELTA;
32874aea 1536 } else
01c034ad 1537 break;
1538
1539 if (send_raw_mouse) {
1540 /* send a mouse-down followed by a mouse up */
1541 term_mouse(b,
1542 MA_CLICK,
32874aea 1543 TO_CHR_X(X_POS(lParam)),
1544 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1545 wParam & MK_CONTROL);
1546 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1547 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1548 wParam & MK_CONTROL);
01c034ad 1549 } else {
1550 /* trigger a scroll */
32874aea 1551 term_scroll(0,
1552 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1553 }
1554 }
1555 return 0;
1556 }
374330e2 1557 case WM_LBUTTONDOWN:
374330e2 1558 case WM_MBUTTONDOWN:
374330e2 1559 case WM_RBUTTONDOWN:
01c034ad 1560 case WM_LBUTTONUP:
1561 case WM_MBUTTONUP:
374330e2 1562 case WM_RBUTTONUP:
01c034ad 1563 {
1564 int button, press;
1565 switch (message) {
32874aea 1566 case WM_LBUTTONDOWN:
1567 button = MBT_LEFT;
1568 press = 1;
1569 break;
1570 case WM_MBUTTONDOWN:
1571 button = MBT_MIDDLE;
1572 press = 1;
1573 break;
1574 case WM_RBUTTONDOWN:
1575 button = MBT_RIGHT;
1576 press = 1;
1577 break;
1578 case WM_LBUTTONUP:
1579 button = MBT_LEFT;
1580 press = 0;
1581 break;
1582 case WM_MBUTTONUP:
1583 button = MBT_MIDDLE;
1584 press = 0;
1585 break;
1586 case WM_RBUTTONUP:
1587 button = MBT_RIGHT;
1588 press = 0;
1589 break;
2d466ffd 1590 default:
1591 button = press = 0; /* shouldn't happen */
01c034ad 1592 }
1593 show_mouseptr(1);
1594 if (press) {
32874aea 1595 click(button,
1596 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1597 wParam & MK_SHIFT, wParam & MK_CONTROL);
01c034ad 1598 SetCapture(hwnd);
1599 } else {
32874aea 1600 term_mouse(button, MA_RELEASE,
1601 TO_CHR_X(X_POS(lParam)),
1602 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1603 wParam & MK_CONTROL);
01c034ad 1604 ReleaseCapture();
1605 }
1606 }
374330e2 1607 return 0;
1608 case WM_MOUSEMOVE:
32874aea 1609 show_mouseptr(1);
374330e2 1610 /*
1611 * Add the mouse position and message time to the random
7d6ee6ff 1612 * number noise.
374330e2 1613 */
32874aea 1614 noise_ultralight(lParam);
374330e2 1615
1616 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1617 Mouse_Button b;
1618 if (wParam & MK_LBUTTON)
6c6e7711 1619 b = MBT_LEFT;
374330e2 1620 else if (wParam & MK_MBUTTON)
6c6e7711 1621 b = MBT_MIDDLE;
374330e2 1622 else
6c6e7711 1623 b = MBT_RIGHT;
32874aea 1624 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1625 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1626 wParam & MK_CONTROL);
374330e2 1627 }
374330e2 1628 return 0;
d318ef8e 1629 case WM_NCMOUSEMOVE:
1630 show_mouseptr(1);
32874aea 1631 noise_ultralight(lParam);
d318ef8e 1632 return 0;
374330e2 1633 case WM_IGNORE_CLIP:
1634 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1635 break;
1636 case WM_DESTROYCLIPBOARD:
1637 if (!ignore_clip)
1638 term_deselect();
1639 ignore_clip = FALSE;
1640 return 0;
1641 case WM_PAINT:
1642 {
1643 PAINTSTRUCT p;
32874aea 1644 HideCaret(hwnd);
1645 hdc = BeginPaint(hwnd, &p);
374330e2 1646 if (pal) {
32874aea 1647 SelectPalette(hdc, pal, TRUE);
1648 RealizePalette(hdc);
374330e2 1649 }
32874aea 1650 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1651 p.rcPaint.right, p.rcPaint.bottom);
1652 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1653 SelectObject(hdc, GetStockObject(WHITE_PEN));
1654 EndPaint(hwnd, &p);
1655 ShowCaret(hwnd);
374330e2 1656 }
1657 return 0;
1658 case WM_NETEVENT:
59ad2c03 1659 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1660 * but the only one that's likely to try to overload us is FD_READ.
1661 * This means buffering just one is fine.
1662 */
1663 if (pending_netevent)
1664 enact_pending_netevent();
1665
1666 pending_netevent = TRUE;
32874aea 1667 pend_netevent_wParam = wParam;
1668 pend_netevent_lParam = lParam;
ec55b220 1669 time(&last_movement);
374330e2 1670 return 0;
1671 case WM_SETFOCUS:
1672 has_focus = TRUE;
32874aea 1673 CreateCaret(hwnd, caretbm, font_width, font_height);
1674 ShowCaret(hwnd);
f8a28d1f 1675 flash_window(0); /* stop */
32874aea 1676 compose_state = 0;
374330e2 1677 term_out();
1678 term_update();
1679 break;
1680 case WM_KILLFOCUS:
32874aea 1681 show_mouseptr(1);
374330e2 1682 has_focus = FALSE;
32874aea 1683 DestroyCaret();
374330e2 1684 term_out();
1685 term_update();
1686 break;
1687 case WM_IGNORE_SIZE:
1688 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1689 break;
73251d5d 1690 case WM_ENTERSIZEMOVE:
32874aea 1691 EnableSizeTip(1);
1692 resizing = TRUE;
3ad8c6db 1693 need_backend_resize = FALSE;
32874aea 1694 break;
73251d5d 1695 case WM_EXITSIZEMOVE:
32874aea 1696 EnableSizeTip(0);
1697 resizing = FALSE;
3ad8c6db 1698 if (need_backend_resize)
1699 back->size();
32874aea 1700 break;
374330e2 1701 case WM_SIZING:
1702 {
1703 int width, height, w, h, ew, eh;
32874aea 1704 LPRECT r = (LPRECT) lParam;
374330e2 1705
1706 width = r->right - r->left - extra_width;
1707 height = r->bottom - r->top - extra_height;
32874aea 1708 w = (width + font_width / 2) / font_width;
1709 if (w < 1)
1710 w = 1;
1711 h = (height + font_height / 2) / font_height;
1712 if (h < 1)
1713 h = 1;
1714 UpdateSizeTip(hwnd, w, h);
374330e2 1715 ew = width - w * font_width;
1716 eh = height - h * font_height;
1717 if (ew != 0) {
1718 if (wParam == WMSZ_LEFT ||
32874aea 1719 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 1720 r->left += ew;
1721 else
1722 r->right -= ew;
1723 }
1724 if (eh != 0) {
1725 if (wParam == WMSZ_TOP ||
32874aea 1726 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 1727 r->top += eh;
1728 else
1729 r->bottom -= eh;
1730 }
1731 if (ew || eh)
1732 return 1;
1733 else
1734 return 0;
1735 }
32874aea 1736 /* break; (never reached) */
374330e2 1737 case WM_SIZE:
1738 if (wParam == SIZE_MINIMIZED) {
32874aea 1739 SetWindowText(hwnd,
1740 cfg.win_name_always ? window_name : icon_name);
374330e2 1741 break;
1742 }
1743 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 1744 SetWindowText(hwnd, window_name);
374330e2 1745 if (!ignore_size) {
1a6f78fe 1746 int width, height, w, h;
32874aea 1747#if 0 /* we have fixed this using WM_SIZING now */
1748 int ew, eh;
1a6f78fe 1749#endif
374330e2 1750
1751 width = LOWORD(lParam);
1752 height = HIWORD(lParam);
32874aea 1753 w = width / font_width;
1754 if (w < 1)
1755 w = 1;
1756 h = height / font_height;
1757 if (h < 1)
1758 h = 1;
1759#if 0 /* we have fixed this using WM_SIZING now */
374330e2 1760 ew = width - w * font_width;
1761 eh = height - h * font_height;
1762 if (ew != 0 || eh != 0) {
1763 RECT r;
32874aea 1764 GetWindowRect(hwnd, &r);
1765 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1766 SetWindowPos(hwnd, NULL, 0, 0,
1767 r.right - r.left - ew, r.bottom - r.top - eh,
1768 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
374330e2 1769 }
1770#endif
1771 if (w != cols || h != rows || just_reconfigged) {
1772 term_invalidate();
32874aea 1773 term_size(h, w, cfg.savelines);
1774 /*
1775 * Don't call back->size in mid-resize. (To prevent
1776 * massive numbers of resize events getting sent
1777 * down the connection during an NT opaque drag.)
1778 */
1779 if (!resizing)
1780 back->size();
e44d78b6 1781 else {
3ad8c6db 1782 need_backend_resize = TRUE;
e44d78b6 1783 cfg.height = h;
1784 cfg.width = w;
1785 }
374330e2 1786 just_reconfigged = FALSE;
1787 }
1788 }
e44d78b6 1789 if (wParam == SIZE_RESTORED && defered_resize) {
1790 defered_resize = FALSE;
1791 SetWindowPos(hwnd, NULL, 0, 0,
1792 extra_width + font_width * cfg.width,
1793 extra_height + font_height * cfg.height,
1794 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1795 SWP_NOMOVE | SWP_NOZORDER);
1796 }
374330e2 1797 ignore_size = FALSE;
1798 return 0;
1799 case WM_VSCROLL:
1800 switch (LOWORD(wParam)) {
32874aea 1801 case SB_BOTTOM:
1802 term_scroll(-1, 0);
1803 break;
1804 case SB_TOP:
1805 term_scroll(+1, 0);
1806 break;
1807 case SB_LINEDOWN:
1808 term_scroll(0, +1);
1809 break;
1810 case SB_LINEUP:
1811 term_scroll(0, -1);
1812 break;
1813 case SB_PAGEDOWN:
1814 term_scroll(0, +rows / 2);
1815 break;
1816 case SB_PAGEUP:
1817 term_scroll(0, -rows / 2);
1818 break;
1819 case SB_THUMBPOSITION:
1820 case SB_THUMBTRACK:
1821 term_scroll(1, HIWORD(wParam));
1822 break;
1823 }
1824 break;
1825 case WM_PALETTECHANGED:
374330e2 1826 if ((HWND) wParam != hwnd && pal != NULL) {
1827 HDC hdc = get_ctx();
1828 if (hdc) {
32874aea 1829 if (RealizePalette(hdc) > 0)
1830 UpdateColors(hdc);
1831 free_ctx(hdc);
374330e2 1832 }
1833 }
1834 break;
1835 case WM_QUERYNEWPALETTE:
1836 if (pal != NULL) {
1837 HDC hdc = get_ctx();
1838 if (hdc) {
32874aea 1839 if (RealizePalette(hdc) > 0)
1840 UpdateColors(hdc);
1841 free_ctx(hdc);
374330e2 1842 return TRUE;
1843 }
1844 }
1845 return FALSE;
1846 case WM_KEYDOWN:
1847 case WM_SYSKEYDOWN:
c9def1b8 1848 case WM_KEYUP:
1849 case WM_SYSKEYUP:
374330e2 1850 /*
1851 * Add the scan code and keypress timing to the random
7d6ee6ff 1852 * number noise.
374330e2 1853 */
32874aea 1854 noise_ultralight(lParam);
374330e2 1855
1856 /*
1857 * We don't do TranslateMessage since it disassociates the
1858 * resulting CHAR message from the KEYDOWN that sparked it,
1859 * which we occasionally don't want. Instead, we process
1860 * KEYDOWN, and call the Win32 translator functions so that
1861 * we get the translations under _our_ control.
1862 */
1863 {
1864 unsigned char buf[20];
1865 int len;
1866
32874aea 1867 if (wParam == VK_PROCESSKEY) {
3cf144db 1868 MSG m;
32874aea 1869 m.hwnd = hwnd;
1870 m.message = WM_KEYDOWN;
1871 m.wParam = wParam;
1872 m.lParam = lParam & 0xdfff;
1873 TranslateMessage(&m);
1874 } else {
1875 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 1876 if (len == -1)
32874aea 1877 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 1878
b2a1eade 1879 if (len != 0) {
bca9517a 1880 /*
1881 * We need not bother about stdin backlogs
1882 * here, because in GUI PuTTY we can't do
1883 * anything about it anyway; there's no means
1884 * of asking Windows to hold off on KEYDOWN
1885 * messages. We _have_ to buffer everything
1886 * we're sent.
1887 */
1888 ldisc_send(buf, len);
32874aea 1889 show_mouseptr(0);
bca9517a 1890 }
3cf144db 1891 }
374330e2 1892 }
1893 return 0;
4eeb7d09 1894 case WM_INPUTLANGCHANGE:
3cf144db 1895 {
4eeb7d09 1896 /* wParam == Font number */
1897 /* lParam == Locale */
1898 char lbuf[20];
1899 HKL NewInputLocale = (HKL) lParam;
1900
1901 // lParam == GetKeyboardLayout(0);
1902
1903 GetLocaleInfo(LOWORD(NewInputLocale),
1904 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1905
1906 kbd_codepage = atoi(lbuf);
1907 }
1908 break;
88485e4d 1909 case WM_IME_COMPOSITION:
1910 {
1911 HIMC hIMC;
1912 int n;
1913 char *buff;
1914
1915 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
1916 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1917
1918 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1919 break; /* fall back to DefWindowProc */
1920
1921 hIMC = ImmGetContext(hwnd);
1922 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1923
1924 if (n > 0) {
1925 buff = (char*) smalloc(n);
1926 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1927 luni_send((unsigned short *)buff, n / 2);
1928 free(buff);
1929 }
1930 ImmReleaseContext(hwnd, hIMC);
1931 return 1;
1932 }
1933
4eeb7d09 1934 case WM_IME_CHAR:
1935 if (wParam & 0xFF00) {
3cf144db 1936 unsigned char buf[2];
1937
1938 buf[1] = wParam;
1939 buf[0] = wParam >> 8;
4eeb7d09 1940 lpage_send(kbd_codepage, buf, 2);
1941 } else {
1942 char c = (unsigned char) wParam;
1943 lpage_send(kbd_codepage, &c, 1);
3cf144db 1944 }
4eeb7d09 1945 return (0);
374330e2 1946 case WM_CHAR:
1947 case WM_SYSCHAR:
1948 /*
1949 * Nevertheless, we are prepared to deal with WM_CHAR
1950 * messages, should they crop up. So if someone wants to
1951 * post the things to us as part of a macro manoeuvre,
1952 * we're ready to cope.
1953 */
32874aea 1954 {
4eeb7d09 1955 char c = (unsigned char)wParam;
1956 lpage_send(CP_ACP, &c, 1);
374330e2 1957 }
1958 return 0;
32874aea 1959 case WM_SETCURSOR:
1960 if (send_raw_mouse) {
1961 SetCursor(LoadCursor(NULL, IDC_ARROW));
1962 return TRUE;
1963 }
374330e2 1964 }
1965
32874aea 1966 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 1967}
1968
1969/*
ec8679e9 1970 * Move the system caret. (We maintain one, even though it's
1971 * invisible, for the benefit of blind people: apparently some
1972 * helper software tracks the system caret, so we should arrange to
1973 * have one.)
1974 */
32874aea 1975void sys_cursor(int x, int y)
1976{
88485e4d 1977 COMPOSITIONFORM cf;
1978 HIMC hIMC;
1979
1980 if (!has_focus) return;
1981
1982 SetCaretPos(x * font_width, y * font_height);
1983
1984 /* IMM calls on Win98 and beyond only */
1985 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1986
1987 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1988 osVersion.dwMinorVersion == 0) return; /* 95 */
1989
1990 /* we should have the IMM functions */
1991 hIMC = ImmGetContext(hwnd);
1992 cf.dwStyle = CFS_POINT;
1993 cf.ptCurrentPos.x = x * font_width;
1994 cf.ptCurrentPos.y = y * font_height;
1995 ImmSetCompositionWindow(hIMC, &cf);
1996
1997 ImmReleaseContext(hwnd, hIMC);
ec8679e9 1998}
1999
2000/*
374330e2 2001 * Draw a line of text in the window, at given character
2002 * coordinates, in given attributes.
2003 *
2004 * We are allowed to fiddle with the contents of `text'.
2005 */
32874aea 2006void do_text(Context ctx, int x, int y, char *text, int len,
2007 unsigned long attr, int lattr)
2008{
374330e2 2009 COLORREF fg, bg, t;
2010 int nfg, nbg, nfont;
2011 HDC hdc = ctx;
59ad2c03 2012 RECT line_box;
2013 int force_manual_underline = 0;
32874aea 2014 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2015 int char_width = fnt_width;
2016 int text_adjust = 0;
2017 static int *IpDx = 0, IpDxLEN = 0;
2018
2019 if (attr & ATTR_WIDE)
2020 char_width *= 2;
59ad2c03 2021
4eeb7d09 2022 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2023 int i;
32874aea 2024 if (len > IpDxLEN) {
59ad2c03 2025 sfree(IpDx);
32874aea 2026 IpDx = smalloc((len + 16) * sizeof(int));
2027 IpDxLEN = (len + 16);
59ad2c03 2028 }
32874aea 2029 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2030 IpDx[i] = char_width;
59ad2c03 2031 }
374330e2 2032
c9def1b8 2033 x *= fnt_width;
374330e2 2034 y *= font_height;
2035
4eeb7d09 2036 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2037 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2038 attr ^= ATTR_CUR_XOR;
2039 }
2040
2041 nfont = 0;
4eeb7d09 2042 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2043 /* Assume a poorman font is borken in other ways too. */
2044 lattr = LATTR_WIDE;
2045 } else
2046 switch (lattr) {
2047 case LATTR_NORM:
2048 break;
2049 case LATTR_WIDE:
2050 nfont |= FONT_WIDE;
374330e2 2051 break;
4eeb7d09 2052 default:
2053 nfont |= FONT_WIDE + FONT_HIGH;
2054 break;
2055 }
2056
2057 /* Special hack for the VT100 linedraw glyphs. */
2058 if ((attr & CSET_MASK) == 0x2300) {
2059 if (!dbcs_screenfont &&
2060 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2061 switch ((unsigned char) (text[0])) {
2062 case 0xBA:
2063 text_adjust = -2 * font_height / 5;
2064 break;
2065 case 0xBB:
2066 text_adjust = -1 * font_height / 5;
2067 break;
2068 case 0xBC:
2069 text_adjust = font_height / 5;
2070 break;
2071 case 0xBD:
2072 text_adjust = 2 * font_height / 5;
32874aea 2073 break;
59ad2c03 2074 }
4eeb7d09 2075 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2076 text_adjust *= 2;
2077 attr &= ~CSET_MASK;
2078 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2079 attr |= (unitab_xterm['q'] & CSET_MASK);
2080 if (attr & ATTR_UNDER) {
2081 attr &= ~ATTR_UNDER;
2082 force_manual_underline = 1;
2083 }
374330e2 2084 }
2085 }
2086
4eeb7d09 2087 /* Anything left as an original character set is unprintable. */
2088 if (DIRECT_CHAR(attr)) {
2089 attr &= ~CSET_MASK;
2090 attr |= 0xFF00;
2091 memset(text, 0xFF, len);
2092 }
2093
2094 /* OEM CP */
2095 if ((attr & CSET_MASK) == ATTR_OEMCP)
2096 nfont |= FONT_OEM;
2097
374330e2 2098 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2099 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2100 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2101 nfont |= FONT_BOLD;
2102 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2103 nfont |= FONT_UNDERLINE;
4eeb7d09 2104 another_font(nfont);
32874aea 2105 if (!fonts[nfont]) {
2106 if (nfont & FONT_UNDERLINE)
59ad2c03 2107 force_manual_underline = 1;
2108 /* Don't do the same for manual bold, it could be bad news. */
2109
32874aea 2110 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2111 }
4eeb7d09 2112 another_font(nfont);
2113 if (!fonts[nfont])
2114 nfont = FONT_NORMAL;
374330e2 2115 if (attr & ATTR_REVERSE) {
32874aea 2116 t = nfg;
2117 nfg = nbg;
2118 nbg = t;
374330e2 2119 }
2120 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2121 nfg++;
59ad2c03 2122 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2123 nbg++;
374330e2 2124 fg = colours[nfg];
2125 bg = colours[nbg];
32874aea 2126 SelectObject(hdc, fonts[nfont]);
2127 SetTextColor(hdc, fg);
2128 SetBkColor(hdc, bg);
2129 SetBkMode(hdc, OPAQUE);
2130 line_box.left = x;
2131 line_box.top = y;
4eeb7d09 2132 line_box.right = x + char_width * len;
32874aea 2133 line_box.bottom = y + font_height;
4eeb7d09 2134
2135 /* We're using a private area for direct to font. (512 chars.) */
2136 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2137 /* Ho Hum, dbcs fonts are a PITA! */
2138 /* To display on W9x I have to convert to UCS */
2139 static wchar_t *uni_buf = 0;
2140 static int uni_len = 0;
2141 int nlen;
2142 if (len > uni_len) {
2143 sfree(uni_buf);
2144 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2145 }
2146 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2147 text, len, uni_buf, uni_len);
2148
2149 if (nlen <= 0)
2150 return; /* Eeek! */
2151
2152 ExtTextOutW(hdc, x,
2153 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2154 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2155 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2156 SetBkMode(hdc, TRANSPARENT);
2157 ExtTextOutW(hdc, x - 1,
2158 y - font_height * (lattr ==
2159 LATTR_BOT) + text_adjust,
2160 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2161 }
2162 } else if (DIRECT_FONT(attr)) {
2163 ExtTextOut(hdc, x,
2164 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2165 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2166 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2167 SetBkMode(hdc, TRANSPARENT);
2168
2169 /* GRR: This draws the character outside it's box and can leave
2170 * 'droppings' even with the clip box! I suppose I could loop it
2171 * one character at a time ... yuk.
2172 *
2173 * Or ... I could do a test print with "W", and use +1 or -1 for this
2174 * shift depending on if the leftmost column is blank...
2175 */
2176 ExtTextOut(hdc, x - 1,
2177 y - font_height * (lattr ==
2178 LATTR_BOT) + text_adjust,
2179 ETO_CLIPPED, &line_box, text, len, IpDx);
2180 }
2181 } else {
2182 /* And 'normal' unicode characters */
2183 static WCHAR *wbuf = NULL;
2184 static int wlen = 0;
2185 int i;
2186 if (wlen < len) {
2187 sfree(wbuf);
2188 wlen = len;
2189 wbuf = smalloc(wlen * sizeof(WCHAR));
2190 }
2191 for (i = 0; i < len; i++)
2192 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2193
2194 ExtTextOutW(hdc, x,
2195 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2196 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2197
2198 /* And the shadow bold hack. */
2199 if (bold_mode == BOLD_SHADOW) {
2200 SetBkMode(hdc, TRANSPARENT);
2201 ExtTextOutW(hdc, x - 1,
2202 y - font_height * (lattr ==
2203 LATTR_BOT) + text_adjust,
2204 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2205 }
374330e2 2206 }
4eeb7d09 2207 if (lattr != LATTR_TOP && (force_manual_underline ||
2208 (und_mode == UND_LINE
2209 && (attr & ATTR_UNDER)))) {
32874aea 2210 HPEN oldpen;
4eeb7d09 2211 int dec = descent;
2212 if (lattr == LATTR_BOT)
2213 dec = dec * 2 - font_height;
2214
32874aea 2215 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2216 MoveToEx(hdc, x, y + dec, NULL);
2217 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2218 oldpen = SelectObject(hdc, oldpen);
2219 DeleteObject(oldpen);
374330e2 2220 }
4eeb7d09 2221}
2222
2223void do_cursor(Context ctx, int x, int y, char *text, int len,
2224 unsigned long attr, int lattr)
2225{
2226
2227 int fnt_width;
2228 int char_width;
2229 HDC hdc = ctx;
2230 int ctype = cfg.cursor_type;
2231
2232 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2233 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2234 do_text(ctx, x, y, text, len, attr, lattr);
2235 return;
2236 }
2237 ctype = 2;
2238 attr |= TATTR_RIGHTCURS;
2239 }
2240
2241 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2242 if (attr & ATTR_WIDE)
2243 char_width *= 2;
2244 x *= fnt_width;
2245 y *= font_height;
2246
2247 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2248 POINT pts[5];
32874aea 2249 HPEN oldpen;
374330e2 2250 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2251 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2252 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2253 pts[1].y = pts[2].y = y + font_height - 1;
2254 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2255 Polyline(hdc, pts, 5);
2256 oldpen = SelectObject(hdc, oldpen);
2257 DeleteObject(oldpen);
4eeb7d09 2258 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2259 int startx, starty, dx, dy, length, i;
4eeb7d09 2260 if (ctype == 1) {
32874aea 2261 startx = x;
2262 starty = y + descent;
2263 dx = 1;
2264 dy = 0;
4eeb7d09 2265 length = char_width;
32874aea 2266 } else {
4e30ff69 2267 int xadjust = 0;
4eeb7d09 2268 if (attr & TATTR_RIGHTCURS)
2269 xadjust = char_width - 1;
32874aea 2270 startx = x + xadjust;
2271 starty = y;
2272 dx = 0;
2273 dy = 1;
2274 length = font_height;
2275 }
4eeb7d09 2276 if (attr & TATTR_ACTCURS) {
32874aea 2277 HPEN oldpen;
2278 oldpen =
2279 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2280 MoveToEx(hdc, startx, starty, NULL);
2281 LineTo(hdc, startx + dx * length, starty + dy * length);
2282 oldpen = SelectObject(hdc, oldpen);
2283 DeleteObject(oldpen);
2284 } else {
2285 for (i = 0; i < length; i++) {
2286 if (i % 2 == 0) {
2287 SetPixel(hdc, startx, starty, colours[23]);
2288 }
2289 startx += dx;
2290 starty += dy;
2291 }
2292 }
4e30ff69 2293 }
374330e2 2294}
2295
2296/*
c9def1b8 2297 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2298 * codes. Returns number of bytes used or zero to drop the message
2299 * or -1 to forward the message to windows.
374330e2 2300 */
3cf144db 2301static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2302 unsigned char *output)
2303{
374330e2 2304 BYTE keystate[256];
32874aea 2305 int scan, left_alt = 0, key_down, shift_state;
2306 int r, i, code;
2307 unsigned char *p = output;
4eeb7d09 2308 static int alt_sum = 0;
374330e2 2309
00e3ba0f 2310 HKL kbd_layout = GetKeyboardLayout(0);
2311
0c50ef57 2312 static WORD keys[3];
0c50ef57 2313 static int compose_char = 0;
2314 static WPARAM compose_key = 0;
32874aea 2315
c9def1b8 2316 r = GetKeyboardState(keystate);
32874aea 2317 if (!r)
2318 memset(keystate, 0, sizeof(keystate));
2319 else {
ec55b220 2320#if 0
4eeb7d09 2321#define SHOW_TOASCII_RESULT
32874aea 2322 { /* Tell us all about key events */
2323 static BYTE oldstate[256];
2324 static int first = 1;
2325 static int scan;
2326 int ch;
2327 if (first)
2328 memcpy(oldstate, keystate, sizeof(oldstate));
2329 first = 0;
2330
2331 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2332 debug(("+"));
2333 } else if ((HIWORD(lParam) & KF_UP)
2334 && scan == (HIWORD(lParam) & 0xFF)) {
2335 debug((". U"));
2336 } else {
2337 debug((".\n"));
2338 if (wParam >= VK_F1 && wParam <= VK_F20)
2339 debug(("K_F%d", wParam + 1 - VK_F1));
2340 else
2341 switch (wParam) {
2342 case VK_SHIFT:
2343 debug(("SHIFT"));
2344 break;
2345 case VK_CONTROL:
2346 debug(("CTRL"));
2347 break;
2348 case VK_MENU:
2349 debug(("ALT"));
2350 break;
2351 default:
2352 debug(("VK_%02x", wParam));
2353 }
2354 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2355 debug(("*"));
2356 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2357
2358 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2359 if (ch >= ' ' && ch <= '~')
2360 debug((", '%c'", ch));
2361 else if (ch)
2362 debug((", $%02x", ch));
2363
2364 if (keys[0])
2365 debug((", KB0=%02x", keys[0]));
2366 if (keys[1])
2367 debug((", KB1=%02x", keys[1]));
2368 if (keys[2])
2369 debug((", KB2=%02x", keys[2]));
2370
2371 if ((keystate[VK_SHIFT] & 0x80) != 0)
2372 debug((", S"));
2373 if ((keystate[VK_CONTROL] & 0x80) != 0)
2374 debug((", C"));
2375 if ((HIWORD(lParam) & KF_EXTENDED))
2376 debug((", E"));
2377 if ((HIWORD(lParam) & KF_UP))
2378 debug((", U"));
2379 }
2380
2381 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2382 else if ((HIWORD(lParam) & KF_UP))
2383 oldstate[wParam & 0xFF] ^= 0x80;
2384 else
2385 oldstate[wParam & 0xFF] ^= 0x81;
2386
2387 for (ch = 0; ch < 256; ch++)
2388 if (oldstate[ch] != keystate[ch])
2389 debug((", M%02x=%02x", ch, keystate[ch]));
2390
2391 memcpy(oldstate, keystate, sizeof(oldstate));
2392 }
ec55b220 2393#endif
2394
32874aea 2395 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2396 keystate[VK_RMENU] = keystate[VK_MENU];
2397 }
2398
c9def1b8 2399
2400 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2401 if ((cfg.funky_type == 3 ||
2402 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2403 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2404
2405 wParam = VK_EXECUTE;
2406
2407 /* UnToggle NUMLock */
32874aea 2408 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2409 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2410 }
2411
2412 /* And write back the 'adjusted' state */
32874aea 2413 SetKeyboardState(keystate);
c9def1b8 2414 }
2415
2416 /* Disable Auto repeat if required */
32874aea 2417 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2418 return 0;
c9def1b8 2419
32874aea 2420 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2421 left_alt = 1;
2422
32874aea 2423 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2424
95bbe1ae 2425 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2426 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2427 if (cfg.ctrlaltkeys)
2428 keystate[VK_MENU] = 0;
2429 else {
2430 keystate[VK_RMENU] = 0x80;
2431 left_alt = 0;
2432 }
2433 }
c9def1b8 2434
2435 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2436 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2437 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2438
95bbe1ae 2439 /* Note if AltGr was pressed and if it was used as a compose key */
2440 if (!compose_state) {
159eba53 2441 compose_key = 0x100;
95bbe1ae 2442 if (cfg.compose_key) {
32874aea 2443 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2444 compose_key = wParam;
2445 }
2446 if (wParam == VK_APPS)
2447 compose_key = wParam;
2448 }
2449
32874aea 2450 if (wParam == compose_key) {
2451 if (compose_state == 0
2452 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2453 1;
2454 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2455 compose_state = 2;
2456 else
2457 compose_state = 0;
32874aea 2458 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2459 compose_state = 0;
2460
67c339f7 2461 /*
cabfd08c 2462 * Record that we pressed key so the scroll window can be reset, but
2463 * be careful to avoid Shift-UP/Down
2464 */
32874aea 2465 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2466 seen_key_event = 1;
cabfd08c 2467 }
2468
c9def1b8 2469 /* Make sure we're not pasting */
32874aea 2470 if (key_down)
2471 term_nopaste();
67c339f7 2472
32874aea 2473 if (compose_state > 1 && left_alt)
2474 compose_state = 0;
67c339f7 2475
c9def1b8 2476 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2477 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2478 && cfg.funky_type != 2)
2479 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2480 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2481 int nParam = 0;
32874aea 2482 switch (wParam) {
2483 case VK_INSERT:
2484 nParam = VK_NUMPAD0;
2485 break;
2486 case VK_END:
2487 nParam = VK_NUMPAD1;
2488 break;
2489 case VK_DOWN:
2490 nParam = VK_NUMPAD2;
2491 break;
2492 case VK_NEXT:
2493 nParam = VK_NUMPAD3;
2494 break;
2495 case VK_LEFT:
2496 nParam = VK_NUMPAD4;
2497 break;
2498 case VK_CLEAR:
2499 nParam = VK_NUMPAD5;
2500 break;
2501 case VK_RIGHT:
2502 nParam = VK_NUMPAD6;
2503 break;
2504 case VK_HOME:
2505 nParam = VK_NUMPAD7;
2506 break;
2507 case VK_UP:
2508 nParam = VK_NUMPAD8;
2509 break;
2510 case VK_PRIOR:
2511 nParam = VK_NUMPAD9;
2512 break;
2513 case VK_DELETE:
2514 nParam = VK_DECIMAL;
2515 break;
c9def1b8 2516 }
32874aea 2517 if (nParam) {
2518 if (keystate[VK_NUMLOCK] & 1)
2519 shift_state |= 1;
c9def1b8 2520 wParam = nParam;
2521 }
25d39ef6 2522 }
2523 }
2524
c9def1b8 2525 /* If a key is pressed and AltGr is not active */
32874aea 2526 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2527 /* Okay, prepare for most alts then ... */
2528 if (left_alt)
2529 *p++ = '\033';
374330e2 2530
c9def1b8 2531 /* Lets see if it's a pattern we know all about ... */
2532 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2533 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2534 return 0;
c9def1b8 2535 }
2536 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2537 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2538 return 0;
2539 }
2540 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 2541 term_do_paste();
32874aea 2542 return 0;
2543 }
c9def1b8 2544 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 2545 return -1;
c9def1b8 2546 }
2547 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 2548 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2549 return -1;
c9def1b8 2550 }
ec55b220 2551 /* Control-Numlock for app-keypad mode switch */
2552 if (wParam == VK_PAUSE && shift_state == 2) {
2553 app_keypad_keys ^= 1;
2554 return 0;
2555 }
374330e2 2556
c9def1b8 2557 /* Nethack keypad */
2558 if (cfg.nethack_keypad && !left_alt) {
32874aea 2559 switch (wParam) {
2560 case VK_NUMPAD1:
2561 *p++ = shift_state ? 'B' : 'b';
2562 return p - output;
2563 case VK_NUMPAD2:
2564 *p++ = shift_state ? 'J' : 'j';
2565 return p - output;
2566 case VK_NUMPAD3:
2567 *p++ = shift_state ? 'N' : 'n';
2568 return p - output;
2569 case VK_NUMPAD4:
2570 *p++ = shift_state ? 'H' : 'h';
2571 return p - output;
2572 case VK_NUMPAD5:
2573 *p++ = shift_state ? '.' : '.';
2574 return p - output;
2575 case VK_NUMPAD6:
2576 *p++ = shift_state ? 'L' : 'l';
2577 return p - output;
2578 case VK_NUMPAD7:
2579 *p++ = shift_state ? 'Y' : 'y';
2580 return p - output;
2581 case VK_NUMPAD8:
2582 *p++ = shift_state ? 'K' : 'k';
2583 return p - output;
2584 case VK_NUMPAD9:
2585 *p++ = shift_state ? 'U' : 'u';
2586 return p - output;
2587 }
c9def1b8 2588 }
2589
2590 /* Application Keypad */
2591 if (!left_alt) {
32874aea 2592 int xkey = 0;
2593
2594 if (cfg.funky_type == 3 ||
2595 (cfg.funky_type <= 1 &&
2596 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2597 case VK_EXECUTE:
2598 xkey = 'P';
2599 break;
2600 case VK_DIVIDE:
2601 xkey = 'Q';
2602 break;
2603 case VK_MULTIPLY:
2604 xkey = 'R';
2605 break;
2606 case VK_SUBTRACT:
2607 xkey = 'S';
2608 break;
2609 }
2610 if (app_keypad_keys && !cfg.no_applic_k)
2611 switch (wParam) {
2612 case VK_NUMPAD0:
2613 xkey = 'p';
2614 break;
2615 case VK_NUMPAD1:
2616 xkey = 'q';
2617 break;
2618 case VK_NUMPAD2:
2619 xkey = 'r';
2620 break;
2621 case VK_NUMPAD3:
2622 xkey = 's';
2623 break;
2624 case VK_NUMPAD4:
2625 xkey = 't';
2626 break;
2627 case VK_NUMPAD5:
2628 xkey = 'u';
2629 break;
2630 case VK_NUMPAD6:
2631 xkey = 'v';
2632 break;
2633 case VK_NUMPAD7:
2634 xkey = 'w';
2635 break;
2636 case VK_NUMPAD8:
2637 xkey = 'x';
2638 break;
2639 case VK_NUMPAD9:
2640 xkey = 'y';
2641 break;
2642
2643 case VK_DECIMAL:
2644 xkey = 'n';
2645 break;
2646 case VK_ADD:
2647 if (cfg.funky_type == 2) {
2648 if (shift_state)
2649 xkey = 'l';
2650 else
2651 xkey = 'k';
2652 } else if (shift_state)
2653 xkey = 'm';
c9def1b8 2654 else
32874aea 2655 xkey = 'l';
2656 break;
2657
2658 case VK_DIVIDE:
2659 if (cfg.funky_type == 2)
2660 xkey = 'o';
2661 break;
2662 case VK_MULTIPLY:
2663 if (cfg.funky_type == 2)
2664 xkey = 'j';
2665 break;
2666 case VK_SUBTRACT:
2667 if (cfg.funky_type == 2)
2668 xkey = 'm';
2669 break;
2670
2671 case VK_RETURN:
2672 if (HIWORD(lParam) & KF_EXTENDED)
2673 xkey = 'M';
2674 break;
c9def1b8 2675 }
32874aea 2676 if (xkey) {
2677 if (vt52_mode) {
2678 if (xkey >= 'P' && xkey <= 'S')
2679 p += sprintf((char *) p, "\x1B%c", xkey);
2680 else
2681 p += sprintf((char *) p, "\x1B?%c", xkey);
2682 } else
2683 p += sprintf((char *) p, "\x1BO%c", xkey);
2684 return p - output;
c9def1b8 2685 }
2686 }
2687
32874aea 2688 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 2689 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 2690 *p++ = 0;
2691 return -2;
c9def1b8 2692 }
32874aea 2693 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2694 *p++ = 0x1B;
2695 *p++ = '[';
2696 *p++ = 'Z';
2697 return p - output;
c9def1b8 2698 }
32874aea 2699 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2700 *p++ = 0;
2701 return p - output;
c9def1b8 2702 }
32874aea 2703 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2704 *p++ = 160;
2705 return p - output;
c9def1b8 2706 }
32874aea 2707 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2708 *p++ = 3;
a5f3e637 2709 *p++ = 0;
2710 return -2;
c9def1b8 2711 }
32874aea 2712 if (wParam == VK_PAUSE) { /* Break/Pause */
2713 *p++ = 26;
2714 *p++ = 0;
2715 return -2;
95bbe1ae 2716 }
c9def1b8 2717 /* Control-2 to Control-8 are special */
32874aea 2718 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2719 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 2720 return p - output;
2721 }
2722 if (shift_state == 2 && wParam == 0xBD) {
2723 *p++ = 0x1F;
2724 return p - output;
2725 }
2726 if (shift_state == 2 && wParam == 0xDF) {
2727 *p++ = 0x1C;
2728 return p - output;
2729 }
2730 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 2731 *p++ = '\r';
2732 *p++ = '\n';
c9def1b8 2733 return p - output;
2734 }
374330e2 2735
c5e9c988 2736 /*
c9def1b8 2737 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2738 * for integer decimal nn.)
2739 *
2740 * We also deal with the weird ones here. Linux VCs replace F1
2741 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2742 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2743 * respectively.
c5e9c988 2744 */
c9def1b8 2745 code = 0;
2746 switch (wParam) {
32874aea 2747 case VK_F1:
2748 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2749 break;
2750 case VK_F2:
2751 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2752 break;
2753 case VK_F3:
2754 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2755 break;
2756 case VK_F4:
2757 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2758 break;
2759 case VK_F5:
2760 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2761 break;
2762 case VK_F6:
2763 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2764 break;
2765 case VK_F7:
2766 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2767 break;
2768 case VK_F8:
2769 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2770 break;
2771 case VK_F9:
2772 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2773 break;
2774 case VK_F10:
2775 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2776 break;
2777 case VK_F11:
2778 code = 23;
2779 break;
2780 case VK_F12:
2781 code = 24;
2782 break;
2783 case VK_F13:
2784 code = 25;
2785 break;
2786 case VK_F14:
2787 code = 26;
2788 break;
2789 case VK_F15:
2790 code = 28;
2791 break;
2792 case VK_F16:
2793 code = 29;
2794 break;
2795 case VK_F17:
2796 code = 31;
2797 break;
2798 case VK_F18:
2799 code = 32;
2800 break;
2801 case VK_F19:
2802 code = 33;
2803 break;
2804 case VK_F20:
2805 code = 34;
2806 break;
dfca2656 2807 }
2808 if ((shift_state&2) == 0) switch (wParam) {
32874aea 2809 case VK_HOME:
2810 code = 1;
2811 break;
2812 case VK_INSERT:
2813 code = 2;
2814 break;
2815 case VK_DELETE:
2816 code = 3;
2817 break;
2818 case VK_END:
2819 code = 4;
2820 break;
2821 case VK_PRIOR:
2822 code = 5;
2823 break;
2824 case VK_NEXT:
2825 code = 6;
2826 break;
374330e2 2827 }
ec55b220 2828 /* Reorder edit keys to physical order */
32874aea 2829 if (cfg.funky_type == 3 && code <= 6)
2830 code = "\0\2\1\4\5\3\6"[code];
ec55b220 2831
f37caa11 2832 if (vt52_mode && code > 0 && code <= 6) {
32874aea 2833 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 2834 return p - output;
2835 }
2836
9bc81a2c 2837 if (cfg.funky_type == 5 && /* SCO function keys */
2838 code >= 11 && code <= 34) {
e24b1972 2839 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2840 int index = 0;
2841 switch (wParam) {
2842 case VK_F1: index = 0; break;
2843 case VK_F2: index = 1; break;
2844 case VK_F3: index = 2; break;
2845 case VK_F4: index = 3; break;
2846 case VK_F5: index = 4; break;
2847 case VK_F6: index = 5; break;
2848 case VK_F7: index = 6; break;
2849 case VK_F8: index = 7; break;
2850 case VK_F9: index = 8; break;
2851 case VK_F10: index = 9; break;
2852 case VK_F11: index = 10; break;
2853 case VK_F12: index = 11; break;
2854 }
2855 if (keystate[VK_SHIFT] & 0x80) index += 12;
2856 if (keystate[VK_CONTROL] & 0x80) index += 24;
2857 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 2858 return p - output;
2859 }
9bc81a2c 2860 if (cfg.funky_type == 5 && /* SCO small keypad */
2861 code >= 1 && code <= 6) {
2862 char codes[] = "HL.FIG";
2863 if (code == 3) {
2864 *p++ = '\x7F';
2865 } else {
2866 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2867 }
2868 return p - output;
2869 }
f37caa11 2870 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2871 int offt = 0;
32874aea 2872 if (code > 15)
2873 offt++;
2874 if (code > 21)
2875 offt++;
f37caa11 2876 if (vt52_mode)
32874aea 2877 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 2878 else
32874aea 2879 p +=
2880 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 2881 return p - output;
2882 }
c9def1b8 2883 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 2884 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 2885 return p - output;
2886 }
2887 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 2888 if (vt52_mode)
32874aea 2889 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 2890 else
32874aea 2891 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 2892 return p - output;
2893 }
2894 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 2895 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 2896 return p - output;
2897 }
2898 if (code) {
32874aea 2899 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 2900 return p - output;
374330e2 2901 }
45dabbc5 2902
c9def1b8 2903 /*
2904 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2905 * some reason seems to send VK_CLEAR to Windows...).
2906 */
2907 {
2908 char xkey = 0;
2909 switch (wParam) {
32874aea 2910 case VK_UP:
2911 xkey = 'A';
2912 break;
2913 case VK_DOWN:
2914 xkey = 'B';
2915 break;
2916 case VK_RIGHT:
2917 xkey = 'C';
2918 break;
2919 case VK_LEFT:
2920 xkey = 'D';
2921 break;
2922 case VK_CLEAR:
2923 xkey = 'G';
2924 break;
c9def1b8 2925 }
32874aea 2926 if (xkey) {
c9def1b8 2927 if (vt52_mode)
32874aea 2928 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 2929 else {
2930 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2931 /* VT100 & VT102 manuals both state the app cursor keys
2932 * only work if the app keypad is on.
2933 */
2934 if (!app_keypad_keys)
2935 app_flg = 0;
2936 /* Useful mapping of Ctrl-arrows */
2937 if (shift_state == 2)
2938 app_flg = !app_flg;
2939
2940 if (app_flg)
2941 p += sprintf((char *) p, "\x1BO%c", xkey);
2942 else
2943 p += sprintf((char *) p, "\x1B[%c", xkey);
2944 }
c9def1b8 2945 return p - output;
2946 }
2947 }
0c50ef57 2948
2949 /*
2950 * Finally, deal with Return ourselves. (Win95 seems to
2951 * foul it up when Alt is pressed, for some reason.)
2952 */
32874aea 2953 if (wParam == VK_RETURN) { /* Return */
0c50ef57 2954 *p++ = 0x0D;
a5f3e637 2955 *p++ = 0;
2956 return -2;
0c50ef57 2957 }
4eeb7d09 2958
2959 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2960 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2961 else
2962 alt_sum = 0;
67c339f7 2963 }
374330e2 2964
c9def1b8 2965 /* Okay we've done everything interesting; let windows deal with
2966 * the boring stuff */
2967 {
00e3ba0f 2968 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 2969#ifdef SHOW_TOASCII_RESULT
2970 if (r == 1 && !key_down) {
2971 if (alt_sum) {
8f22582c 2972 if (in_utf || dbcs_screenfont)
4eeb7d09 2973 debug((", (U+%04x)", alt_sum));
2974 else
2975 debug((", LCH(%d)", alt_sum));
2976 } else {
2977 debug((", ACH(%d)", keys[0]));
2978 }
2979 } else if (r > 0) {
2980 int r1;
2981 debug((", ASC("));
2982 for (r1 = 0; r1 < r; r1++) {
2983 debug(("%s%d", r1 ? "," : "", keys[r1]));
2984 }
2985 debug((")"));
2986 }
2987#endif
32874aea 2988 if (r > 0) {
4eeb7d09 2989 WCHAR keybuf;
c9def1b8 2990 p = output;
32874aea 2991 for (i = 0; i < r; i++) {
2992 unsigned char ch = (unsigned char) keys[i];
14963b8f 2993
32874aea 2994 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 2995 compose_char = ch;
32874aea 2996 compose_state++;
c9def1b8 2997 continue;
2998 }
32874aea 2999 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3000 int nc;
3001 compose_state = 0;
3002
32874aea 3003 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3004 MessageBeep(MB_ICONHAND);
c9def1b8 3005 return 0;
3006 }
4eeb7d09 3007 keybuf = nc;
3008 luni_send(&keybuf, 1);
3009 continue;
c9def1b8 3010 }
374330e2 3011
c9def1b8 3012 compose_state = 0;
374330e2 3013
4eeb7d09 3014 if (!key_down) {
3015 if (alt_sum) {
8f22582c 3016 if (in_utf || dbcs_screenfont) {
4eeb7d09 3017 keybuf = alt_sum;
3018 luni_send(&keybuf, 1);
3019 } else {
3020 ch = (char) alt_sum;
5471d09a 3021 /*
3022 * We need not bother about stdin
3023 * backlogs here, because in GUI PuTTY
3024 * we can't do anything about it
3025 * anyway; there's no means of asking
3026 * Windows to hold off on KEYDOWN
3027 * messages. We _have_ to buffer
3028 * everything we're sent.
3029 */
4eeb7d09 3030 ldisc_send(&ch, 1);
3031 }
3032 alt_sum = 0;
3033 } else
3034 lpage_send(kbd_codepage, &ch, 1);
3035 } else {
3036 static char cbuf[] = "\033 ";
3037 cbuf[1] = ch;
3038 lpage_send(kbd_codepage, cbuf + !left_alt,
3039 1 + !!left_alt);
c9def1b8 3040 }
bca9517a 3041 show_mouseptr(0);
c9def1b8 3042 }
374330e2 3043
c9def1b8 3044 /* This is so the ALT-Numpad and dead keys work correctly. */
3045 keys[0] = 0;
3046
32874aea 3047 return p - output;
c9def1b8 3048 }
159eba53 3049 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3050 if (!left_alt)
3051 keys[0] = 0;
4eeb7d09 3052 /* If we will be using alt_sum fix the 256s */
8f22582c 3053 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3054 keys[0] = 10;
374330e2 3055 }
3056
dfca2656 3057 /*
3058 * ALT alone may or may not want to bring up the System menu.
3059 * If it's not meant to, we return 0 on presses or releases of
3060 * ALT, to show that we've swallowed the keystroke. Otherwise
3061 * we return -1, which means Windows will give the keystroke
3062 * its default handling (i.e. bring up the System menu).
3063 */
3064 if (wParam == VK_MENU && !cfg.alt_only)
3065 return 0;
374330e2 3066
c9def1b8 3067 return -1;
374330e2 3068}
3069
32874aea 3070void set_title(char *title)
3071{
3072 sfree(window_name);
3073 window_name = smalloc(1 + strlen(title));
3074 strcpy(window_name, title);
37508af4 3075 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3076 SetWindowText(hwnd, title);
374330e2 3077}
3078
32874aea 3079void set_icon(char *title)
3080{
3081 sfree(icon_name);
3082 icon_name = smalloc(1 + strlen(title));
3083 strcpy(icon_name, title);
37508af4 3084 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3085 SetWindowText(hwnd, title);
374330e2 3086}
3087
32874aea 3088void set_sbar(int total, int start, int page)
3089{
374330e2 3090 SCROLLINFO si;
c9def1b8 3091
32874aea 3092 if (!cfg.scrollbar)
3093 return;
c9def1b8 3094
374330e2 3095 si.cbSize = sizeof(si);
c9def1b8 3096 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3097 si.nMin = 0;
3098 si.nMax = total - 1;
3099 si.nPage = page;
3100 si.nPos = start;
c1f5f956 3101 if (hwnd)
32874aea 3102 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3103}
3104
32874aea 3105Context get_ctx(void)
3106{
374330e2 3107 HDC hdc;
3108 if (hwnd) {
32874aea 3109 hdc = GetDC(hwnd);
374330e2 3110 if (hdc && pal)
32874aea 3111 SelectPalette(hdc, pal, FALSE);
374330e2 3112 return hdc;
3113 } else
3114 return NULL;
3115}
3116
32874aea 3117void free_ctx(Context ctx)
3118{
3119 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3120 ReleaseDC(hwnd, ctx);
374330e2 3121}
3122
32874aea 3123static void real_palette_set(int n, int r, int g, int b)
3124{
374330e2 3125 if (pal) {
3126 logpal->palPalEntry[n].peRed = r;
3127 logpal->palPalEntry[n].peGreen = g;
3128 logpal->palPalEntry[n].peBlue = b;
3129 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3130 colours[n] = PALETTERGB(r, g, b);
32874aea 3131 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3132 } else
3133 colours[n] = RGB(r, g, b);
3134}
3135
32874aea 3136void palette_set(int n, int r, int g, int b)
3137{
374330e2 3138 static const int first[21] = {
3139 0, 2, 4, 6, 8, 10, 12, 14,
3140 1, 3, 5, 7, 9, 11, 13, 15,
3141 16, 17, 18, 20, 22
3142 };
32874aea 3143 real_palette_set(first[n], r, g, b);
374330e2 3144 if (first[n] >= 18)
32874aea 3145 real_palette_set(first[n] + 1, r, g, b);
374330e2 3146 if (pal) {
3147 HDC hdc = get_ctx();
32874aea 3148 UnrealizeObject(pal);
3149 RealizePalette(hdc);
3150 free_ctx(hdc);
374330e2 3151 }
3152}
3153
32874aea 3154void palette_reset(void)
3155{
374330e2 3156 int i;
3157
3158 for (i = 0; i < NCOLOURS; i++) {
3159 if (pal) {
3160 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3161 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3162 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3163 logpal->palPalEntry[i].peFlags = 0;
3164 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3165 defpal[i].rgbtGreen,
3166 defpal[i].rgbtBlue);
3167 } else
3168 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3169 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3170 }
3171
3172 if (pal) {
3173 HDC hdc;
32874aea 3174 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3175 hdc = get_ctx();
32874aea 3176 RealizePalette(hdc);
3177 free_ctx(hdc);
374330e2 3178 }
3179}
3180
4eeb7d09 3181void write_aclip(char *data, int len, int must_deselect)
32874aea 3182{
374330e2 3183 HGLOBAL clipdata;
3184 void *lock;
3185
32874aea 3186 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3187 if (!clipdata)
3188 return;
32874aea 3189 lock = GlobalLock(clipdata);
374330e2 3190 if (!lock)
3191 return;
32874aea 3192 memcpy(lock, data, len);
3193 ((unsigned char *) lock)[len] = 0;
3194 GlobalUnlock(clipdata);
374330e2 3195
f0df44da 3196 if (!must_deselect)
32874aea 3197 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3198
32874aea 3199 if (OpenClipboard(hwnd)) {
374330e2 3200 EmptyClipboard();
32874aea 3201 SetClipboardData(CF_TEXT, clipdata);
374330e2 3202 CloseClipboard();
3203 } else
32874aea 3204 GlobalFree(clipdata);
f0df44da 3205
3206 if (!must_deselect)
32874aea 3207 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3208}
3209
4eeb7d09 3210/*
3211 * Note: unlike write_aclip() this will not append a nul.
3212 */
3213void write_clip(wchar_t * data, int len, int must_deselect)
3214{
3215 HGLOBAL clipdata;
3216 HGLOBAL clipdata2;
3217 int len2;
3218 void *lock, *lock2;
3219
3220 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3221
3222 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3223 len * sizeof(wchar_t));
3224 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3225
3226 if (!clipdata || !clipdata2) {
3227 if (clipdata)
3228 GlobalFree(clipdata);
3229 if (clipdata2)
3230 GlobalFree(clipdata2);
3231 return;
3232 }
3233 if (!(lock = GlobalLock(clipdata)))
3234 return;
3235 if (!(lock2 = GlobalLock(clipdata2)))
3236 return;
3237
3238 memcpy(lock, data, len * sizeof(wchar_t));
3239 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3240
3241 GlobalUnlock(clipdata);
3242 GlobalUnlock(clipdata2);
3243
3244 if (!must_deselect)
3245 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3246
3247 if (OpenClipboard(hwnd)) {
3248 EmptyClipboard();
3249 SetClipboardData(CF_UNICODETEXT, clipdata);
3250 SetClipboardData(CF_TEXT, clipdata2);
3251 CloseClipboard();
3252 } else {
3253 GlobalFree(clipdata);
3254 GlobalFree(clipdata2);
3255 }
3256
3257 if (!must_deselect)
3258 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3259}
3260
3261void get_clip(wchar_t ** p, int *len)
32874aea 3262{
374330e2 3263 static HGLOBAL clipdata = NULL;
4eeb7d09 3264 static wchar_t *converted = 0;
3265 wchar_t *p2;
374330e2 3266
4eeb7d09 3267 if (converted) {
3268 sfree(converted);
3269 converted = 0;
3270 }
374330e2 3271 if (!p) {
3272 if (clipdata)
32874aea 3273 GlobalUnlock(clipdata);
374330e2 3274 clipdata = NULL;
3275 return;
4eeb7d09 3276 } else if (OpenClipboard(NULL)) {
2d466ffd 3277 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3278 CloseClipboard();
4eeb7d09 3279 *p = GlobalLock(clipdata);
3280 if (*p) {
3281 for (p2 = *p; *p2; p2++);
3282 *len = p2 - *p;
3283 return;
374330e2 3284 }
2d466ffd 3285 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3286 char *s;
3287 int i;
3288 CloseClipboard();
3289 s = GlobalLock(clipdata);
3290 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3291 *p = converted = smalloc(i * sizeof(wchar_t));
3292 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3293 *len = i - 1;
3294 return;
3295 } else
3296 CloseClipboard();
374330e2 3297 }
3298
3299 *p = NULL;
3300 *len = 0;
3301}
3302
4eeb7d09 3303#if 0
374330e2 3304/*
3305 * Move `lines' lines from position `from' to position `to' in the
3306 * window.
3307 */
32874aea 3308void optimised_move(int to, int from, int lines)
3309{
374330e2 3310 RECT r;
f67b4e85 3311 int min, max;
374330e2 3312
3313 min = (to < from ? to : from);
3314 max = to + from - min;
374330e2 3315
32874aea 3316 r.left = 0;
3317 r.right = cols * font_width;
3318 r.top = min * font_height;
3319 r.bottom = (max + lines) * font_height;
3320 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3321}
4eeb7d09 3322#endif
374330e2 3323
3324/*
3325 * Print a message box and perform a fatal exit.
3326 */
32874aea 3327void fatalbox(char *fmt, ...)
3328{
374330e2 3329 va_list ap;
3330 char stuff[200];
3331
3332 va_start(ap, fmt);
3333 vsprintf(stuff, fmt, ap);
3334 va_end(ap);
3335 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3336 exit(1);
3337}
3338
3339/*
f8a28d1f 3340 * Manage window caption / taskbar flashing, if enabled.
3341 * 0 = stop, 1 = maintain, 2 = start
3342 */
3343static void flash_window(int mode)
3344{
3345 static long last_flash = 0;
3346 static int flashing = 0;
3347 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3348 /* stop */
3349 if (flashing) {
3350 FlashWindow(hwnd, FALSE);
3351 flashing = 0;
3352 }
3353
3354 } else if (mode == 2) {
3355 /* start */
3356 if (!flashing) {
3357 last_flash = GetTickCount();
3358 flashing = 1;
3359 FlashWindow(hwnd, TRUE);
3360 }
3361
3362 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3363 /* maintain */
3364 if (flashing) {
3365 long now = GetTickCount();
3366 long fdiff = now - last_flash;
3367 if (fdiff < 0 || fdiff > 450) {
3368 last_flash = now;
3369 FlashWindow(hwnd, TRUE); /* toggle */
3370 }
3371 }
3372 }
3373}
3374
3375/*
374330e2 3376 * Beep.
3377 */
32874aea 3378void beep(int mode)
3379{
03169ad0 3380 if (mode == BELL_DEFAULT) {
eb04402e 3381 /*
3382 * For MessageBeep style bells, we want to be careful of
3383 * timing, because they don't have the nice property of
3384 * PlaySound bells that each one cancels the previous
3385 * active one. So we limit the rate to one per 50ms or so.
3386 */
3387 static long lastbeep = 0;
d51cdf1e 3388 long beepdiff;
eb04402e 3389
d51cdf1e 3390 beepdiff = GetTickCount() - lastbeep;
eb04402e 3391 if (beepdiff >= 0 && beepdiff < 50)
3392 return;
156686ef 3393 MessageBeep(MB_OK);
d51cdf1e 3394 /*
3395 * The above MessageBeep call takes time, so we record the
3396 * time _after_ it finishes rather than before it starts.
3397 */
3398 lastbeep = GetTickCount();
03169ad0 3399 } else if (mode == BELL_WAVEFILE) {
3400 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 3401 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 3402 sprintf(buf, "Unable to play sound file\n%s\n"
3403 "Using default sound instead", cfg.bell_wavefile);
32874aea 3404 MessageBox(hwnd, buf, "PuTTY Sound Error",
3405 MB_OK | MB_ICONEXCLAMATION);
03169ad0 3406 cfg.beep = BELL_DEFAULT;
3407 }
3408 }
f8a28d1f 3409 /* Otherwise, either visual bell or disabled; do nothing here */
3410 if (!has_focus) {
3411 flash_window(2); /* start */
3412 }
374330e2 3413}