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