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