Robert de Bath's asynchronous-connect patch. Helps a lot in port
[u/mdw/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;
3ad9d396 709 events = (FD_CONNECT | FD_READ | FD_WRITE |
710 FD_OOB | FD_CLOSE | FD_ACCEPT);
8df7a775 711 } else {
712 msg = events = 0;
713 }
714 if (!hwnd)
715 return "do_select(): internal error (hwnd==NULL)";
32874aea 716 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
717 switch (WSAGetLastError()) {
718 case WSAENETDOWN:
719 return "Network is down";
720 default:
721 return "WSAAsyncSelect(): unknown error";
722 }
8df7a775 723 }
724 return NULL;
725}
726
727/*
01c034ad 728 * set or clear the "raw mouse message" mode
729 */
730void set_raw_mouse_mode(int activate)
731{
732 send_raw_mouse = activate;
733 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
734}
735
736/*
8d5de777 737 * Print a message box and close the connection.
738 */
32874aea 739void connection_fatal(char *fmt, ...)
740{
8d5de777 741 va_list ap;
742 char stuff[200];
743
744 va_start(ap, fmt);
745 vsprintf(stuff, fmt, ap);
746 va_end(ap);
747 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
b41069ff 748 if (cfg.close_on_exit == COE_ALWAYS)
32874aea 749 PostQuitMessage(1);
8d5de777 750 else {
32874aea 751 session_closed = TRUE;
752 SetWindowText(hwnd, "PuTTY (inactive)");
8d5de777 753 }
754}
755
756/*
59ad2c03 757 * Actually do the job requested by a WM_NETEVENT
758 */
32874aea 759static void enact_pending_netevent(void)
760{
9dde0b46 761 static int reentering = 0;
8df7a775 762 extern int select_result(WPARAM, LPARAM);
763 int ret;
9dde0b46 764
765 if (reentering)
32874aea 766 return; /* don't unpend the pending */
9dde0b46 767
59ad2c03 768 pending_netevent = FALSE;
9dde0b46 769
770 reentering = 1;
32874aea 771 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
9dde0b46 772 reentering = 0;
59ad2c03 773
b41069ff 774 if (ret == 0 && !session_closed) {
32874aea 775 /* Abnormal exits will already have set session_closed and taken
776 * appropriate action. */
b41069ff 777 if (cfg.close_on_exit == COE_ALWAYS ||
32874aea 778 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
59ad2c03 779 else {
32874aea 780 session_closed = TRUE;
781 SetWindowText(hwnd, "PuTTY (inactive)");
782 MessageBox(hwnd, "Connection closed by remote host",
783 "PuTTY", MB_OK | MB_ICONINFORMATION);
59ad2c03 784 }
785 }
786}
787
788/*
374330e2 789 * Copy the colour palette from the configuration data into defpal.
790 * This is non-trivial because the colour indices are different.
791 */
32874aea 792static void cfgtopalette(void)
793{
374330e2 794 int i;
795 static const int ww[] = {
796 6, 7, 8, 9, 10, 11, 12, 13,
797 14, 15, 16, 17, 18, 19, 20, 21,
798 0, 1, 2, 3, 4, 4, 5, 5
799 };
800
32874aea 801 for (i = 0; i < 24; i++) {
374330e2 802 int w = ww[i];
803 defpal[i].rgbtRed = cfg.colours[w][0];
804 defpal[i].rgbtGreen = cfg.colours[w][1];
805 defpal[i].rgbtBlue = cfg.colours[w][2];
806 }
807}
808
809/*
810 * Set up the colour palette.
811 */
32874aea 812static void init_palette(void)
813{
374330e2 814 int i;
32874aea 815 HDC hdc = GetDC(hwnd);
374330e2 816 if (hdc) {
32874aea 817 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
374330e2 818 logpal = smalloc(sizeof(*logpal)
819 - sizeof(logpal->palPalEntry)
820 + NCOLOURS * sizeof(PALETTEENTRY));
821 logpal->palVersion = 0x300;
822 logpal->palNumEntries = NCOLOURS;
823 for (i = 0; i < NCOLOURS; i++) {
824 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
825 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
826 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
827 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
828 }
32874aea 829 pal = CreatePalette(logpal);
374330e2 830 if (pal) {
32874aea 831 SelectPalette(hdc, pal, FALSE);
832 RealizePalette(hdc);
833 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
374330e2 834 }
835 }
32874aea 836 ReleaseDC(hwnd, hdc);
374330e2 837 }
838 if (pal)
32874aea 839 for (i = 0; i < NCOLOURS; i++)
374330e2 840 colours[i] = PALETTERGB(defpal[i].rgbtRed,
841 defpal[i].rgbtGreen,
842 defpal[i].rgbtBlue);
843 else
32874aea 844 for (i = 0; i < NCOLOURS; i++)
374330e2 845 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 846 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 847}
848
849/*
4eeb7d09 850 * Initialise all the fonts we will need initially. There may be as many as
851 * three or as few as one. The other (poentially) twentyone fonts are done
852 * if/when they are needed.
853 *
854 * We also:
374330e2 855 *
856 * - check the font width and height, correcting our guesses if
857 * necessary.
858 *
859 * - verify that the bold font is the same width as the ordinary
860 * one, and engage shadow bolding if not.
861 *
862 * - verify that the underlined font is the same width as the
863 * ordinary one (manual underlining by means of line drawing can
864 * be done in a pinch).
374330e2 865 */
32874aea 866static void init_fonts(int pick_width)
867{
374330e2 868 TEXTMETRIC tm;
4eeb7d09 869 CPINFO cpinfo;
870 int fontsize[3];
97fc891e 871 int i;
374330e2 872 HDC hdc;
873 int fw_dontcare, fw_bold;
874
4eeb7d09 875 for (i = 0; i < FONT_MAXNO; i++)
374330e2 876 fonts[i] = NULL;
877
878 if (cfg.fontisbold) {
879 fw_dontcare = FW_BOLD;
5b80d07f 880 fw_bold = FW_HEAVY;
32874aea 881 } else {
374330e2 882 fw_dontcare = FW_DONTCARE;
883 fw_bold = FW_BOLD;
884 }
885
97fc891e 886 hdc = GetDC(hwnd);
887
888 font_height = cfg.fontheight;
3b2c664e 889 if (font_height > 0) {
32874aea 890 font_height =
891 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
3b2c664e 892 }
59ad2c03 893 font_width = pick_width;
97fc891e 894
374330e2 895#define f(i,c,w,u) \
97fc891e 896 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
374330e2 897 c, OUT_DEFAULT_PRECIS, \
898 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
899 FIXED_PITCH | FF_DONTCARE, cfg.font)
97fc891e 900
4eeb7d09 901 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
97fc891e 902
4eeb7d09 903 SelectObject(hdc, fonts[FONT_NORMAL]);
904 GetTextMetrics(hdc, &tm);
905 font_height = tm.tmHeight;
906 font_width = tm.tmAveCharWidth;
97fc891e 907
4eeb7d09 908 {
909 CHARSETINFO info;
910 DWORD cset = tm.tmCharSet;
911 memset(&info, 0xFF, sizeof(info));
97fc891e 912
4eeb7d09 913 /* !!! Yes the next line is right */
914 if (cset == OEM_CHARSET)
915 font_codepage = GetOEMCP();
916 else
917 if (TranslateCharsetInfo
918 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
919 info.ciACP;
920 else
921 font_codepage = -1;
32874aea 922
4eeb7d09 923 GetCPInfo(font_codepage, &cpinfo);
924 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
925 }
97fc891e 926
4eeb7d09 927 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
97fc891e 928
4eeb7d09 929 /*
930 * Some fonts, e.g. 9-pt Courier, draw their underlines
931 * outside their character cell. We successfully prevent
932 * screen corruption by clipping the text output, but then
933 * we lose the underline completely. Here we try to work
934 * out whether this is such a font, and if it is, we set a
935 * flag that causes underlines to be drawn by hand.
936 *
937 * Having tried other more sophisticated approaches (such
938 * as examining the TEXTMETRIC structure or requesting the
939 * height of a string), I think we'll do this the brute
940 * force way: we create a small bitmap, draw an underlined
941 * space on it, and test to see whether any pixels are
942 * foreground-coloured. (Since we expect the underline to
943 * go all the way across the character cell, we only search
944 * down a single column of the bitmap, half way across.)
945 */
946 {
947 HDC und_dc;
948 HBITMAP und_bm, und_oldbm;
949 int i, gotit;
950 COLORREF c;
951
952 und_dc = CreateCompatibleDC(hdc);
953 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
954 und_oldbm = SelectObject(und_dc, und_bm);
955 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
956 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
957 SetTextColor(und_dc, RGB(255, 255, 255));
958 SetBkColor(und_dc, RGB(0, 0, 0));
959 SetBkMode(und_dc, OPAQUE);
960 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
961 gotit = FALSE;
962 for (i = 0; i < font_height; i++) {
963 c = GetPixel(und_dc, font_width / 2, i);
964 if (c != RGB(0, 0, 0))
965 gotit = TRUE;
966 }
967 SelectObject(und_dc, und_oldbm);
968 DeleteObject(und_bm);
969 DeleteDC(und_dc);
970 if (!gotit) {
971 und_mode = UND_LINE;
972 DeleteObject(fonts[FONT_UNDERLINE]);
973 fonts[FONT_UNDERLINE] = 0;
32874aea 974 }
4eeb7d09 975 }
97fc891e 976
4eeb7d09 977 if (bold_mode == BOLD_FONT) {
978 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
374330e2 979 }
980#undef f
981
97fc891e 982 descent = tm.tmAscent + 1;
983 if (descent >= font_height)
984 descent = font_height - 1;
374330e2 985
4eeb7d09 986 for (i = 0; i < 3; i++) {
97fc891e 987 if (fonts[i]) {
32874aea 988 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
4eeb7d09 989 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
32874aea 990 else
4eeb7d09 991 fontsize[i] = -i;
32874aea 992 } else
4eeb7d09 993 fontsize[i] = -i;
374330e2 994 }
995
32874aea 996 ReleaseDC(hwnd, hdc);
374330e2 997
4eeb7d09 998 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
374330e2 999 und_mode = UND_LINE;
32874aea 1000 DeleteObject(fonts[FONT_UNDERLINE]);
4eeb7d09 1001 fonts[FONT_UNDERLINE] = 0;
374330e2 1002 }
1003
4eeb7d09 1004 if (bold_mode == BOLD_FONT &&
1005 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
374330e2 1006 bold_mode = BOLD_SHADOW;
32874aea 1007 DeleteObject(fonts[FONT_BOLD]);
4eeb7d09 1008 fonts[FONT_BOLD] = 0;
374330e2 1009 }
4eeb7d09 1010 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1011
1012 init_ucs_tables();
1013}
1014
1015static void another_font(int fontno)
1016{
1017 int basefont;
1018 int fw_dontcare, fw_bold;
1019 int c, u, w, x;
1020 char *s;
1021
1022 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1023 return;
1024
1025 basefont = (fontno & ~(FONT_BOLDUND));
1026 if (basefont != fontno && !fontflag[basefont])
1027 another_font(basefont);
97fc891e 1028
4eeb7d09 1029 if (cfg.fontisbold) {
1030 fw_dontcare = FW_BOLD;
1031 fw_bold = FW_HEAVY;
1032 } else {
1033 fw_dontcare = FW_DONTCARE;
1034 fw_bold = FW_BOLD;
1035 }
1036
1037 c = cfg.fontcharset;
1038 w = fw_dontcare;
1039 u = FALSE;
1040 s = cfg.font;
1041 x = font_width;
1042
1043 if (fontno & FONT_WIDE)
1044 x *= 2;
1045 if (fontno & FONT_NARROW)
1046 x /= 2;
1047 if (fontno & FONT_OEM)
1048 c = OEM_CHARSET;
1049 if (fontno & FONT_BOLD)
1050 w = fw_bold;
1051 if (fontno & FONT_UNDERLINE)
1052 u = TRUE;
1053
1054 fonts[fontno] =
1055 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1056 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1057 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1058 FIXED_PITCH | FF_DONTCARE, s);
1059
1060 fontflag[fontno] = 1;
1061}
1062
1063static void deinit_fonts(void)
1064{
1065 int i;
1066 for (i = 0; i < FONT_MAXNO; i++) {
1067 if (fonts[i])
1068 DeleteObject(fonts[i]);
1069 fonts[i] = 0;
1070 fontflag[i] = 0;
374330e2 1071 }
1072}
1073
32874aea 1074void request_resize(int w, int h, int refont)
1075{
59ad2c03 1076 int width, height;
c9def1b8 1077
1078 /* If the window is maximized supress resizing attempts */
32874aea 1079 if (IsZoomed(hwnd))
1080 return;
1081
4eeb7d09 1082 if (refont && w != cols && (cols == 80 || cols == 132)) {
32874aea 1083 /* If font width too big for screen should we shrink the font more ? */
1084 if (w == 132)
1085 font_width = ((font_width * cols + w / 2) / w);
1086 else
59ad2c03 1087 font_width = 0;
4eeb7d09 1088 deinit_fonts();
32874aea 1089 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1090 und_mode = UND_FONT;
1091 init_fonts(font_width);
1092 } else {
1093 static int first_time = 1;
1094 static RECT ss;
1095
1096 switch (first_time) {
1097 case 1:
1098 /* Get the size of the screen */
1099 if (GetClientRect(GetDesktopWindow(), &ss))
1100 /* first_time = 0 */ ;
1101 else {
1102 first_time = 2;
1103 break;
1104 }
1105 case 0:
1106 /* Make sure the values are sane */
1107 width = (ss.right - ss.left - extra_width) / font_width;
1108 height = (ss.bottom - ss.top - extra_height) / font_height;
1109
1110 if (w > width)
1111 w = width;
1112 if (h > height)
1113 h = height;
1114 if (w < 15)
1115 w = 15;
1116 if (h < 1)
1117 w = 1;
1118 }
c9def1b8 1119 }
59ad2c03 1120
1121 width = extra_width + font_width * w;
1122 height = extra_height + font_height * h;
374330e2 1123
32874aea 1124 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1125 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1126 SWP_NOMOVE | SWP_NOZORDER);
374330e2 1127}
1128
32874aea 1129static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1130{
fdedf2c8 1131 int thistime = GetMessageTime();
1132
b90840c3 1133 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1134 lastbtn = MBT_NOTHING;
01c034ad 1135 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
1136 return;
1137 }
1138
fdedf2c8 1139 if (lastbtn == b && thistime - lasttime < dbltime) {
374330e2 1140 lastact = (lastact == MA_CLICK ? MA_2CLK :
1141 lastact == MA_2CLK ? MA_3CLK :
1142 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1143 } else {
1144 lastbtn = b;
1145 lastact = MA_CLICK;
1146 }
1147 if (lastact != MA_NOTHING)
32874aea 1148 term_mouse(b, lastact, x, y, shift, ctrl);
fdedf2c8 1149 lasttime = thistime;
374330e2 1150}
1151
01c034ad 1152/*
1153 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1154 * into a cooked one (SELECT, EXTEND, PASTE).
1155 */
32874aea 1156Mouse_Button translate_button(Mouse_Button button)
1157{
01c034ad 1158 if (button == MBT_LEFT)
1159 return MBT_SELECT;
1160 if (button == MBT_MIDDLE)
1161 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1162 if (button == MBT_RIGHT)
1163 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
2d466ffd 1164 return 0; /* shouldn't happen */
01c034ad 1165}
1166
32874aea 1167static void show_mouseptr(int show)
1168{
554c540d 1169 static int cursor_visible = 1;
32874aea 1170 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1171 show = 1;
554c540d 1172 if (cursor_visible && !show)
32874aea 1173 ShowCursor(FALSE);
554c540d 1174 else if (!cursor_visible && show)
32874aea 1175 ShowCursor(TRUE);
554c540d 1176 cursor_visible = show;
1177}
1178
32874aea 1179static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1180 WPARAM wParam, LPARAM lParam)
1181{
374330e2 1182 HDC hdc;
1183 static int ignore_size = FALSE;
1184 static int ignore_clip = FALSE;
1185 static int just_reconfigged = FALSE;
ffc31afe 1186 static int resizing = FALSE;
3ad8c6db 1187 static int need_backend_resize = FALSE;
e44d78b6 1188 static int defered_resize = FALSE;
374330e2 1189
1190 switch (message) {
59ad2c03 1191 case WM_TIMER:
1192 if (pending_netevent)
1193 enact_pending_netevent();
c9def1b8 1194 if (inbuf_head)
59ad2c03 1195 term_out();
32874aea 1196 noise_regular();
1197 HideCaret(hwnd);
59ad2c03 1198 term_update();
32874aea 1199 ShowCaret(hwnd);
1200 if (cfg.ping_interval > 0) {
1201 time_t now;
1202 time(&now);
1203 if (now - last_movement > cfg.ping_interval) {
1204 back->special(TS_PING);
1205 last_movement = now;
1206 }
1207 }
59ad2c03 1208 return 0;
374330e2 1209 case WM_CREATE:
1210 break;
68130d34 1211 case WM_CLOSE:
32874aea 1212 show_mouseptr(1);
d85548fe 1213 if (!cfg.warn_on_close || session_closed ||
32874aea 1214 MessageBox(hwnd,
1215 "Are you sure you want to close this session?",
68130d34 1216 "PuTTY Exit Confirmation",
1217 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1218 DestroyWindow(hwnd);
1219 return 0;
374330e2 1220 case WM_DESTROY:
32874aea 1221 show_mouseptr(1);
1222 PostQuitMessage(0);
374330e2 1223 return 0;
6833a413 1224 case WM_SYSCOMMAND:
1225 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1226 case IDM_SHOWLOG:
c5e9c988 1227 showeventlog(hwnd);
374330e2 1228 break;
1229 case IDM_NEWSESS:
1230 case IDM_DUPSESS:
6833a413 1231 case IDM_SAVEDSESS:
374330e2 1232 {
1233 char b[2048];
1234 char c[30], *cl;
e4e4cc7e 1235 int freecl = FALSE;
374330e2 1236 STARTUPINFO si;
1237 PROCESS_INFORMATION pi;
1238 HANDLE filemap = NULL;
1239
1240 if (wParam == IDM_DUPSESS) {
1241 /*
1242 * Allocate a file-mapping memory chunk for the
1243 * config structure.
1244 */
1245 SECURITY_ATTRIBUTES sa;
1246 Config *p;
1247
1248 sa.nLength = sizeof(sa);
1249 sa.lpSecurityDescriptor = NULL;
1250 sa.bInheritHandle = TRUE;
32874aea 1251 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1252 &sa,
1253 PAGE_READWRITE,
32874aea 1254 0, sizeof(Config), NULL);
374330e2 1255 if (filemap) {
32874aea 1256 p = (Config *) MapViewOfFile(filemap,
1257 FILE_MAP_WRITE,
1258 0, 0, sizeof(Config));
374330e2 1259 if (p) {
1260 *p = cfg; /* structure copy */
1261 UnmapViewOfFile(p);
1262 }
1263 }
1d470ad2 1264 sprintf(c, "putty &%p", filemap);
374330e2 1265 cl = c;
0a4aa984 1266 } else if (wParam == IDM_SAVEDSESS) {
32874aea 1267 char *session =
1268 sessions[(lParam - IDM_SAVED_MIN) / 16];
1269 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
e4e4cc7e 1270 if (!cl)
1271 cl = NULL; /* not a very important failure mode */
94e6450e 1272 else {
1273 sprintf(cl, "putty @%s", session);
1274 freecl = TRUE;
1275 }
374330e2 1276 } else
6833a413 1277 cl = NULL;
374330e2 1278
32874aea 1279 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1280 si.cb = sizeof(si);
1281 si.lpReserved = NULL;
1282 si.lpDesktop = NULL;
1283 si.lpTitle = NULL;
1284 si.dwFlags = 0;
1285 si.cbReserved2 = 0;
1286 si.lpReserved2 = NULL;
32874aea 1287 CreateProcess(b, cl, NULL, NULL, TRUE,
1288 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1289
1290 if (filemap)
1291 CloseHandle(filemap);
e4e4cc7e 1292 if (freecl)
dcbde236 1293 sfree(cl);
374330e2 1294 }
1295 break;
32874aea 1296 case IDM_RECONF:
1297 {
1298 int prev_alwaysontop = cfg.alwaysontop;
1299 int prev_sunken_edge = cfg.sunken_edge;
e1c8e0ed 1300 char oldlogfile[FILENAME_MAX];
1301 int oldlogtype;
3da0b1d2 1302 int need_setwpos = FALSE;
cbc3272b 1303 int old_fwidth, old_fheight;
e1c8e0ed 1304
1305 strcpy(oldlogfile, cfg.logfilename);
1306 oldlogtype = cfg.logtype;
cbc3272b 1307 old_fwidth = font_width;
1308 old_fheight = font_height;
32874aea 1309 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
e1c8e0ed 1310
32874aea 1311 if (!do_reconfig(hwnd))
1312 break;
e1c8e0ed 1313
1314 if (strcmp(oldlogfile, cfg.logfilename) ||
1315 oldlogtype != cfg.logtype) {
1316 logfclose(); /* reset logging */
1317 logfopen();
1318 }
1319
32874aea 1320 just_reconfigged = TRUE;
4eeb7d09 1321 deinit_fonts();
32874aea 1322 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1323 und_mode = UND_FONT;
1324 init_fonts(0);
1325 sfree(logpal);
1326 /*
1327 * Flush the line discipline's edit buffer in the
1328 * case where local editing has just been disabled.
1329 */
1330 ldisc_send(NULL, 0);
1331 if (pal)
1332 DeleteObject(pal);
1333 logpal = NULL;
1334 pal = NULL;
1335 cfgtopalette();
1336 init_palette();
1337
1338 /* Enable or disable the scroll bar, etc */
1339 {
1340 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1341 LONG nexflag, exflag =
1342 GetWindowLong(hwnd, GWL_EXSTYLE);
1343
1344 nexflag = exflag;
1345 if (cfg.alwaysontop != prev_alwaysontop) {
1346 if (cfg.alwaysontop) {
1347 nexflag |= WS_EX_TOPMOST;
1348 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1349 SWP_NOMOVE | SWP_NOSIZE);
1350 } else {
1351 nexflag &= ~(WS_EX_TOPMOST);
1352 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1353 SWP_NOMOVE | SWP_NOSIZE);
1354 }
1355 }
1356 if (cfg.sunken_edge)
1357 nexflag |= WS_EX_CLIENTEDGE;
1358 else
1359 nexflag &= ~(WS_EX_CLIENTEDGE);
1360
1361 nflg = flag;
1362 if (cfg.scrollbar)
1363 nflg |= WS_VSCROLL;
1364 else
1365 nflg &= ~WS_VSCROLL;
1366 if (cfg.locksize)
1367 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1368 else
1369 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1370
1371 if (nflg != flag || nexflag != exflag) {
1372 RECT cr, wr;
1373
1374 if (nflg != flag)
1375 SetWindowLong(hwnd, GWL_STYLE, nflg);
1376 if (nexflag != exflag)
1377 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1378
1379 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1380
1381 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1382 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1383 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER
1384 | SWP_FRAMECHANGED);
1385
1386 GetWindowRect(hwnd, &wr);
1387 GetClientRect(hwnd, &cr);
1388 extra_width =
1389 wr.right - wr.left - cr.right + cr.left;
1390 extra_height =
1391 wr.bottom - wr.top - cr.bottom + cr.top;
e44d78b6 1392 need_setwpos = TRUE;
32874aea 1393 }
1394 }
c9def1b8 1395
3da0b1d2 1396 if (cfg.height != rows ||
1397 cfg.width != cols ||
cbc3272b 1398 old_fwidth != font_width ||
1399 old_fheight != font_height ||
57d08f2f 1400 cfg.savelines != savelines ||
32874aea 1401 cfg.sunken_edge != prev_sunken_edge)
1402 need_setwpos = TRUE;
e44d78b6 1403
1404 if (IsZoomed(hwnd)) {
1405 int w, h;
1406 RECT cr;
1407 if (need_setwpos)
1408 defered_resize = TRUE;
1409
1410 GetClientRect(hwnd, &cr);
1411 w = cr.right - cr.left;
1412 h = cr.bottom - cr.top;
1413 w = w / font_width;
1414 if (w < 1)
1415 w = 1;
1416 h = h / font_height;
1417 if (h < 1)
1418 h = 1;
1419
1420 term_size(h, w, cfg.savelines);
1421 InvalidateRect(hwnd, NULL, TRUE);
1422 back->size();
1423 } else {
1424 term_size(cfg.height, cfg.width, cfg.savelines);
1425 InvalidateRect(hwnd, NULL, TRUE);
1426 if (need_setwpos) {
1427 SetWindowPos(hwnd, NULL, 0, 0,
1428 extra_width + font_width * cfg.width,
1429 extra_height +
1430 font_height * cfg.height,
1431 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1432 SWP_NOMOVE | SWP_NOZORDER);
1433 }
32874aea 1434 }
e44d78b6 1435 /* Oops */
1436 if (cfg.locksize && IsZoomed(hwnd))
1437 force_normal(hwnd);
32874aea 1438 set_title(cfg.wintitle);
1439 if (IsIconic(hwnd)) {
1440 SetWindowText(hwnd,
1441 cfg.win_name_always ? window_name :
1442 icon_name);
3da0b1d2 1443 }
32874aea 1444 }
1445 break;
bc1235d4 1446 case IDM_COPYALL:
1447 term_copyall();
1448 break;
32874aea 1449 case IDM_CLRSB:
1450 term_clrsb();
1451 break;
1452 case IDM_RESET:
1453 term_pwron();
1454 break;
1455 case IDM_TEL_AYT:
1456 back->special(TS_AYT);
1457 break;
1458 case IDM_TEL_BRK:
1459 back->special(TS_BRK);
1460 break;
1461 case IDM_TEL_SYNCH:
1462 back->special(TS_SYNCH);
1463 break;
1464 case IDM_TEL_EC:
1465 back->special(TS_EC);
1466 break;
1467 case IDM_TEL_EL:
1468 back->special(TS_EL);
1469 break;
1470 case IDM_TEL_GA:
1471 back->special(TS_GA);
1472 break;
1473 case IDM_TEL_NOP:
1474 back->special(TS_NOP);
1475 break;
1476 case IDM_TEL_ABORT:
1477 back->special(TS_ABORT);
1478 break;
1479 case IDM_TEL_AO:
1480 back->special(TS_AO);
1481 break;
1482 case IDM_TEL_IP:
1483 back->special(TS_IP);
1484 break;
1485 case IDM_TEL_SUSP:
1486 back->special(TS_SUSP);
1487 break;
1488 case IDM_TEL_EOR:
1489 back->special(TS_EOR);
1490 break;
1491 case IDM_TEL_EOF:
1492 back->special(TS_EOF);
1493 break;
374330e2 1494 case IDM_ABOUT:
32874aea 1495 showabout(hwnd);
374330e2 1496 break;
dfca2656 1497 case SC_KEYMENU:
1498 /*
1499 * We get this if the System menu has been activated.
1500 * This might happen from within TranslateKey, in which
1501 * case it really wants to be followed by a `space'
1502 * character to actually _bring the menu up_ rather
1503 * than just sitting there in `ready to appear' state.
1504 */
1505 if( lParam == 0 )
1506 PostMessage(hwnd, WM_CHAR, ' ', 0);
1507 break;
32874aea 1508 default:
1509 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1510 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1511 }
374330e2 1512 }
1513 break;
37508af4 1514
1515#define X_POS(l) ((int)(short)LOWORD(l))
1516#define Y_POS(l) ((int)(short)HIWORD(l))
1517
fdedf2c8 1518#define TO_CHR_X(x) (((x)<0 ? (x)-font_width+1 : (x)) / font_width)
1519#define TO_CHR_Y(y) (((y)<0 ? (y)-font_height+1: (y)) / font_height)
01c034ad 1520#define WHEEL_DELTA 120
1521 case WM_MOUSEWHEEL:
1522 {
32874aea 1523 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1524 wParam = LOWORD(wParam);
1525
1526 /* process events when the threshold is reached */
1527 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1528 int b;
fdedf2c8 1529
01c034ad 1530 /* reduce amount for next time */
1531 if (wheel_accumulator > 0) {
1532 b = MBT_WHEEL_UP;
1533 wheel_accumulator -= WHEEL_DELTA;
32874aea 1534 } else if (wheel_accumulator < 0) {
01c034ad 1535 b = MBT_WHEEL_DOWN;
1536 wheel_accumulator += WHEEL_DELTA;
32874aea 1537 } else
01c034ad 1538 break;
1539
1540 if (send_raw_mouse) {
1541 /* send a mouse-down followed by a mouse up */
1542 term_mouse(b,
1543 MA_CLICK,
32874aea 1544 TO_CHR_X(X_POS(lParam)),
1545 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1546 wParam & MK_CONTROL);
1547 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1548 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1549 wParam & MK_CONTROL);
01c034ad 1550 } else {
1551 /* trigger a scroll */
32874aea 1552 term_scroll(0,
1553 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1554 }
1555 }
1556 return 0;
1557 }
374330e2 1558 case WM_LBUTTONDOWN:
374330e2 1559 case WM_MBUTTONDOWN:
374330e2 1560 case WM_RBUTTONDOWN:
01c034ad 1561 case WM_LBUTTONUP:
1562 case WM_MBUTTONUP:
374330e2 1563 case WM_RBUTTONUP:
01c034ad 1564 {
1565 int button, press;
1566 switch (message) {
32874aea 1567 case WM_LBUTTONDOWN:
1568 button = MBT_LEFT;
1569 press = 1;
1570 break;
1571 case WM_MBUTTONDOWN:
1572 button = MBT_MIDDLE;
1573 press = 1;
1574 break;
1575 case WM_RBUTTONDOWN:
1576 button = MBT_RIGHT;
1577 press = 1;
1578 break;
1579 case WM_LBUTTONUP:
1580 button = MBT_LEFT;
1581 press = 0;
1582 break;
1583 case WM_MBUTTONUP:
1584 button = MBT_MIDDLE;
1585 press = 0;
1586 break;
1587 case WM_RBUTTONUP:
1588 button = MBT_RIGHT;
1589 press = 0;
1590 break;
2d466ffd 1591 default:
1592 button = press = 0; /* shouldn't happen */
01c034ad 1593 }
1594 show_mouseptr(1);
1595 if (press) {
32874aea 1596 click(button,
1597 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1598 wParam & MK_SHIFT, wParam & MK_CONTROL);
01c034ad 1599 SetCapture(hwnd);
1600 } else {
32874aea 1601 term_mouse(button, MA_RELEASE,
1602 TO_CHR_X(X_POS(lParam)),
1603 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1604 wParam & MK_CONTROL);
01c034ad 1605 ReleaseCapture();
1606 }
1607 }
374330e2 1608 return 0;
1609 case WM_MOUSEMOVE:
32874aea 1610 show_mouseptr(1);
374330e2 1611 /*
1612 * Add the mouse position and message time to the random
7d6ee6ff 1613 * number noise.
374330e2 1614 */
32874aea 1615 noise_ultralight(lParam);
374330e2 1616
1617 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1618 Mouse_Button b;
1619 if (wParam & MK_LBUTTON)
6c6e7711 1620 b = MBT_LEFT;
374330e2 1621 else if (wParam & MK_MBUTTON)
6c6e7711 1622 b = MBT_MIDDLE;
374330e2 1623 else
6c6e7711 1624 b = MBT_RIGHT;
32874aea 1625 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1626 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1627 wParam & MK_CONTROL);
374330e2 1628 }
374330e2 1629 return 0;
d318ef8e 1630 case WM_NCMOUSEMOVE:
1631 show_mouseptr(1);
32874aea 1632 noise_ultralight(lParam);
d318ef8e 1633 return 0;
374330e2 1634 case WM_IGNORE_CLIP:
1635 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1636 break;
1637 case WM_DESTROYCLIPBOARD:
1638 if (!ignore_clip)
1639 term_deselect();
1640 ignore_clip = FALSE;
1641 return 0;
1642 case WM_PAINT:
1643 {
1644 PAINTSTRUCT p;
32874aea 1645 HideCaret(hwnd);
1646 hdc = BeginPaint(hwnd, &p);
374330e2 1647 if (pal) {
32874aea 1648 SelectPalette(hdc, pal, TRUE);
1649 RealizePalette(hdc);
374330e2 1650 }
32874aea 1651 term_paint(hdc, p.rcPaint.left, p.rcPaint.top,
1652 p.rcPaint.right, p.rcPaint.bottom);
1653 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1654 SelectObject(hdc, GetStockObject(WHITE_PEN));
1655 EndPaint(hwnd, &p);
1656 ShowCaret(hwnd);
374330e2 1657 }
1658 return 0;
1659 case WM_NETEVENT:
59ad2c03 1660 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1661 * but the only one that's likely to try to overload us is FD_READ.
1662 * This means buffering just one is fine.
1663 */
1664 if (pending_netevent)
1665 enact_pending_netevent();
1666
1667 pending_netevent = TRUE;
32874aea 1668 pend_netevent_wParam = wParam;
1669 pend_netevent_lParam = lParam;
3ad9d396 1670 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1671 enact_pending_netevent();
1672
ec55b220 1673 time(&last_movement);
374330e2 1674 return 0;
1675 case WM_SETFOCUS:
1676 has_focus = TRUE;
32874aea 1677 CreateCaret(hwnd, caretbm, font_width, font_height);
1678 ShowCaret(hwnd);
f8a28d1f 1679 flash_window(0); /* stop */
32874aea 1680 compose_state = 0;
374330e2 1681 term_out();
1682 term_update();
1683 break;
1684 case WM_KILLFOCUS:
32874aea 1685 show_mouseptr(1);
374330e2 1686 has_focus = FALSE;
32874aea 1687 DestroyCaret();
374330e2 1688 term_out();
1689 term_update();
1690 break;
1691 case WM_IGNORE_SIZE:
1692 ignore_size = TRUE; /* don't panic on next WM_SIZE msg */
1693 break;
73251d5d 1694 case WM_ENTERSIZEMOVE:
32874aea 1695 EnableSizeTip(1);
1696 resizing = TRUE;
3ad8c6db 1697 need_backend_resize = FALSE;
32874aea 1698 break;
73251d5d 1699 case WM_EXITSIZEMOVE:
32874aea 1700 EnableSizeTip(0);
1701 resizing = FALSE;
3ad8c6db 1702 if (need_backend_resize)
1703 back->size();
32874aea 1704 break;
374330e2 1705 case WM_SIZING:
1706 {
1707 int width, height, w, h, ew, eh;
32874aea 1708 LPRECT r = (LPRECT) lParam;
374330e2 1709
1710 width = r->right - r->left - extra_width;
1711 height = r->bottom - r->top - extra_height;
32874aea 1712 w = (width + font_width / 2) / font_width;
1713 if (w < 1)
1714 w = 1;
1715 h = (height + font_height / 2) / font_height;
1716 if (h < 1)
1717 h = 1;
1718 UpdateSizeTip(hwnd, w, h);
374330e2 1719 ew = width - w * font_width;
1720 eh = height - h * font_height;
1721 if (ew != 0) {
1722 if (wParam == WMSZ_LEFT ||
32874aea 1723 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 1724 r->left += ew;
1725 else
1726 r->right -= ew;
1727 }
1728 if (eh != 0) {
1729 if (wParam == WMSZ_TOP ||
32874aea 1730 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 1731 r->top += eh;
1732 else
1733 r->bottom -= eh;
1734 }
1735 if (ew || eh)
1736 return 1;
1737 else
1738 return 0;
1739 }
32874aea 1740 /* break; (never reached) */
374330e2 1741 case WM_SIZE:
1742 if (wParam == SIZE_MINIMIZED) {
32874aea 1743 SetWindowText(hwnd,
1744 cfg.win_name_always ? window_name : icon_name);
374330e2 1745 break;
1746 }
1747 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 1748 SetWindowText(hwnd, window_name);
374330e2 1749 if (!ignore_size) {
1a6f78fe 1750 int width, height, w, h;
32874aea 1751#if 0 /* we have fixed this using WM_SIZING now */
1752 int ew, eh;
1a6f78fe 1753#endif
374330e2 1754
1755 width = LOWORD(lParam);
1756 height = HIWORD(lParam);
32874aea 1757 w = width / font_width;
1758 if (w < 1)
1759 w = 1;
1760 h = height / font_height;
1761 if (h < 1)
1762 h = 1;
1763#if 0 /* we have fixed this using WM_SIZING now */
374330e2 1764 ew = width - w * font_width;
1765 eh = height - h * font_height;
1766 if (ew != 0 || eh != 0) {
1767 RECT r;
32874aea 1768 GetWindowRect(hwnd, &r);
1769 SendMessage(hwnd, WM_IGNORE_SIZE, 0, 0);
1770 SetWindowPos(hwnd, NULL, 0, 0,
1771 r.right - r.left - ew, r.bottom - r.top - eh,
1772 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER);
374330e2 1773 }
1774#endif
1775 if (w != cols || h != rows || just_reconfigged) {
1776 term_invalidate();
32874aea 1777 term_size(h, w, cfg.savelines);
1778 /*
1779 * Don't call back->size in mid-resize. (To prevent
1780 * massive numbers of resize events getting sent
1781 * down the connection during an NT opaque drag.)
1782 */
1783 if (!resizing)
1784 back->size();
e44d78b6 1785 else {
3ad8c6db 1786 need_backend_resize = TRUE;
e44d78b6 1787 cfg.height = h;
1788 cfg.width = w;
1789 }
374330e2 1790 just_reconfigged = FALSE;
1791 }
1792 }
e44d78b6 1793 if (wParam == SIZE_RESTORED && defered_resize) {
1794 defered_resize = FALSE;
1795 SetWindowPos(hwnd, NULL, 0, 0,
1796 extra_width + font_width * cfg.width,
1797 extra_height + font_height * cfg.height,
1798 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1799 SWP_NOMOVE | SWP_NOZORDER);
1800 }
374330e2 1801 ignore_size = FALSE;
1802 return 0;
1803 case WM_VSCROLL:
1804 switch (LOWORD(wParam)) {
32874aea 1805 case SB_BOTTOM:
1806 term_scroll(-1, 0);
1807 break;
1808 case SB_TOP:
1809 term_scroll(+1, 0);
1810 break;
1811 case SB_LINEDOWN:
1812 term_scroll(0, +1);
1813 break;
1814 case SB_LINEUP:
1815 term_scroll(0, -1);
1816 break;
1817 case SB_PAGEDOWN:
1818 term_scroll(0, +rows / 2);
1819 break;
1820 case SB_PAGEUP:
1821 term_scroll(0, -rows / 2);
1822 break;
1823 case SB_THUMBPOSITION:
1824 case SB_THUMBTRACK:
1825 term_scroll(1, HIWORD(wParam));
1826 break;
1827 }
1828 break;
1829 case WM_PALETTECHANGED:
374330e2 1830 if ((HWND) wParam != hwnd && pal != NULL) {
1831 HDC hdc = get_ctx();
1832 if (hdc) {
32874aea 1833 if (RealizePalette(hdc) > 0)
1834 UpdateColors(hdc);
1835 free_ctx(hdc);
374330e2 1836 }
1837 }
1838 break;
1839 case WM_QUERYNEWPALETTE:
1840 if (pal != NULL) {
1841 HDC hdc = get_ctx();
1842 if (hdc) {
32874aea 1843 if (RealizePalette(hdc) > 0)
1844 UpdateColors(hdc);
1845 free_ctx(hdc);
374330e2 1846 return TRUE;
1847 }
1848 }
1849 return FALSE;
1850 case WM_KEYDOWN:
1851 case WM_SYSKEYDOWN:
c9def1b8 1852 case WM_KEYUP:
1853 case WM_SYSKEYUP:
374330e2 1854 /*
1855 * Add the scan code and keypress timing to the random
7d6ee6ff 1856 * number noise.
374330e2 1857 */
32874aea 1858 noise_ultralight(lParam);
374330e2 1859
1860 /*
1861 * We don't do TranslateMessage since it disassociates the
1862 * resulting CHAR message from the KEYDOWN that sparked it,
1863 * which we occasionally don't want. Instead, we process
1864 * KEYDOWN, and call the Win32 translator functions so that
1865 * we get the translations under _our_ control.
1866 */
1867 {
1868 unsigned char buf[20];
1869 int len;
1870
32874aea 1871 if (wParam == VK_PROCESSKEY) {
3cf144db 1872 MSG m;
32874aea 1873 m.hwnd = hwnd;
1874 m.message = WM_KEYDOWN;
1875 m.wParam = wParam;
1876 m.lParam = lParam & 0xdfff;
1877 TranslateMessage(&m);
1878 } else {
1879 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 1880 if (len == -1)
32874aea 1881 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 1882
b2a1eade 1883 if (len != 0) {
bca9517a 1884 /*
1885 * We need not bother about stdin backlogs
1886 * here, because in GUI PuTTY we can't do
1887 * anything about it anyway; there's no means
1888 * of asking Windows to hold off on KEYDOWN
1889 * messages. We _have_ to buffer everything
1890 * we're sent.
1891 */
1892 ldisc_send(buf, len);
32874aea 1893 show_mouseptr(0);
bca9517a 1894 }
3cf144db 1895 }
374330e2 1896 }
1897 return 0;
4eeb7d09 1898 case WM_INPUTLANGCHANGE:
3cf144db 1899 {
4eeb7d09 1900 /* wParam == Font number */
1901 /* lParam == Locale */
1902 char lbuf[20];
1903 HKL NewInputLocale = (HKL) lParam;
1904
1905 // lParam == GetKeyboardLayout(0);
1906
1907 GetLocaleInfo(LOWORD(NewInputLocale),
1908 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
1909
1910 kbd_codepage = atoi(lbuf);
1911 }
1912 break;
88485e4d 1913 case WM_IME_COMPOSITION:
1914 {
1915 HIMC hIMC;
1916 int n;
1917 char *buff;
1918
1919 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
1920 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
1921
1922 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
1923 break; /* fall back to DefWindowProc */
1924
1925 hIMC = ImmGetContext(hwnd);
1926 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1927
1928 if (n > 0) {
1929 buff = (char*) smalloc(n);
1930 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1931 luni_send((unsigned short *)buff, n / 2);
1932 free(buff);
1933 }
1934 ImmReleaseContext(hwnd, hIMC);
1935 return 1;
1936 }
1937
4eeb7d09 1938 case WM_IME_CHAR:
1939 if (wParam & 0xFF00) {
3cf144db 1940 unsigned char buf[2];
1941
1942 buf[1] = wParam;
1943 buf[0] = wParam >> 8;
4eeb7d09 1944 lpage_send(kbd_codepage, buf, 2);
1945 } else {
1946 char c = (unsigned char) wParam;
1947 lpage_send(kbd_codepage, &c, 1);
3cf144db 1948 }
4eeb7d09 1949 return (0);
374330e2 1950 case WM_CHAR:
1951 case WM_SYSCHAR:
1952 /*
1953 * Nevertheless, we are prepared to deal with WM_CHAR
1954 * messages, should they crop up. So if someone wants to
1955 * post the things to us as part of a macro manoeuvre,
1956 * we're ready to cope.
1957 */
32874aea 1958 {
4eeb7d09 1959 char c = (unsigned char)wParam;
1960 lpage_send(CP_ACP, &c, 1);
374330e2 1961 }
1962 return 0;
32874aea 1963 case WM_SETCURSOR:
1964 if (send_raw_mouse) {
1965 SetCursor(LoadCursor(NULL, IDC_ARROW));
1966 return TRUE;
1967 }
374330e2 1968 }
1969
32874aea 1970 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 1971}
1972
1973/*
ec8679e9 1974 * Move the system caret. (We maintain one, even though it's
1975 * invisible, for the benefit of blind people: apparently some
1976 * helper software tracks the system caret, so we should arrange to
1977 * have one.)
1978 */
32874aea 1979void sys_cursor(int x, int y)
1980{
88485e4d 1981 COMPOSITIONFORM cf;
1982 HIMC hIMC;
1983
1984 if (!has_focus) return;
1985
1986 SetCaretPos(x * font_width, y * font_height);
1987
1988 /* IMM calls on Win98 and beyond only */
1989 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
1990
1991 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
1992 osVersion.dwMinorVersion == 0) return; /* 95 */
1993
1994 /* we should have the IMM functions */
1995 hIMC = ImmGetContext(hwnd);
1996 cf.dwStyle = CFS_POINT;
1997 cf.ptCurrentPos.x = x * font_width;
1998 cf.ptCurrentPos.y = y * font_height;
1999 ImmSetCompositionWindow(hIMC, &cf);
2000
2001 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2002}
2003
2004/*
374330e2 2005 * Draw a line of text in the window, at given character
2006 * coordinates, in given attributes.
2007 *
2008 * We are allowed to fiddle with the contents of `text'.
2009 */
32874aea 2010void do_text(Context ctx, int x, int y, char *text, int len,
2011 unsigned long attr, int lattr)
2012{
374330e2 2013 COLORREF fg, bg, t;
2014 int nfg, nbg, nfont;
2015 HDC hdc = ctx;
59ad2c03 2016 RECT line_box;
2017 int force_manual_underline = 0;
32874aea 2018 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2019 int char_width = fnt_width;
2020 int text_adjust = 0;
2021 static int *IpDx = 0, IpDxLEN = 0;
2022
2023 if (attr & ATTR_WIDE)
2024 char_width *= 2;
59ad2c03 2025
4eeb7d09 2026 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2027 int i;
32874aea 2028 if (len > IpDxLEN) {
59ad2c03 2029 sfree(IpDx);
32874aea 2030 IpDx = smalloc((len + 16) * sizeof(int));
2031 IpDxLEN = (len + 16);
59ad2c03 2032 }
32874aea 2033 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2034 IpDx[i] = char_width;
59ad2c03 2035 }
374330e2 2036
c9def1b8 2037 x *= fnt_width;
374330e2 2038 y *= font_height;
2039
4eeb7d09 2040 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2041 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2042 attr ^= ATTR_CUR_XOR;
2043 }
2044
2045 nfont = 0;
4eeb7d09 2046 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2047 /* Assume a poorman font is borken in other ways too. */
2048 lattr = LATTR_WIDE;
2049 } else
2050 switch (lattr) {
2051 case LATTR_NORM:
2052 break;
2053 case LATTR_WIDE:
2054 nfont |= FONT_WIDE;
374330e2 2055 break;
4eeb7d09 2056 default:
2057 nfont |= FONT_WIDE + FONT_HIGH;
2058 break;
2059 }
2060
2061 /* Special hack for the VT100 linedraw glyphs. */
2062 if ((attr & CSET_MASK) == 0x2300) {
2063 if (!dbcs_screenfont &&
2064 text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2065 switch ((unsigned char) (text[0])) {
2066 case 0xBA:
2067 text_adjust = -2 * font_height / 5;
2068 break;
2069 case 0xBB:
2070 text_adjust = -1 * font_height / 5;
2071 break;
2072 case 0xBC:
2073 text_adjust = font_height / 5;
2074 break;
2075 case 0xBD:
2076 text_adjust = 2 * font_height / 5;
32874aea 2077 break;
59ad2c03 2078 }
4eeb7d09 2079 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2080 text_adjust *= 2;
2081 attr &= ~CSET_MASK;
2082 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2083 attr |= (unitab_xterm['q'] & CSET_MASK);
2084 if (attr & ATTR_UNDER) {
2085 attr &= ~ATTR_UNDER;
2086 force_manual_underline = 1;
2087 }
374330e2 2088 }
2089 }
2090
4eeb7d09 2091 /* Anything left as an original character set is unprintable. */
2092 if (DIRECT_CHAR(attr)) {
2093 attr &= ~CSET_MASK;
2094 attr |= 0xFF00;
2095 memset(text, 0xFF, len);
2096 }
2097
2098 /* OEM CP */
2099 if ((attr & CSET_MASK) == ATTR_OEMCP)
2100 nfont |= FONT_OEM;
2101
374330e2 2102 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2103 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2104 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2105 nfont |= FONT_BOLD;
2106 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2107 nfont |= FONT_UNDERLINE;
4eeb7d09 2108 another_font(nfont);
32874aea 2109 if (!fonts[nfont]) {
2110 if (nfont & FONT_UNDERLINE)
59ad2c03 2111 force_manual_underline = 1;
2112 /* Don't do the same for manual bold, it could be bad news. */
2113
32874aea 2114 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2115 }
4eeb7d09 2116 another_font(nfont);
2117 if (!fonts[nfont])
2118 nfont = FONT_NORMAL;
374330e2 2119 if (attr & ATTR_REVERSE) {
32874aea 2120 t = nfg;
2121 nfg = nbg;
2122 nbg = t;
374330e2 2123 }
2124 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2125 nfg++;
59ad2c03 2126 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2127 nbg++;
374330e2 2128 fg = colours[nfg];
2129 bg = colours[nbg];
32874aea 2130 SelectObject(hdc, fonts[nfont]);
2131 SetTextColor(hdc, fg);
2132 SetBkColor(hdc, bg);
2133 SetBkMode(hdc, OPAQUE);
2134 line_box.left = x;
2135 line_box.top = y;
4eeb7d09 2136 line_box.right = x + char_width * len;
32874aea 2137 line_box.bottom = y + font_height;
4eeb7d09 2138
2139 /* We're using a private area for direct to font. (512 chars.) */
2140 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2141 /* Ho Hum, dbcs fonts are a PITA! */
2142 /* To display on W9x I have to convert to UCS */
2143 static wchar_t *uni_buf = 0;
2144 static int uni_len = 0;
2145 int nlen;
2146 if (len > uni_len) {
2147 sfree(uni_buf);
2148 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2149 }
2150 nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2151 text, len, uni_buf, uni_len);
2152
2153 if (nlen <= 0)
2154 return; /* Eeek! */
2155
2156 ExtTextOutW(hdc, x,
2157 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2158 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
2159 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2160 SetBkMode(hdc, TRANSPARENT);
2161 ExtTextOutW(hdc, x - 1,
2162 y - font_height * (lattr ==
2163 LATTR_BOT) + text_adjust,
2164 ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
2165 }
2166 } else if (DIRECT_FONT(attr)) {
2167 ExtTextOut(hdc, x,
2168 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2169 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2170 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2171 SetBkMode(hdc, TRANSPARENT);
2172
2173 /* GRR: This draws the character outside it's box and can leave
2174 * 'droppings' even with the clip box! I suppose I could loop it
2175 * one character at a time ... yuk.
2176 *
2177 * Or ... I could do a test print with "W", and use +1 or -1 for this
2178 * shift depending on if the leftmost column is blank...
2179 */
2180 ExtTextOut(hdc, x - 1,
2181 y - font_height * (lattr ==
2182 LATTR_BOT) + text_adjust,
2183 ETO_CLIPPED, &line_box, text, len, IpDx);
2184 }
2185 } else {
2186 /* And 'normal' unicode characters */
2187 static WCHAR *wbuf = NULL;
2188 static int wlen = 0;
2189 int i;
2190 if (wlen < len) {
2191 sfree(wbuf);
2192 wlen = len;
2193 wbuf = smalloc(wlen * sizeof(WCHAR));
2194 }
2195 for (i = 0; i < len; i++)
2196 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2197
2198 ExtTextOutW(hdc, x,
2199 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2200 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2201
2202 /* And the shadow bold hack. */
2203 if (bold_mode == BOLD_SHADOW) {
2204 SetBkMode(hdc, TRANSPARENT);
2205 ExtTextOutW(hdc, x - 1,
2206 y - font_height * (lattr ==
2207 LATTR_BOT) + text_adjust,
2208 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2209 }
374330e2 2210 }
4eeb7d09 2211 if (lattr != LATTR_TOP && (force_manual_underline ||
2212 (und_mode == UND_LINE
2213 && (attr & ATTR_UNDER)))) {
32874aea 2214 HPEN oldpen;
4eeb7d09 2215 int dec = descent;
2216 if (lattr == LATTR_BOT)
2217 dec = dec * 2 - font_height;
2218
32874aea 2219 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2220 MoveToEx(hdc, x, y + dec, NULL);
2221 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2222 oldpen = SelectObject(hdc, oldpen);
2223 DeleteObject(oldpen);
374330e2 2224 }
4eeb7d09 2225}
2226
2227void do_cursor(Context ctx, int x, int y, char *text, int len,
2228 unsigned long attr, int lattr)
2229{
2230
2231 int fnt_width;
2232 int char_width;
2233 HDC hdc = ctx;
2234 int ctype = cfg.cursor_type;
2235
2236 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2237 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2238 do_text(ctx, x, y, text, len, attr, lattr);
2239 return;
2240 }
2241 ctype = 2;
2242 attr |= TATTR_RIGHTCURS;
2243 }
2244
2245 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2246 if (attr & ATTR_WIDE)
2247 char_width *= 2;
2248 x *= fnt_width;
2249 y *= font_height;
2250
2251 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2252 POINT pts[5];
32874aea 2253 HPEN oldpen;
374330e2 2254 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2255 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2256 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2257 pts[1].y = pts[2].y = y + font_height - 1;
2258 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2259 Polyline(hdc, pts, 5);
2260 oldpen = SelectObject(hdc, oldpen);
2261 DeleteObject(oldpen);
4eeb7d09 2262 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2263 int startx, starty, dx, dy, length, i;
4eeb7d09 2264 if (ctype == 1) {
32874aea 2265 startx = x;
2266 starty = y + descent;
2267 dx = 1;
2268 dy = 0;
4eeb7d09 2269 length = char_width;
32874aea 2270 } else {
4e30ff69 2271 int xadjust = 0;
4eeb7d09 2272 if (attr & TATTR_RIGHTCURS)
2273 xadjust = char_width - 1;
32874aea 2274 startx = x + xadjust;
2275 starty = y;
2276 dx = 0;
2277 dy = 1;
2278 length = font_height;
2279 }
4eeb7d09 2280 if (attr & TATTR_ACTCURS) {
32874aea 2281 HPEN oldpen;
2282 oldpen =
2283 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2284 MoveToEx(hdc, startx, starty, NULL);
2285 LineTo(hdc, startx + dx * length, starty + dy * length);
2286 oldpen = SelectObject(hdc, oldpen);
2287 DeleteObject(oldpen);
2288 } else {
2289 for (i = 0; i < length; i++) {
2290 if (i % 2 == 0) {
2291 SetPixel(hdc, startx, starty, colours[23]);
2292 }
2293 startx += dx;
2294 starty += dy;
2295 }
2296 }
4e30ff69 2297 }
374330e2 2298}
2299
2300/*
c9def1b8 2301 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2302 * codes. Returns number of bytes used or zero to drop the message
2303 * or -1 to forward the message to windows.
374330e2 2304 */
3cf144db 2305static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2306 unsigned char *output)
2307{
374330e2 2308 BYTE keystate[256];
32874aea 2309 int scan, left_alt = 0, key_down, shift_state;
2310 int r, i, code;
2311 unsigned char *p = output;
4eeb7d09 2312 static int alt_sum = 0;
374330e2 2313
00e3ba0f 2314 HKL kbd_layout = GetKeyboardLayout(0);
2315
0c50ef57 2316 static WORD keys[3];
0c50ef57 2317 static int compose_char = 0;
2318 static WPARAM compose_key = 0;
32874aea 2319
c9def1b8 2320 r = GetKeyboardState(keystate);
32874aea 2321 if (!r)
2322 memset(keystate, 0, sizeof(keystate));
2323 else {
ec55b220 2324#if 0
4eeb7d09 2325#define SHOW_TOASCII_RESULT
32874aea 2326 { /* Tell us all about key events */
2327 static BYTE oldstate[256];
2328 static int first = 1;
2329 static int scan;
2330 int ch;
2331 if (first)
2332 memcpy(oldstate, keystate, sizeof(oldstate));
2333 first = 0;
2334
2335 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2336 debug(("+"));
2337 } else if ((HIWORD(lParam) & KF_UP)
2338 && scan == (HIWORD(lParam) & 0xFF)) {
2339 debug((". U"));
2340 } else {
2341 debug((".\n"));
2342 if (wParam >= VK_F1 && wParam <= VK_F20)
2343 debug(("K_F%d", wParam + 1 - VK_F1));
2344 else
2345 switch (wParam) {
2346 case VK_SHIFT:
2347 debug(("SHIFT"));
2348 break;
2349 case VK_CONTROL:
2350 debug(("CTRL"));
2351 break;
2352 case VK_MENU:
2353 debug(("ALT"));
2354 break;
2355 default:
2356 debug(("VK_%02x", wParam));
2357 }
2358 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2359 debug(("*"));
2360 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2361
2362 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2363 if (ch >= ' ' && ch <= '~')
2364 debug((", '%c'", ch));
2365 else if (ch)
2366 debug((", $%02x", ch));
2367
2368 if (keys[0])
2369 debug((", KB0=%02x", keys[0]));
2370 if (keys[1])
2371 debug((", KB1=%02x", keys[1]));
2372 if (keys[2])
2373 debug((", KB2=%02x", keys[2]));
2374
2375 if ((keystate[VK_SHIFT] & 0x80) != 0)
2376 debug((", S"));
2377 if ((keystate[VK_CONTROL] & 0x80) != 0)
2378 debug((", C"));
2379 if ((HIWORD(lParam) & KF_EXTENDED))
2380 debug((", E"));
2381 if ((HIWORD(lParam) & KF_UP))
2382 debug((", U"));
2383 }
2384
2385 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2386 else if ((HIWORD(lParam) & KF_UP))
2387 oldstate[wParam & 0xFF] ^= 0x80;
2388 else
2389 oldstate[wParam & 0xFF] ^= 0x81;
2390
2391 for (ch = 0; ch < 256; ch++)
2392 if (oldstate[ch] != keystate[ch])
2393 debug((", M%02x=%02x", ch, keystate[ch]));
2394
2395 memcpy(oldstate, keystate, sizeof(oldstate));
2396 }
ec55b220 2397#endif
2398
32874aea 2399 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2400 keystate[VK_RMENU] = keystate[VK_MENU];
2401 }
2402
c9def1b8 2403
2404 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2405 if ((cfg.funky_type == 3 ||
2406 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2407 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2408
2409 wParam = VK_EXECUTE;
2410
2411 /* UnToggle NUMLock */
32874aea 2412 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2413 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2414 }
2415
2416 /* And write back the 'adjusted' state */
32874aea 2417 SetKeyboardState(keystate);
c9def1b8 2418 }
2419
2420 /* Disable Auto repeat if required */
32874aea 2421 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2422 return 0;
c9def1b8 2423
32874aea 2424 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2425 left_alt = 1;
2426
32874aea 2427 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2428
95bbe1ae 2429 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2430 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2431 if (cfg.ctrlaltkeys)
2432 keystate[VK_MENU] = 0;
2433 else {
2434 keystate[VK_RMENU] = 0x80;
2435 left_alt = 0;
2436 }
2437 }
c9def1b8 2438
2439 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2440 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2441 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2442
95bbe1ae 2443 /* Note if AltGr was pressed and if it was used as a compose key */
2444 if (!compose_state) {
159eba53 2445 compose_key = 0x100;
95bbe1ae 2446 if (cfg.compose_key) {
32874aea 2447 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2448 compose_key = wParam;
2449 }
2450 if (wParam == VK_APPS)
2451 compose_key = wParam;
2452 }
2453
32874aea 2454 if (wParam == compose_key) {
2455 if (compose_state == 0
2456 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2457 1;
2458 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2459 compose_state = 2;
2460 else
2461 compose_state = 0;
32874aea 2462 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2463 compose_state = 0;
2464
67c339f7 2465 /*
cabfd08c 2466 * Record that we pressed key so the scroll window can be reset, but
2467 * be careful to avoid Shift-UP/Down
2468 */
32874aea 2469 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2470 seen_key_event = 1;
cabfd08c 2471 }
2472
c9def1b8 2473 /* Make sure we're not pasting */
32874aea 2474 if (key_down)
2475 term_nopaste();
67c339f7 2476
32874aea 2477 if (compose_state > 1 && left_alt)
2478 compose_state = 0;
67c339f7 2479
c9def1b8 2480 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2481 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2482 && cfg.funky_type != 2)
2483 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2484 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2485 int nParam = 0;
32874aea 2486 switch (wParam) {
2487 case VK_INSERT:
2488 nParam = VK_NUMPAD0;
2489 break;
2490 case VK_END:
2491 nParam = VK_NUMPAD1;
2492 break;
2493 case VK_DOWN:
2494 nParam = VK_NUMPAD2;
2495 break;
2496 case VK_NEXT:
2497 nParam = VK_NUMPAD3;
2498 break;
2499 case VK_LEFT:
2500 nParam = VK_NUMPAD4;
2501 break;
2502 case VK_CLEAR:
2503 nParam = VK_NUMPAD5;
2504 break;
2505 case VK_RIGHT:
2506 nParam = VK_NUMPAD6;
2507 break;
2508 case VK_HOME:
2509 nParam = VK_NUMPAD7;
2510 break;
2511 case VK_UP:
2512 nParam = VK_NUMPAD8;
2513 break;
2514 case VK_PRIOR:
2515 nParam = VK_NUMPAD9;
2516 break;
2517 case VK_DELETE:
2518 nParam = VK_DECIMAL;
2519 break;
c9def1b8 2520 }
32874aea 2521 if (nParam) {
2522 if (keystate[VK_NUMLOCK] & 1)
2523 shift_state |= 1;
c9def1b8 2524 wParam = nParam;
2525 }
25d39ef6 2526 }
2527 }
2528
c9def1b8 2529 /* If a key is pressed and AltGr is not active */
32874aea 2530 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2531 /* Okay, prepare for most alts then ... */
2532 if (left_alt)
2533 *p++ = '\033';
374330e2 2534
c9def1b8 2535 /* Lets see if it's a pattern we know all about ... */
2536 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2537 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2538 return 0;
c9def1b8 2539 }
2540 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2541 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2542 return 0;
2543 }
2544 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 2545 term_do_paste();
32874aea 2546 return 0;
2547 }
c9def1b8 2548 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 2549 return -1;
c9def1b8 2550 }
2551 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 2552 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2553 return -1;
c9def1b8 2554 }
ec55b220 2555 /* Control-Numlock for app-keypad mode switch */
2556 if (wParam == VK_PAUSE && shift_state == 2) {
2557 app_keypad_keys ^= 1;
2558 return 0;
2559 }
374330e2 2560
c9def1b8 2561 /* Nethack keypad */
2562 if (cfg.nethack_keypad && !left_alt) {
32874aea 2563 switch (wParam) {
2564 case VK_NUMPAD1:
2565 *p++ = shift_state ? 'B' : 'b';
2566 return p - output;
2567 case VK_NUMPAD2:
2568 *p++ = shift_state ? 'J' : 'j';
2569 return p - output;
2570 case VK_NUMPAD3:
2571 *p++ = shift_state ? 'N' : 'n';
2572 return p - output;
2573 case VK_NUMPAD4:
2574 *p++ = shift_state ? 'H' : 'h';
2575 return p - output;
2576 case VK_NUMPAD5:
2577 *p++ = shift_state ? '.' : '.';
2578 return p - output;
2579 case VK_NUMPAD6:
2580 *p++ = shift_state ? 'L' : 'l';
2581 return p - output;
2582 case VK_NUMPAD7:
2583 *p++ = shift_state ? 'Y' : 'y';
2584 return p - output;
2585 case VK_NUMPAD8:
2586 *p++ = shift_state ? 'K' : 'k';
2587 return p - output;
2588 case VK_NUMPAD9:
2589 *p++ = shift_state ? 'U' : 'u';
2590 return p - output;
2591 }
c9def1b8 2592 }
2593
2594 /* Application Keypad */
2595 if (!left_alt) {
32874aea 2596 int xkey = 0;
2597
2598 if (cfg.funky_type == 3 ||
2599 (cfg.funky_type <= 1 &&
2600 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2601 case VK_EXECUTE:
2602 xkey = 'P';
2603 break;
2604 case VK_DIVIDE:
2605 xkey = 'Q';
2606 break;
2607 case VK_MULTIPLY:
2608 xkey = 'R';
2609 break;
2610 case VK_SUBTRACT:
2611 xkey = 'S';
2612 break;
2613 }
2614 if (app_keypad_keys && !cfg.no_applic_k)
2615 switch (wParam) {
2616 case VK_NUMPAD0:
2617 xkey = 'p';
2618 break;
2619 case VK_NUMPAD1:
2620 xkey = 'q';
2621 break;
2622 case VK_NUMPAD2:
2623 xkey = 'r';
2624 break;
2625 case VK_NUMPAD3:
2626 xkey = 's';
2627 break;
2628 case VK_NUMPAD4:
2629 xkey = 't';
2630 break;
2631 case VK_NUMPAD5:
2632 xkey = 'u';
2633 break;
2634 case VK_NUMPAD6:
2635 xkey = 'v';
2636 break;
2637 case VK_NUMPAD7:
2638 xkey = 'w';
2639 break;
2640 case VK_NUMPAD8:
2641 xkey = 'x';
2642 break;
2643 case VK_NUMPAD9:
2644 xkey = 'y';
2645 break;
2646
2647 case VK_DECIMAL:
2648 xkey = 'n';
2649 break;
2650 case VK_ADD:
2651 if (cfg.funky_type == 2) {
2652 if (shift_state)
2653 xkey = 'l';
2654 else
2655 xkey = 'k';
2656 } else if (shift_state)
2657 xkey = 'm';
c9def1b8 2658 else
32874aea 2659 xkey = 'l';
2660 break;
2661
2662 case VK_DIVIDE:
2663 if (cfg.funky_type == 2)
2664 xkey = 'o';
2665 break;
2666 case VK_MULTIPLY:
2667 if (cfg.funky_type == 2)
2668 xkey = 'j';
2669 break;
2670 case VK_SUBTRACT:
2671 if (cfg.funky_type == 2)
2672 xkey = 'm';
2673 break;
2674
2675 case VK_RETURN:
2676 if (HIWORD(lParam) & KF_EXTENDED)
2677 xkey = 'M';
2678 break;
c9def1b8 2679 }
32874aea 2680 if (xkey) {
2681 if (vt52_mode) {
2682 if (xkey >= 'P' && xkey <= 'S')
2683 p += sprintf((char *) p, "\x1B%c", xkey);
2684 else
2685 p += sprintf((char *) p, "\x1B?%c", xkey);
2686 } else
2687 p += sprintf((char *) p, "\x1BO%c", xkey);
2688 return p - output;
c9def1b8 2689 }
2690 }
2691
32874aea 2692 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 2693 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 2694 *p++ = 0;
2695 return -2;
c9def1b8 2696 }
32874aea 2697 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
2698 *p++ = 0x1B;
2699 *p++ = '[';
2700 *p++ = 'Z';
2701 return p - output;
c9def1b8 2702 }
32874aea 2703 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
2704 *p++ = 0;
2705 return p - output;
c9def1b8 2706 }
32874aea 2707 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
2708 *p++ = 160;
2709 return p - output;
c9def1b8 2710 }
32874aea 2711 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
2712 *p++ = 3;
a5f3e637 2713 *p++ = 0;
2714 return -2;
c9def1b8 2715 }
32874aea 2716 if (wParam == VK_PAUSE) { /* Break/Pause */
2717 *p++ = 26;
2718 *p++ = 0;
2719 return -2;
95bbe1ae 2720 }
c9def1b8 2721 /* Control-2 to Control-8 are special */
32874aea 2722 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
2723 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 2724 return p - output;
2725 }
2726 if (shift_state == 2 && wParam == 0xBD) {
2727 *p++ = 0x1F;
2728 return p - output;
2729 }
2730 if (shift_state == 2 && wParam == 0xDF) {
2731 *p++ = 0x1C;
2732 return p - output;
2733 }
2734 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 2735 *p++ = '\r';
2736 *p++ = '\n';
c9def1b8 2737 return p - output;
2738 }
374330e2 2739
c5e9c988 2740 /*
c9def1b8 2741 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
2742 * for integer decimal nn.)
2743 *
2744 * We also deal with the weird ones here. Linux VCs replace F1
2745 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
2746 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
2747 * respectively.
c5e9c988 2748 */
c9def1b8 2749 code = 0;
2750 switch (wParam) {
32874aea 2751 case VK_F1:
2752 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
2753 break;
2754 case VK_F2:
2755 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
2756 break;
2757 case VK_F3:
2758 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
2759 break;
2760 case VK_F4:
2761 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
2762 break;
2763 case VK_F5:
2764 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
2765 break;
2766 case VK_F6:
2767 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
2768 break;
2769 case VK_F7:
2770 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
2771 break;
2772 case VK_F8:
2773 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
2774 break;
2775 case VK_F9:
2776 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
2777 break;
2778 case VK_F10:
2779 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
2780 break;
2781 case VK_F11:
2782 code = 23;
2783 break;
2784 case VK_F12:
2785 code = 24;
2786 break;
2787 case VK_F13:
2788 code = 25;
2789 break;
2790 case VK_F14:
2791 code = 26;
2792 break;
2793 case VK_F15:
2794 code = 28;
2795 break;
2796 case VK_F16:
2797 code = 29;
2798 break;
2799 case VK_F17:
2800 code = 31;
2801 break;
2802 case VK_F18:
2803 code = 32;
2804 break;
2805 case VK_F19:
2806 code = 33;
2807 break;
2808 case VK_F20:
2809 code = 34;
2810 break;
dfca2656 2811 }
2812 if ((shift_state&2) == 0) switch (wParam) {
32874aea 2813 case VK_HOME:
2814 code = 1;
2815 break;
2816 case VK_INSERT:
2817 code = 2;
2818 break;
2819 case VK_DELETE:
2820 code = 3;
2821 break;
2822 case VK_END:
2823 code = 4;
2824 break;
2825 case VK_PRIOR:
2826 code = 5;
2827 break;
2828 case VK_NEXT:
2829 code = 6;
2830 break;
374330e2 2831 }
ec55b220 2832 /* Reorder edit keys to physical order */
32874aea 2833 if (cfg.funky_type == 3 && code <= 6)
2834 code = "\0\2\1\4\5\3\6"[code];
ec55b220 2835
f37caa11 2836 if (vt52_mode && code > 0 && code <= 6) {
32874aea 2837 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 2838 return p - output;
2839 }
2840
9bc81a2c 2841 if (cfg.funky_type == 5 && /* SCO function keys */
2842 code >= 11 && code <= 34) {
e24b1972 2843 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
2844 int index = 0;
2845 switch (wParam) {
2846 case VK_F1: index = 0; break;
2847 case VK_F2: index = 1; break;
2848 case VK_F3: index = 2; break;
2849 case VK_F4: index = 3; break;
2850 case VK_F5: index = 4; break;
2851 case VK_F6: index = 5; break;
2852 case VK_F7: index = 6; break;
2853 case VK_F8: index = 7; break;
2854 case VK_F9: index = 8; break;
2855 case VK_F10: index = 9; break;
2856 case VK_F11: index = 10; break;
2857 case VK_F12: index = 11; break;
2858 }
2859 if (keystate[VK_SHIFT] & 0x80) index += 12;
2860 if (keystate[VK_CONTROL] & 0x80) index += 24;
2861 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 2862 return p - output;
2863 }
9bc81a2c 2864 if (cfg.funky_type == 5 && /* SCO small keypad */
2865 code >= 1 && code <= 6) {
2866 char codes[] = "HL.FIG";
2867 if (code == 3) {
2868 *p++ = '\x7F';
2869 } else {
2870 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
2871 }
2872 return p - output;
2873 }
f37caa11 2874 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
2875 int offt = 0;
32874aea 2876 if (code > 15)
2877 offt++;
2878 if (code > 21)
2879 offt++;
f37caa11 2880 if (vt52_mode)
32874aea 2881 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 2882 else
32874aea 2883 p +=
2884 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 2885 return p - output;
2886 }
c9def1b8 2887 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 2888 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 2889 return p - output;
2890 }
2891 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 2892 if (vt52_mode)
32874aea 2893 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 2894 else
32874aea 2895 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 2896 return p - output;
2897 }
2898 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 2899 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 2900 return p - output;
2901 }
2902 if (code) {
32874aea 2903 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 2904 return p - output;
374330e2 2905 }
45dabbc5 2906
c9def1b8 2907 /*
2908 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
2909 * some reason seems to send VK_CLEAR to Windows...).
2910 */
2911 {
2912 char xkey = 0;
2913 switch (wParam) {
32874aea 2914 case VK_UP:
2915 xkey = 'A';
2916 break;
2917 case VK_DOWN:
2918 xkey = 'B';
2919 break;
2920 case VK_RIGHT:
2921 xkey = 'C';
2922 break;
2923 case VK_LEFT:
2924 xkey = 'D';
2925 break;
2926 case VK_CLEAR:
2927 xkey = 'G';
2928 break;
c9def1b8 2929 }
32874aea 2930 if (xkey) {
c9def1b8 2931 if (vt52_mode)
32874aea 2932 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 2933 else {
2934 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
2935 /* VT100 & VT102 manuals both state the app cursor keys
2936 * only work if the app keypad is on.
2937 */
2938 if (!app_keypad_keys)
2939 app_flg = 0;
2940 /* Useful mapping of Ctrl-arrows */
2941 if (shift_state == 2)
2942 app_flg = !app_flg;
2943
2944 if (app_flg)
2945 p += sprintf((char *) p, "\x1BO%c", xkey);
2946 else
2947 p += sprintf((char *) p, "\x1B[%c", xkey);
2948 }
c9def1b8 2949 return p - output;
2950 }
2951 }
0c50ef57 2952
2953 /*
2954 * Finally, deal with Return ourselves. (Win95 seems to
2955 * foul it up when Alt is pressed, for some reason.)
2956 */
32874aea 2957 if (wParam == VK_RETURN) { /* Return */
0c50ef57 2958 *p++ = 0x0D;
a5f3e637 2959 *p++ = 0;
2960 return -2;
0c50ef57 2961 }
4eeb7d09 2962
2963 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
2964 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
2965 else
2966 alt_sum = 0;
67c339f7 2967 }
374330e2 2968
c9def1b8 2969 /* Okay we've done everything interesting; let windows deal with
2970 * the boring stuff */
2971 {
00e3ba0f 2972 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 2973#ifdef SHOW_TOASCII_RESULT
2974 if (r == 1 && !key_down) {
2975 if (alt_sum) {
8f22582c 2976 if (in_utf || dbcs_screenfont)
4eeb7d09 2977 debug((", (U+%04x)", alt_sum));
2978 else
2979 debug((", LCH(%d)", alt_sum));
2980 } else {
2981 debug((", ACH(%d)", keys[0]));
2982 }
2983 } else if (r > 0) {
2984 int r1;
2985 debug((", ASC("));
2986 for (r1 = 0; r1 < r; r1++) {
2987 debug(("%s%d", r1 ? "," : "", keys[r1]));
2988 }
2989 debug((")"));
2990 }
2991#endif
32874aea 2992 if (r > 0) {
4eeb7d09 2993 WCHAR keybuf;
c9def1b8 2994 p = output;
32874aea 2995 for (i = 0; i < r; i++) {
2996 unsigned char ch = (unsigned char) keys[i];
14963b8f 2997
32874aea 2998 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 2999 compose_char = ch;
32874aea 3000 compose_state++;
c9def1b8 3001 continue;
3002 }
32874aea 3003 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3004 int nc;
3005 compose_state = 0;
3006
32874aea 3007 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3008 MessageBeep(MB_ICONHAND);
c9def1b8 3009 return 0;
3010 }
4eeb7d09 3011 keybuf = nc;
3012 luni_send(&keybuf, 1);
3013 continue;
c9def1b8 3014 }
374330e2 3015
c9def1b8 3016 compose_state = 0;
374330e2 3017
4eeb7d09 3018 if (!key_down) {
3019 if (alt_sum) {
8f22582c 3020 if (in_utf || dbcs_screenfont) {
4eeb7d09 3021 keybuf = alt_sum;
3022 luni_send(&keybuf, 1);
3023 } else {
3024 ch = (char) alt_sum;
5471d09a 3025 /*
3026 * We need not bother about stdin
3027 * backlogs here, because in GUI PuTTY
3028 * we can't do anything about it
3029 * anyway; there's no means of asking
3030 * Windows to hold off on KEYDOWN
3031 * messages. We _have_ to buffer
3032 * everything we're sent.
3033 */
4eeb7d09 3034 ldisc_send(&ch, 1);
3035 }
3036 alt_sum = 0;
3037 } else
3038 lpage_send(kbd_codepage, &ch, 1);
3039 } else {
3040 static char cbuf[] = "\033 ";
3041 cbuf[1] = ch;
3042 lpage_send(kbd_codepage, cbuf + !left_alt,
3043 1 + !!left_alt);
c9def1b8 3044 }
bca9517a 3045 show_mouseptr(0);
c9def1b8 3046 }
374330e2 3047
c9def1b8 3048 /* This is so the ALT-Numpad and dead keys work correctly. */
3049 keys[0] = 0;
3050
32874aea 3051 return p - output;
c9def1b8 3052 }
159eba53 3053 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3054 if (!left_alt)
3055 keys[0] = 0;
4eeb7d09 3056 /* If we will be using alt_sum fix the 256s */
8f22582c 3057 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3058 keys[0] = 10;
374330e2 3059 }
3060
dfca2656 3061 /*
3062 * ALT alone may or may not want to bring up the System menu.
3063 * If it's not meant to, we return 0 on presses or releases of
3064 * ALT, to show that we've swallowed the keystroke. Otherwise
3065 * we return -1, which means Windows will give the keystroke
3066 * its default handling (i.e. bring up the System menu).
3067 */
3068 if (wParam == VK_MENU && !cfg.alt_only)
3069 return 0;
374330e2 3070
c9def1b8 3071 return -1;
374330e2 3072}
3073
32874aea 3074void set_title(char *title)
3075{
3076 sfree(window_name);
3077 window_name = smalloc(1 + strlen(title));
3078 strcpy(window_name, title);
37508af4 3079 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3080 SetWindowText(hwnd, title);
374330e2 3081}
3082
32874aea 3083void set_icon(char *title)
3084{
3085 sfree(icon_name);
3086 icon_name = smalloc(1 + strlen(title));
3087 strcpy(icon_name, title);
37508af4 3088 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3089 SetWindowText(hwnd, title);
374330e2 3090}
3091
32874aea 3092void set_sbar(int total, int start, int page)
3093{
374330e2 3094 SCROLLINFO si;
c9def1b8 3095
32874aea 3096 if (!cfg.scrollbar)
3097 return;
c9def1b8 3098
374330e2 3099 si.cbSize = sizeof(si);
c9def1b8 3100 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3101 si.nMin = 0;
3102 si.nMax = total - 1;
3103 si.nPage = page;
3104 si.nPos = start;
c1f5f956 3105 if (hwnd)
32874aea 3106 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3107}
3108
32874aea 3109Context get_ctx(void)
3110{
374330e2 3111 HDC hdc;
3112 if (hwnd) {
32874aea 3113 hdc = GetDC(hwnd);
374330e2 3114 if (hdc && pal)
32874aea 3115 SelectPalette(hdc, pal, FALSE);
374330e2 3116 return hdc;
3117 } else
3118 return NULL;
3119}
3120
32874aea 3121void free_ctx(Context ctx)
3122{
3123 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3124 ReleaseDC(hwnd, ctx);
374330e2 3125}
3126
32874aea 3127static void real_palette_set(int n, int r, int g, int b)
3128{
374330e2 3129 if (pal) {
3130 logpal->palPalEntry[n].peRed = r;
3131 logpal->palPalEntry[n].peGreen = g;
3132 logpal->palPalEntry[n].peBlue = b;
3133 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3134 colours[n] = PALETTERGB(r, g, b);
32874aea 3135 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3136 } else
3137 colours[n] = RGB(r, g, b);
3138}
3139
32874aea 3140void palette_set(int n, int r, int g, int b)
3141{
374330e2 3142 static const int first[21] = {
3143 0, 2, 4, 6, 8, 10, 12, 14,
3144 1, 3, 5, 7, 9, 11, 13, 15,
3145 16, 17, 18, 20, 22
3146 };
32874aea 3147 real_palette_set(first[n], r, g, b);
374330e2 3148 if (first[n] >= 18)
32874aea 3149 real_palette_set(first[n] + 1, r, g, b);
374330e2 3150 if (pal) {
3151 HDC hdc = get_ctx();
32874aea 3152 UnrealizeObject(pal);
3153 RealizePalette(hdc);
3154 free_ctx(hdc);
374330e2 3155 }
3156}
3157
32874aea 3158void palette_reset(void)
3159{
374330e2 3160 int i;
3161
3162 for (i = 0; i < NCOLOURS; i++) {
3163 if (pal) {
3164 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3165 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3166 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3167 logpal->palPalEntry[i].peFlags = 0;
3168 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3169 defpal[i].rgbtGreen,
3170 defpal[i].rgbtBlue);
3171 } else
3172 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3173 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3174 }
3175
3176 if (pal) {
3177 HDC hdc;
32874aea 3178 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3179 hdc = get_ctx();
32874aea 3180 RealizePalette(hdc);
3181 free_ctx(hdc);
374330e2 3182 }
3183}
3184
4eeb7d09 3185void write_aclip(char *data, int len, int must_deselect)
32874aea 3186{
374330e2 3187 HGLOBAL clipdata;
3188 void *lock;
3189
32874aea 3190 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3191 if (!clipdata)
3192 return;
32874aea 3193 lock = GlobalLock(clipdata);
374330e2 3194 if (!lock)
3195 return;
32874aea 3196 memcpy(lock, data, len);
3197 ((unsigned char *) lock)[len] = 0;
3198 GlobalUnlock(clipdata);
374330e2 3199
f0df44da 3200 if (!must_deselect)
32874aea 3201 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3202
32874aea 3203 if (OpenClipboard(hwnd)) {
374330e2 3204 EmptyClipboard();
32874aea 3205 SetClipboardData(CF_TEXT, clipdata);
374330e2 3206 CloseClipboard();
3207 } else
32874aea 3208 GlobalFree(clipdata);
f0df44da 3209
3210 if (!must_deselect)
32874aea 3211 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3212}
3213
4eeb7d09 3214/*
3215 * Note: unlike write_aclip() this will not append a nul.
3216 */
3217void write_clip(wchar_t * data, int len, int must_deselect)
3218{
3219 HGLOBAL clipdata;
3220 HGLOBAL clipdata2;
3221 int len2;
3222 void *lock, *lock2;
3223
3224 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3225
3226 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3227 len * sizeof(wchar_t));
3228 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3229
3230 if (!clipdata || !clipdata2) {
3231 if (clipdata)
3232 GlobalFree(clipdata);
3233 if (clipdata2)
3234 GlobalFree(clipdata2);
3235 return;
3236 }
3237 if (!(lock = GlobalLock(clipdata)))
3238 return;
3239 if (!(lock2 = GlobalLock(clipdata2)))
3240 return;
3241
3242 memcpy(lock, data, len * sizeof(wchar_t));
3243 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3244
3245 GlobalUnlock(clipdata);
3246 GlobalUnlock(clipdata2);
3247
3248 if (!must_deselect)
3249 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3250
3251 if (OpenClipboard(hwnd)) {
3252 EmptyClipboard();
3253 SetClipboardData(CF_UNICODETEXT, clipdata);
3254 SetClipboardData(CF_TEXT, clipdata2);
3255 CloseClipboard();
3256 } else {
3257 GlobalFree(clipdata);
3258 GlobalFree(clipdata2);
3259 }
3260
3261 if (!must_deselect)
3262 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3263}
3264
3265void get_clip(wchar_t ** p, int *len)
32874aea 3266{
374330e2 3267 static HGLOBAL clipdata = NULL;
4eeb7d09 3268 static wchar_t *converted = 0;
3269 wchar_t *p2;
374330e2 3270
4eeb7d09 3271 if (converted) {
3272 sfree(converted);
3273 converted = 0;
3274 }
374330e2 3275 if (!p) {
3276 if (clipdata)
32874aea 3277 GlobalUnlock(clipdata);
374330e2 3278 clipdata = NULL;
3279 return;
4eeb7d09 3280 } else if (OpenClipboard(NULL)) {
2d466ffd 3281 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3282 CloseClipboard();
4eeb7d09 3283 *p = GlobalLock(clipdata);
3284 if (*p) {
3285 for (p2 = *p; *p2; p2++);
3286 *len = p2 - *p;
3287 return;
374330e2 3288 }
2d466ffd 3289 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3290 char *s;
3291 int i;
3292 CloseClipboard();
3293 s = GlobalLock(clipdata);
3294 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3295 *p = converted = smalloc(i * sizeof(wchar_t));
3296 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3297 *len = i - 1;
3298 return;
3299 } else
3300 CloseClipboard();
374330e2 3301 }
3302
3303 *p = NULL;
3304 *len = 0;
3305}
3306
4eeb7d09 3307#if 0
374330e2 3308/*
3309 * Move `lines' lines from position `from' to position `to' in the
3310 * window.
3311 */
32874aea 3312void optimised_move(int to, int from, int lines)
3313{
374330e2 3314 RECT r;
f67b4e85 3315 int min, max;
374330e2 3316
3317 min = (to < from ? to : from);
3318 max = to + from - min;
374330e2 3319
32874aea 3320 r.left = 0;
3321 r.right = cols * font_width;
3322 r.top = min * font_height;
3323 r.bottom = (max + lines) * font_height;
3324 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3325}
4eeb7d09 3326#endif
374330e2 3327
3328/*
3329 * Print a message box and perform a fatal exit.
3330 */
32874aea 3331void fatalbox(char *fmt, ...)
3332{
374330e2 3333 va_list ap;
3334 char stuff[200];
3335
3336 va_start(ap, fmt);
3337 vsprintf(stuff, fmt, ap);
3338 va_end(ap);
3339 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3340 exit(1);
3341}
3342
3343/*
f8a28d1f 3344 * Manage window caption / taskbar flashing, if enabled.
3345 * 0 = stop, 1 = maintain, 2 = start
3346 */
3347static void flash_window(int mode)
3348{
3349 static long last_flash = 0;
3350 static int flashing = 0;
3351 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3352 /* stop */
3353 if (flashing) {
3354 FlashWindow(hwnd, FALSE);
3355 flashing = 0;
3356 }
3357
3358 } else if (mode == 2) {
3359 /* start */
3360 if (!flashing) {
3361 last_flash = GetTickCount();
3362 flashing = 1;
3363 FlashWindow(hwnd, TRUE);
3364 }
3365
3366 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3367 /* maintain */
3368 if (flashing) {
3369 long now = GetTickCount();
3370 long fdiff = now - last_flash;
3371 if (fdiff < 0 || fdiff > 450) {
3372 last_flash = now;
3373 FlashWindow(hwnd, TRUE); /* toggle */
3374 }
3375 }
3376 }
3377}
3378
3379/*
374330e2 3380 * Beep.
3381 */
32874aea 3382void beep(int mode)
3383{
03169ad0 3384 if (mode == BELL_DEFAULT) {
eb04402e 3385 /*
3386 * For MessageBeep style bells, we want to be careful of
3387 * timing, because they don't have the nice property of
3388 * PlaySound bells that each one cancels the previous
3389 * active one. So we limit the rate to one per 50ms or so.
3390 */
3391 static long lastbeep = 0;
d51cdf1e 3392 long beepdiff;
eb04402e 3393
d51cdf1e 3394 beepdiff = GetTickCount() - lastbeep;
eb04402e 3395 if (beepdiff >= 0 && beepdiff < 50)
3396 return;
156686ef 3397 MessageBeep(MB_OK);
d51cdf1e 3398 /*
3399 * The above MessageBeep call takes time, so we record the
3400 * time _after_ it finishes rather than before it starts.
3401 */
3402 lastbeep = GetTickCount();
03169ad0 3403 } else if (mode == BELL_WAVEFILE) {
3404 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 3405 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 3406 sprintf(buf, "Unable to play sound file\n%s\n"
3407 "Using default sound instead", cfg.bell_wavefile);
32874aea 3408 MessageBox(hwnd, buf, "PuTTY Sound Error",
3409 MB_OK | MB_ICONEXCLAMATION);
03169ad0 3410 cfg.beep = BELL_DEFAULT;
3411 }
3412 }
f8a28d1f 3413 /* Otherwise, either visual bell or disabled; do nothing here */
3414 if (!has_focus) {
3415 flash_window(2); /* start */
3416 }
374330e2 3417}