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