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