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