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