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