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