Add a couple more FAQs (`where's the Tunnels panel' and `how do I do
[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
32874aea 1338static void click(Mouse_Button b, int x, int y, int shift, int ctrl)
1339{
fdedf2c8 1340 int thistime = GetMessageTime();
1341
b90840c3 1342 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1343 lastbtn = MBT_NOTHING;
01c034ad 1344 term_mouse(b, MA_CLICK, x, y, shift, ctrl);
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)
32874aea 1357 term_mouse(b, lastact, x, y, shift, ctrl);
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
32874aea 1388static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1389 WPARAM wParam, LPARAM lParam)
1390{
374330e2 1391 HDC hdc;
374330e2 1392 static int ignore_clip = FALSE;
ffc31afe 1393 static int resizing = FALSE;
3ad8c6db 1394 static int need_backend_resize = FALSE;
374330e2 1395
1396 switch (message) {
59ad2c03 1397 case WM_TIMER:
1398 if (pending_netevent)
1399 enact_pending_netevent();
a748a096 1400 term_out();
32874aea 1401 noise_regular();
1402 HideCaret(hwnd);
59ad2c03 1403 term_update();
32874aea 1404 ShowCaret(hwnd);
1405 if (cfg.ping_interval > 0) {
1406 time_t now;
1407 time(&now);
1408 if (now - last_movement > cfg.ping_interval) {
1409 back->special(TS_PING);
1410 last_movement = now;
1411 }
1412 }
7732d38a 1413 net_pending_errors();
59ad2c03 1414 return 0;
374330e2 1415 case WM_CREATE:
1416 break;
68130d34 1417 case WM_CLOSE:
32874aea 1418 show_mouseptr(1);
d85548fe 1419 if (!cfg.warn_on_close || session_closed ||
32874aea 1420 MessageBox(hwnd,
1421 "Are you sure you want to close this session?",
68130d34 1422 "PuTTY Exit Confirmation",
1423 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1424 DestroyWindow(hwnd);
1425 return 0;
374330e2 1426 case WM_DESTROY:
32874aea 1427 show_mouseptr(1);
1428 PostQuitMessage(0);
374330e2 1429 return 0;
6833a413 1430 case WM_SYSCOMMAND:
1431 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
374330e2 1432 case IDM_SHOWLOG:
c5e9c988 1433 showeventlog(hwnd);
374330e2 1434 break;
1435 case IDM_NEWSESS:
1436 case IDM_DUPSESS:
6833a413 1437 case IDM_SAVEDSESS:
374330e2 1438 {
1439 char b[2048];
1440 char c[30], *cl;
e4e4cc7e 1441 int freecl = FALSE;
374330e2 1442 STARTUPINFO si;
1443 PROCESS_INFORMATION pi;
1444 HANDLE filemap = NULL;
1445
1446 if (wParam == IDM_DUPSESS) {
1447 /*
1448 * Allocate a file-mapping memory chunk for the
1449 * config structure.
1450 */
1451 SECURITY_ATTRIBUTES sa;
1452 Config *p;
1453
1454 sa.nLength = sizeof(sa);
1455 sa.lpSecurityDescriptor = NULL;
1456 sa.bInheritHandle = TRUE;
32874aea 1457 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
374330e2 1458 &sa,
1459 PAGE_READWRITE,
32874aea 1460 0, sizeof(Config), NULL);
374330e2 1461 if (filemap) {
32874aea 1462 p = (Config *) MapViewOfFile(filemap,
1463 FILE_MAP_WRITE,
1464 0, 0, sizeof(Config));
374330e2 1465 if (p) {
1466 *p = cfg; /* structure copy */
1467 UnmapViewOfFile(p);
1468 }
1469 }
1d470ad2 1470 sprintf(c, "putty &%p", filemap);
374330e2 1471 cl = c;
0a4aa984 1472 } else if (wParam == IDM_SAVEDSESS) {
32874aea 1473 char *session =
1474 sessions[(lParam - IDM_SAVED_MIN) / 16];
1475 cl = smalloc(16 + strlen(session)); /* 8, but play safe */
e4e4cc7e 1476 if (!cl)
1477 cl = NULL; /* not a very important failure mode */
94e6450e 1478 else {
1479 sprintf(cl, "putty @%s", session);
1480 freecl = TRUE;
1481 }
374330e2 1482 } else
6833a413 1483 cl = NULL;
374330e2 1484
32874aea 1485 GetModuleFileName(NULL, b, sizeof(b) - 1);
374330e2 1486 si.cb = sizeof(si);
1487 si.lpReserved = NULL;
1488 si.lpDesktop = NULL;
1489 si.lpTitle = NULL;
1490 si.dwFlags = 0;
1491 si.cbReserved2 = 0;
1492 si.lpReserved2 = NULL;
32874aea 1493 CreateProcess(b, cl, NULL, NULL, TRUE,
1494 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
374330e2 1495
1496 if (filemap)
1497 CloseHandle(filemap);
e4e4cc7e 1498 if (freecl)
dcbde236 1499 sfree(cl);
374330e2 1500 }
1501 break;
32874aea 1502 case IDM_RECONF:
1503 {
5a73255e 1504 Config prev_cfg;
1505 int init_lvl = 1;
1506
32874aea 1507 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
5a73255e 1508 prev_cfg = cfg;
e1c8e0ed 1509
32874aea 1510 if (!do_reconfig(hwnd))
1511 break;
e1c8e0ed 1512
a401e5f3 1513 /* If user forcibly disables full-screen, gracefully unzoom */
1514 if (full_screen && !cfg.fullscreenonaltenter) {
1515 flip_full_screen();
1516 }
1517
5a73255e 1518 if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
1519 prev_cfg.logtype != cfg.logtype) {
e1c8e0ed 1520 logfclose(); /* reset logging */
1521 logfopen();
1522 }
1523
32874aea 1524 sfree(logpal);
1525 /*
1526 * Flush the line discipline's edit buffer in the
1527 * case where local editing has just been disabled.
1528 */
760e88b2 1529 ldisc_send(NULL, 0, 0);
32874aea 1530 if (pal)
1531 DeleteObject(pal);
1532 logpal = NULL;
1533 pal = NULL;
1534 cfgtopalette();
1535 init_palette();
1536
5a73255e 1537 /* Screen size changed ? */
1538 if (cfg.height != prev_cfg.height ||
1539 cfg.width != prev_cfg.width ||
1540 cfg.savelines != prev_cfg.savelines ||
ad5c93cc 1541 cfg.resize_action != RESIZE_TERM)
5a73255e 1542 term_size(cfg.height, cfg.width, cfg.savelines);
1543
32874aea 1544 /* Enable or disable the scroll bar, etc */
1545 {
1546 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1547 LONG nexflag, exflag =
1548 GetWindowLong(hwnd, GWL_EXSTYLE);
1549
1550 nexflag = exflag;
5a73255e 1551 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
32874aea 1552 if (cfg.alwaysontop) {
1553 nexflag |= WS_EX_TOPMOST;
1554 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1555 SWP_NOMOVE | SWP_NOSIZE);
1556 } else {
1557 nexflag &= ~(WS_EX_TOPMOST);
1558 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1559 SWP_NOMOVE | SWP_NOSIZE);
1560 }
1561 }
1562 if (cfg.sunken_edge)
1563 nexflag |= WS_EX_CLIENTEDGE;
1564 else
1565 nexflag &= ~(WS_EX_CLIENTEDGE);
1566
1567 nflg = flag;
1568 if (cfg.scrollbar)
1569 nflg |= WS_VSCROLL;
1570 else
1571 nflg &= ~WS_VSCROLL;
ad5c93cc 1572 if (cfg.resize_action == RESIZE_DISABLED)
32874aea 1573 nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
1574 else
1575 nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
1576
1577 if (nflg != flag || nexflag != exflag) {
32874aea 1578 if (nflg != flag)
1579 SetWindowLong(hwnd, GWL_STYLE, nflg);
1580 if (nexflag != exflag)
1581 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1582
32874aea 1583 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1584 SWP_NOACTIVATE | SWP_NOCOPYBITS |
5a73255e 1585 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1586 SWP_FRAMECHANGED);
c9def1b8 1587
5a73255e 1588 init_lvl = 2;
e44d78b6 1589 }
32874aea 1590 }
5a73255e 1591
e44d78b6 1592 /* Oops */
ad5c93cc 1593 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
e44d78b6 1594 force_normal(hwnd);
5a73255e 1595 init_lvl = 2;
1596 }
1597
32874aea 1598 set_title(cfg.wintitle);
1599 if (IsIconic(hwnd)) {
1600 SetWindowText(hwnd,
1601 cfg.win_name_always ? window_name :
1602 icon_name);
3da0b1d2 1603 }
5a73255e 1604
1605 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1606 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1607 cfg.fontisbold != prev_cfg.fontisbold ||
1608 cfg.fontheight != prev_cfg.fontheight ||
1609 cfg.fontcharset != prev_cfg.fontcharset ||
1610 cfg.vtmode != prev_cfg.vtmode ||
1611 cfg.bold_colour != prev_cfg.bold_colour ||
ad5c93cc 1612 (cfg.resize_action != RESIZE_FONT &&
1613 prev_cfg.resize_action == RESIZE_FONT))
5a73255e 1614 init_lvl = 2;
1615
1616 InvalidateRect(hwnd, NULL, TRUE);
1617 reset_window(init_lvl);
7732d38a 1618 net_pending_errors();
32874aea 1619 }
1620 break;
bc1235d4 1621 case IDM_COPYALL:
1622 term_copyall();
1623 break;
32874aea 1624 case IDM_CLRSB:
1625 term_clrsb();
1626 break;
1627 case IDM_RESET:
1628 term_pwron();
1629 break;
1630 case IDM_TEL_AYT:
1631 back->special(TS_AYT);
7732d38a 1632 net_pending_errors();
32874aea 1633 break;
1634 case IDM_TEL_BRK:
1635 back->special(TS_BRK);
7732d38a 1636 net_pending_errors();
32874aea 1637 break;
1638 case IDM_TEL_SYNCH:
1639 back->special(TS_SYNCH);
7732d38a 1640 net_pending_errors();
32874aea 1641 break;
1642 case IDM_TEL_EC:
1643 back->special(TS_EC);
7732d38a 1644 net_pending_errors();
32874aea 1645 break;
1646 case IDM_TEL_EL:
1647 back->special(TS_EL);
7732d38a 1648 net_pending_errors();
32874aea 1649 break;
1650 case IDM_TEL_GA:
1651 back->special(TS_GA);
7732d38a 1652 net_pending_errors();
32874aea 1653 break;
1654 case IDM_TEL_NOP:
1655 back->special(TS_NOP);
7732d38a 1656 net_pending_errors();
32874aea 1657 break;
1658 case IDM_TEL_ABORT:
1659 back->special(TS_ABORT);
7732d38a 1660 net_pending_errors();
32874aea 1661 break;
1662 case IDM_TEL_AO:
1663 back->special(TS_AO);
7732d38a 1664 net_pending_errors();
32874aea 1665 break;
1666 case IDM_TEL_IP:
1667 back->special(TS_IP);
7732d38a 1668 net_pending_errors();
32874aea 1669 break;
1670 case IDM_TEL_SUSP:
1671 back->special(TS_SUSP);
7732d38a 1672 net_pending_errors();
32874aea 1673 break;
1674 case IDM_TEL_EOR:
1675 back->special(TS_EOR);
7732d38a 1676 net_pending_errors();
32874aea 1677 break;
1678 case IDM_TEL_EOF:
1679 back->special(TS_EOF);
7732d38a 1680 net_pending_errors();
32874aea 1681 break;
374330e2 1682 case IDM_ABOUT:
32874aea 1683 showabout(hwnd);
374330e2 1684 break;
dfca2656 1685 case SC_KEYMENU:
1686 /*
1687 * We get this if the System menu has been activated.
1688 * This might happen from within TranslateKey, in which
1689 * case it really wants to be followed by a `space'
1690 * character to actually _bring the menu up_ rather
1691 * than just sitting there in `ready to appear' state.
1692 */
1693 if( lParam == 0 )
1694 PostMessage(hwnd, WM_CHAR, ' ', 0);
1695 break;
a401e5f3 1696 case IDM_FULLSCREEN:
1697 flip_full_screen();
1698 break;
32874aea 1699 default:
1700 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1701 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1702 }
374330e2 1703 }
1704 break;
37508af4 1705
1706#define X_POS(l) ((int)(short)LOWORD(l))
1707#define Y_POS(l) ((int)(short)HIWORD(l))
1708
5a73255e 1709#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1710#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
01c034ad 1711#define WHEEL_DELTA 120
1712 case WM_MOUSEWHEEL:
1713 {
32874aea 1714 wheel_accumulator += (short) HIWORD(wParam);
01c034ad 1715 wParam = LOWORD(wParam);
1716
1717 /* process events when the threshold is reached */
1718 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
1719 int b;
fdedf2c8 1720
01c034ad 1721 /* reduce amount for next time */
1722 if (wheel_accumulator > 0) {
1723 b = MBT_WHEEL_UP;
1724 wheel_accumulator -= WHEEL_DELTA;
32874aea 1725 } else if (wheel_accumulator < 0) {
01c034ad 1726 b = MBT_WHEEL_DOWN;
1727 wheel_accumulator += WHEEL_DELTA;
32874aea 1728 } else
01c034ad 1729 break;
1730
1731 if (send_raw_mouse) {
1732 /* send a mouse-down followed by a mouse up */
1733 term_mouse(b,
1734 MA_CLICK,
32874aea 1735 TO_CHR_X(X_POS(lParam)),
1736 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1737 wParam & MK_CONTROL);
1738 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
1739 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1740 wParam & MK_CONTROL);
01c034ad 1741 } else {
1742 /* trigger a scroll */
32874aea 1743 term_scroll(0,
1744 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
01c034ad 1745 }
1746 }
1747 return 0;
1748 }
374330e2 1749 case WM_LBUTTONDOWN:
374330e2 1750 case WM_MBUTTONDOWN:
374330e2 1751 case WM_RBUTTONDOWN:
01c034ad 1752 case WM_LBUTTONUP:
1753 case WM_MBUTTONUP:
374330e2 1754 case WM_RBUTTONUP:
01c034ad 1755 {
1756 int button, press;
1757 switch (message) {
32874aea 1758 case WM_LBUTTONDOWN:
1759 button = MBT_LEFT;
1760 press = 1;
1761 break;
1762 case WM_MBUTTONDOWN:
1763 button = MBT_MIDDLE;
1764 press = 1;
1765 break;
1766 case WM_RBUTTONDOWN:
1767 button = MBT_RIGHT;
1768 press = 1;
1769 break;
1770 case WM_LBUTTONUP:
1771 button = MBT_LEFT;
1772 press = 0;
1773 break;
1774 case WM_MBUTTONUP:
1775 button = MBT_MIDDLE;
1776 press = 0;
1777 break;
1778 case WM_RBUTTONUP:
1779 button = MBT_RIGHT;
1780 press = 0;
1781 break;
2d466ffd 1782 default:
1783 button = press = 0; /* shouldn't happen */
01c034ad 1784 }
1785 show_mouseptr(1);
1786 if (press) {
32874aea 1787 click(button,
1788 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
1789 wParam & MK_SHIFT, wParam & MK_CONTROL);
01c034ad 1790 SetCapture(hwnd);
1791 } else {
32874aea 1792 term_mouse(button, MA_RELEASE,
1793 TO_CHR_X(X_POS(lParam)),
1794 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1795 wParam & MK_CONTROL);
01c034ad 1796 ReleaseCapture();
1797 }
1798 }
374330e2 1799 return 0;
1800 case WM_MOUSEMOVE:
32874aea 1801 show_mouseptr(1);
374330e2 1802 /*
1803 * Add the mouse position and message time to the random
7d6ee6ff 1804 * number noise.
374330e2 1805 */
32874aea 1806 noise_ultralight(lParam);
374330e2 1807
1808 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
1809 Mouse_Button b;
1810 if (wParam & MK_LBUTTON)
6c6e7711 1811 b = MBT_LEFT;
374330e2 1812 else if (wParam & MK_MBUTTON)
6c6e7711 1813 b = MBT_MIDDLE;
374330e2 1814 else
6c6e7711 1815 b = MBT_RIGHT;
32874aea 1816 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1817 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1818 wParam & MK_CONTROL);
374330e2 1819 }
374330e2 1820 return 0;
d318ef8e 1821 case WM_NCMOUSEMOVE:
1822 show_mouseptr(1);
32874aea 1823 noise_ultralight(lParam);
d318ef8e 1824 return 0;
374330e2 1825 case WM_IGNORE_CLIP:
1826 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1827 break;
1828 case WM_DESTROYCLIPBOARD:
1829 if (!ignore_clip)
1830 term_deselect();
1831 ignore_clip = FALSE;
1832 return 0;
1833 case WM_PAINT:
1834 {
1835 PAINTSTRUCT p;
32874aea 1836 HideCaret(hwnd);
1837 hdc = BeginPaint(hwnd, &p);
374330e2 1838 if (pal) {
32874aea 1839 SelectPalette(hdc, pal, TRUE);
1840 RealizePalette(hdc);
374330e2 1841 }
5a73255e 1842 term_paint(hdc,
1843 (p.rcPaint.left-offset_width)/font_width,
1844 (p.rcPaint.top-offset_height)/font_height,
1845 (p.rcPaint.right-offset_width-1)/font_width,
1846 (p.rcPaint.bottom-offset_height-1)/font_height);
1847
1848 if (p.fErase ||
1849 p.rcPaint.left < offset_width ||
1850 p.rcPaint.top < offset_height ||
1851 p.rcPaint.right >= offset_width + font_width*cols ||
1852 p.rcPaint.bottom>= offset_height + font_height*rows)
1853 {
1854 HBRUSH fillcolour, oldbrush;
1855 HPEN edge, oldpen;
1856 fillcolour = CreateSolidBrush (
1857 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1858 oldbrush = SelectObject(hdc, fillcolour);
1859 edge = CreatePen(PS_SOLID, 0,
1860 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1861 oldpen = SelectObject(hdc, edge);
1862
1863 ExcludeClipRect(hdc,
1864 offset_width, offset_height,
1865 offset_width+font_width*cols,
1866 offset_height+font_height*rows);
1867
1868 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
1869 p.rcPaint.right, p.rcPaint.bottom);
1870
1871 // SelectClipRgn(hdc, NULL);
1872
1873 SelectObject(hdc, oldbrush);
1874 DeleteObject(fillcolour);
1875 SelectObject(hdc, oldpen);
1876 DeleteObject(edge);
1877 }
32874aea 1878 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
1879 SelectObject(hdc, GetStockObject(WHITE_PEN));
1880 EndPaint(hwnd, &p);
1881 ShowCaret(hwnd);
374330e2 1882 }
1883 return 0;
1884 case WM_NETEVENT:
59ad2c03 1885 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
1886 * but the only one that's likely to try to overload us is FD_READ.
1887 * This means buffering just one is fine.
1888 */
1889 if (pending_netevent)
1890 enact_pending_netevent();
1891
1892 pending_netevent = TRUE;
32874aea 1893 pend_netevent_wParam = wParam;
1894 pend_netevent_lParam = lParam;
3ad9d396 1895 if (WSAGETSELECTEVENT(lParam) != FD_READ)
1896 enact_pending_netevent();
1897
ec55b220 1898 time(&last_movement);
374330e2 1899 return 0;
1900 case WM_SETFOCUS:
1901 has_focus = TRUE;
32874aea 1902 CreateCaret(hwnd, caretbm, font_width, font_height);
1903 ShowCaret(hwnd);
f8a28d1f 1904 flash_window(0); /* stop */
32874aea 1905 compose_state = 0;
374330e2 1906 term_out();
1907 term_update();
1908 break;
1909 case WM_KILLFOCUS:
32874aea 1910 show_mouseptr(1);
374330e2 1911 has_focus = FALSE;
32874aea 1912 DestroyCaret();
374330e2 1913 term_out();
1914 term_update();
1915 break;
73251d5d 1916 case WM_ENTERSIZEMOVE:
5a73255e 1917#ifdef RDB_DEBUG_PATCH
1918 debug((27, "WM_ENTERSIZEMOVE"));
1919#endif
32874aea 1920 EnableSizeTip(1);
1921 resizing = TRUE;
3ad8c6db 1922 need_backend_resize = FALSE;
32874aea 1923 break;
73251d5d 1924 case WM_EXITSIZEMOVE:
32874aea 1925 EnableSizeTip(0);
1926 resizing = FALSE;
5a73255e 1927#ifdef RDB_DEBUG_PATCH
1928 debug((27, "WM_EXITSIZEMOVE"));
1929#endif
1930 if (need_backend_resize) {
1931 term_size(cfg.height, cfg.width, cfg.savelines);
1932 InvalidateRect(hwnd, NULL, TRUE);
1933 }
32874aea 1934 break;
374330e2 1935 case WM_SIZING:
5a73255e 1936 /*
1937 * This does two jobs:
1938 * 1) Keep the sizetip uptodate
1939 * 2) Make sure the window size is _stepped_ in units of the font size.
1940 */
ad5c93cc 1941 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
374330e2 1942 int width, height, w, h, ew, eh;
32874aea 1943 LPRECT r = (LPRECT) lParam;
374330e2 1944
5a73255e 1945 if ( !need_backend_resize &&
1946 (cfg.height != rows || cfg.width != cols )) {
1947 /*
1948 * Great! It seems the host has been changing the terminal
1949 * size, well the user is now grabbing so this is probably
1950 * the least confusing solution in the long run even though
1951 * it a is suprise. Unfortunatly the only way to prevent
1952 * this seems to be to let the host change the window size
1953 * and as that's a user option we're still right back here.
1954 */
1955 term_size(cfg.height, cfg.width, cfg.savelines);
1956 reset_window(2);
1957 InvalidateRect(hwnd, NULL, TRUE);
1958 need_backend_resize = TRUE;
1959 }
1960
374330e2 1961 width = r->right - r->left - extra_width;
1962 height = r->bottom - r->top - extra_height;
32874aea 1963 w = (width + font_width / 2) / font_width;
1964 if (w < 1)
1965 w = 1;
1966 h = (height + font_height / 2) / font_height;
1967 if (h < 1)
1968 h = 1;
1969 UpdateSizeTip(hwnd, w, h);
374330e2 1970 ew = width - w * font_width;
1971 eh = height - h * font_height;
1972 if (ew != 0) {
1973 if (wParam == WMSZ_LEFT ||
32874aea 1974 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
374330e2 1975 r->left += ew;
1976 else
1977 r->right -= ew;
1978 }
1979 if (eh != 0) {
1980 if (wParam == WMSZ_TOP ||
32874aea 1981 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
374330e2 1982 r->top += eh;
1983 else
1984 r->bottom -= eh;
1985 }
1986 if (ew || eh)
1987 return 1;
1988 else
1989 return 0;
5a73255e 1990 } else {
1991 int width, height, w, h, rv = 0;
1992 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
1993 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
1994 LPRECT r = (LPRECT) lParam;
1995
1996 width = r->right - r->left - ex_width;
1997 height = r->bottom - r->top - ex_height;
1998
1999 w = (width + cols/2)/cols;
2000 h = (height + rows/2)/rows;
2001 if ( r->right != r->left + w*cols + ex_width)
2002 rv = 1;
2003
2004 if (wParam == WMSZ_LEFT ||
2005 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2006 r->left = r->right - w*cols - ex_width;
2007 else
2008 r->right = r->left + w*cols + ex_width;
2009
2010 if (r->bottom != r->top + h*rows + ex_height)
2011 rv = 1;
2012
2013 if (wParam == WMSZ_TOP ||
2014 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2015 r->top = r->bottom - h*rows - ex_height;
2016 else
2017 r->bottom = r->top + h*rows + ex_height;
2018
2019 return rv;
374330e2 2020 }
32874aea 2021 /* break; (never reached) */
374330e2 2022 case WM_SIZE:
5a73255e 2023#ifdef RDB_DEBUG_PATCH
2024 debug((27, "WM_SIZE %s (%d,%d)",
2025 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2026 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2027 (wParam == SIZE_RESTORED && resizing) ? "to":
2028 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2029 "...",
2030 LOWORD(lParam), HIWORD(lParam)));
2031#endif
374330e2 2032 if (wParam == SIZE_MINIMIZED) {
32874aea 2033 SetWindowText(hwnd,
2034 cfg.win_name_always ? window_name : icon_name);
374330e2 2035 break;
2036 }
2037 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
32874aea 2038 SetWindowText(hwnd, window_name);
5a73255e 2039
ad5c93cc 2040 if (cfg.resize_action == RESIZE_DISABLED) {
5a73255e 2041 /* A resize, well it better be a minimize. */
2042 reset_window(-1);
2043 } else {
2044
1a6f78fe 2045 int width, height, w, h;
374330e2 2046
2047 width = LOWORD(lParam);
2048 height = HIWORD(lParam);
5a73255e 2049
2050 if (!resizing) {
2051 if (wParam == SIZE_MAXIMIZED) {
2052 was_zoomed = 1;
2053 prev_rows = rows;
2054 prev_cols = cols;
ad5c93cc 2055 if (cfg.resize_action != RESIZE_FONT) {
5a73255e 2056 w = width / font_width;
2057 if (w < 1) w = 1;
2058 h = height / font_height;
2059 if (h < 1) h = 1;
2060
2061 term_size(h, w, cfg.savelines);
2062 }
2063 reset_window(0);
2064 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2065 was_zoomed = 0;
ad5c93cc 2066 if (cfg.resize_action != RESIZE_FONT)
5a73255e 2067 term_size(prev_rows, prev_cols, cfg.savelines);
2068 reset_window(0);
a401e5f3 2069 } else if (was_full_screen) {
2070 was_full_screen = 0;
2071 if (cfg.resize_action != RESIZE_FONT)
2072 term_size(pre_fs_rows, pre_fs_cols, cfg.savelines);
2073 reset_window(0);
5a73255e 2074 }
2075 /* This is an unexpected resize, these will normally happen
2076 * if the window is too large. Probably either the user
2077 * selected a huge font or the screen size has changed.
2078 *
2079 * This is also called with minimize.
32874aea 2080 */
5a73255e 2081 else reset_window(-1);
2082 }
2083
2084 /*
2085 * Don't call back->size in mid-resize. (To prevent
2086 * massive numbers of resize events getting sent
2087 * down the connection during an NT opaque drag.)
2088 */
2089 if (resizing) {
ad5c93cc 2090 if (cfg.resize_action == RESIZE_TERM && !alt_pressed) {
3ad8c6db 2091 need_backend_resize = TRUE;
5a73255e 2092 w = (width-cfg.window_border*2) / font_width;
2093 if (w < 1) w = 1;
2094 h = (height-cfg.window_border*2) / font_height;
2095 if (h < 1) h = 1;
2096
e44d78b6 2097 cfg.height = h;
2098 cfg.width = w;
5a73255e 2099 } else
2100 reset_window(0);
374330e2 2101 }
2102 }
374330e2 2103 return 0;
2104 case WM_VSCROLL:
2105 switch (LOWORD(wParam)) {
32874aea 2106 case SB_BOTTOM:
2107 term_scroll(-1, 0);
2108 break;
2109 case SB_TOP:
2110 term_scroll(+1, 0);
2111 break;
2112 case SB_LINEDOWN:
2113 term_scroll(0, +1);
2114 break;
2115 case SB_LINEUP:
2116 term_scroll(0, -1);
2117 break;
2118 case SB_PAGEDOWN:
2119 term_scroll(0, +rows / 2);
2120 break;
2121 case SB_PAGEUP:
2122 term_scroll(0, -rows / 2);
2123 break;
2124 case SB_THUMBPOSITION:
2125 case SB_THUMBTRACK:
2126 term_scroll(1, HIWORD(wParam));
2127 break;
2128 }
2129 break;
2130 case WM_PALETTECHANGED:
374330e2 2131 if ((HWND) wParam != hwnd && pal != NULL) {
2132 HDC hdc = get_ctx();
2133 if (hdc) {
32874aea 2134 if (RealizePalette(hdc) > 0)
2135 UpdateColors(hdc);
2136 free_ctx(hdc);
374330e2 2137 }
2138 }
2139 break;
2140 case WM_QUERYNEWPALETTE:
2141 if (pal != NULL) {
2142 HDC hdc = get_ctx();
2143 if (hdc) {
32874aea 2144 if (RealizePalette(hdc) > 0)
2145 UpdateColors(hdc);
2146 free_ctx(hdc);
374330e2 2147 return TRUE;
2148 }
2149 }
2150 return FALSE;
2151 case WM_KEYDOWN:
2152 case WM_SYSKEYDOWN:
c9def1b8 2153 case WM_KEYUP:
2154 case WM_SYSKEYUP:
374330e2 2155 /*
2156 * Add the scan code and keypress timing to the random
7d6ee6ff 2157 * number noise.
374330e2 2158 */
32874aea 2159 noise_ultralight(lParam);
374330e2 2160
2161 /*
2162 * We don't do TranslateMessage since it disassociates the
2163 * resulting CHAR message from the KEYDOWN that sparked it,
2164 * which we occasionally don't want. Instead, we process
2165 * KEYDOWN, and call the Win32 translator functions so that
2166 * we get the translations under _our_ control.
2167 */
2168 {
2169 unsigned char buf[20];
2170 int len;
2171
32874aea 2172 if (wParam == VK_PROCESSKEY) {
3cf144db 2173 MSG m;
32874aea 2174 m.hwnd = hwnd;
2175 m.message = WM_KEYDOWN;
2176 m.wParam = wParam;
2177 m.lParam = lParam & 0xdfff;
2178 TranslateMessage(&m);
2179 } else {
2180 len = TranslateKey(message, wParam, lParam, buf);
3cf144db 2181 if (len == -1)
32874aea 2182 return DefWindowProc(hwnd, message, wParam, lParam);
5471d09a 2183
b2a1eade 2184 if (len != 0) {
bca9517a 2185 /*
256cb87c 2186 * Interrupt an ongoing paste. I'm not sure
2187 * this is sensible, but for the moment it's
2188 * preferable to having to faff about buffering
2189 * things.
2190 */
2191 term_nopaste();
2192
2193 /*
bca9517a 2194 * We need not bother about stdin backlogs
2195 * here, because in GUI PuTTY we can't do
2196 * anything about it anyway; there's no means
2197 * of asking Windows to hold off on KEYDOWN
2198 * messages. We _have_ to buffer everything
2199 * we're sent.
2200 */
760e88b2 2201 ldisc_send(buf, len, 1);
32874aea 2202 show_mouseptr(0);
bca9517a 2203 }
3cf144db 2204 }
374330e2 2205 }
7732d38a 2206 net_pending_errors();
374330e2 2207 return 0;
4eeb7d09 2208 case WM_INPUTLANGCHANGE:
3cf144db 2209 {
4eeb7d09 2210 /* wParam == Font number */
2211 /* lParam == Locale */
2212 char lbuf[20];
2213 HKL NewInputLocale = (HKL) lParam;
2214
2215 // lParam == GetKeyboardLayout(0);
2216
2217 GetLocaleInfo(LOWORD(NewInputLocale),
2218 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2219
2220 kbd_codepage = atoi(lbuf);
2221 }
2222 break;
88485e4d 2223 case WM_IME_COMPOSITION:
2224 {
2225 HIMC hIMC;
2226 int n;
2227 char *buff;
2228
2229 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2230 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2231
2232 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2233 break; /* fall back to DefWindowProc */
2234
2235 hIMC = ImmGetContext(hwnd);
2236 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2237
2238 if (n > 0) {
2239 buff = (char*) smalloc(n);
2240 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
760e88b2 2241 luni_send((unsigned short *)buff, n / 2, 1);
88485e4d 2242 free(buff);
2243 }
2244 ImmReleaseContext(hwnd, hIMC);
2245 return 1;
2246 }
2247
4eeb7d09 2248 case WM_IME_CHAR:
2249 if (wParam & 0xFF00) {
3cf144db 2250 unsigned char buf[2];
2251
2252 buf[1] = wParam;
2253 buf[0] = wParam >> 8;
760e88b2 2254 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2255 } else {
2256 char c = (unsigned char) wParam;
760e88b2 2257 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2258 }
4eeb7d09 2259 return (0);
374330e2 2260 case WM_CHAR:
2261 case WM_SYSCHAR:
2262 /*
2263 * Nevertheless, we are prepared to deal with WM_CHAR
2264 * messages, should they crop up. So if someone wants to
2265 * post the things to us as part of a macro manoeuvre,
2266 * we're ready to cope.
2267 */
32874aea 2268 {
4eeb7d09 2269 char c = (unsigned char)wParam;
760e88b2 2270 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2271 }
2272 return 0;
32874aea 2273 case WM_SETCURSOR:
ca3ab3ee 2274 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2275 SetCursor(LoadCursor(NULL, IDC_ARROW));
2276 return TRUE;
2277 }
374330e2 2278 }
2279
32874aea 2280 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2281}
2282
2283/*
ec8679e9 2284 * Move the system caret. (We maintain one, even though it's
2285 * invisible, for the benefit of blind people: apparently some
2286 * helper software tracks the system caret, so we should arrange to
2287 * have one.)
2288 */
32874aea 2289void sys_cursor(int x, int y)
2290{
88485e4d 2291 COMPOSITIONFORM cf;
2292 HIMC hIMC;
2293
2294 if (!has_focus) return;
2295
5a73255e 2296 SetCaretPos(x * font_width + offset_width,
2297 y * font_height + offset_height);
88485e4d 2298
2299 /* IMM calls on Win98 and beyond only */
2300 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2301
2302 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2303 osVersion.dwMinorVersion == 0) return; /* 95 */
2304
2305 /* we should have the IMM functions */
2306 hIMC = ImmGetContext(hwnd);
2307 cf.dwStyle = CFS_POINT;
2308 cf.ptCurrentPos.x = x * font_width;
2309 cf.ptCurrentPos.y = y * font_height;
2310 ImmSetCompositionWindow(hIMC, &cf);
2311
2312 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2313}
2314
2315/*
374330e2 2316 * Draw a line of text in the window, at given character
2317 * coordinates, in given attributes.
2318 *
2319 * We are allowed to fiddle with the contents of `text'.
2320 */
32874aea 2321void do_text(Context ctx, int x, int y, char *text, int len,
2322 unsigned long attr, int lattr)
2323{
374330e2 2324 COLORREF fg, bg, t;
2325 int nfg, nbg, nfont;
2326 HDC hdc = ctx;
59ad2c03 2327 RECT line_box;
2328 int force_manual_underline = 0;
32874aea 2329 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2330 int char_width = fnt_width;
2331 int text_adjust = 0;
2332 static int *IpDx = 0, IpDxLEN = 0;
2333
2334 if (attr & ATTR_WIDE)
2335 char_width *= 2;
59ad2c03 2336
4eeb7d09 2337 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2338 int i;
32874aea 2339 if (len > IpDxLEN) {
59ad2c03 2340 sfree(IpDx);
32874aea 2341 IpDx = smalloc((len + 16) * sizeof(int));
2342 IpDxLEN = (len + 16);
59ad2c03 2343 }
32874aea 2344 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2345 IpDx[i] = char_width;
59ad2c03 2346 }
374330e2 2347
5a73255e 2348 /* Only want the left half of double width lines */
2349 if (lattr != LATTR_NORM && x*2 >= cols)
2350 return;
2351
c9def1b8 2352 x *= fnt_width;
374330e2 2353 y *= font_height;
5a73255e 2354 x += offset_width;
2355 y += offset_height;
374330e2 2356
4eeb7d09 2357 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2358 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2359 attr ^= ATTR_CUR_XOR;
2360 }
2361
2362 nfont = 0;
4eeb7d09 2363 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2364 /* Assume a poorman font is borken in other ways too. */
2365 lattr = LATTR_WIDE;
2366 } else
2367 switch (lattr) {
2368 case LATTR_NORM:
2369 break;
2370 case LATTR_WIDE:
2371 nfont |= FONT_WIDE;
374330e2 2372 break;
4eeb7d09 2373 default:
2374 nfont |= FONT_WIDE + FONT_HIGH;
2375 break;
2376 }
5a73255e 2377 if (attr & ATTR_NARROW)
2378 nfont |= FONT_NARROW;
4eeb7d09 2379
2380 /* Special hack for the VT100 linedraw glyphs. */
2381 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2382 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2383 switch ((unsigned char) (text[0])) {
2384 case 0xBA:
2385 text_adjust = -2 * font_height / 5;
2386 break;
2387 case 0xBB:
2388 text_adjust = -1 * font_height / 5;
2389 break;
2390 case 0xBC:
2391 text_adjust = font_height / 5;
2392 break;
2393 case 0xBD:
2394 text_adjust = 2 * font_height / 5;
32874aea 2395 break;
59ad2c03 2396 }
4eeb7d09 2397 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2398 text_adjust *= 2;
2399 attr &= ~CSET_MASK;
2400 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2401 attr |= (unitab_xterm['q'] & CSET_MASK);
2402 if (attr & ATTR_UNDER) {
2403 attr &= ~ATTR_UNDER;
2404 force_manual_underline = 1;
2405 }
374330e2 2406 }
2407 }
2408
4eeb7d09 2409 /* Anything left as an original character set is unprintable. */
2410 if (DIRECT_CHAR(attr)) {
2411 attr &= ~CSET_MASK;
2412 attr |= 0xFF00;
5a73255e 2413 memset(text, 0xFD, len);
4eeb7d09 2414 }
2415
2416 /* OEM CP */
2417 if ((attr & CSET_MASK) == ATTR_OEMCP)
2418 nfont |= FONT_OEM;
2419
374330e2 2420 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2421 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2422 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2423 nfont |= FONT_BOLD;
2424 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2425 nfont |= FONT_UNDERLINE;
4eeb7d09 2426 another_font(nfont);
32874aea 2427 if (!fonts[nfont]) {
2428 if (nfont & FONT_UNDERLINE)
59ad2c03 2429 force_manual_underline = 1;
2430 /* Don't do the same for manual bold, it could be bad news. */
2431
32874aea 2432 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2433 }
4eeb7d09 2434 another_font(nfont);
2435 if (!fonts[nfont])
2436 nfont = FONT_NORMAL;
374330e2 2437 if (attr & ATTR_REVERSE) {
32874aea 2438 t = nfg;
2439 nfg = nbg;
2440 nbg = t;
374330e2 2441 }
2442 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2443 nfg++;
59ad2c03 2444 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2445 nbg++;
374330e2 2446 fg = colours[nfg];
2447 bg = colours[nbg];
32874aea 2448 SelectObject(hdc, fonts[nfont]);
2449 SetTextColor(hdc, fg);
2450 SetBkColor(hdc, bg);
2451 SetBkMode(hdc, OPAQUE);
2452 line_box.left = x;
2453 line_box.top = y;
4eeb7d09 2454 line_box.right = x + char_width * len;
32874aea 2455 line_box.bottom = y + font_height;
4eeb7d09 2456
5a73255e 2457 /* Only want the left half of double width lines */
2458 if (line_box.right > font_width*cols+offset_width)
2459 line_box.right = font_width*cols+offset_width;
2460
4eeb7d09 2461 /* We're using a private area for direct to font. (512 chars.) */
2462 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2463 /* Ho Hum, dbcs fonts are a PITA! */
2464 /* To display on W9x I have to convert to UCS */
2465 static wchar_t *uni_buf = 0;
2466 static int uni_len = 0;
5a73255e 2467 int nlen, mptr;
4eeb7d09 2468 if (len > uni_len) {
2469 sfree(uni_buf);
2470 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2471 }
4eeb7d09 2472
5a73255e 2473 for(nlen = mptr = 0; mptr<len; mptr++) {
2474 uni_buf[nlen] = 0xFFFD;
2475 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2476 IpDx[nlen] += char_width;
2477 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2478 text+mptr, 2, uni_buf+nlen, 1);
2479 mptr++;
2480 }
2481 else
2482 {
2483 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2484 text+mptr, 1, uni_buf+nlen, 1);
2485 }
2486 nlen++;
2487 }
4eeb7d09 2488 if (nlen <= 0)
2489 return; /* Eeek! */
2490
2491 ExtTextOutW(hdc, x,
2492 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2493 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2494 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2495 SetBkMode(hdc, TRANSPARENT);
2496 ExtTextOutW(hdc, x - 1,
2497 y - font_height * (lattr ==
2498 LATTR_BOT) + text_adjust,
5a73255e 2499 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2500 }
5a73255e 2501
2502 IpDx[0] = -1;
4eeb7d09 2503 } else if (DIRECT_FONT(attr)) {
2504 ExtTextOut(hdc, x,
2505 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2506 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2507 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2508 SetBkMode(hdc, TRANSPARENT);
2509
2510 /* GRR: This draws the character outside it's box and can leave
2511 * 'droppings' even with the clip box! I suppose I could loop it
2512 * one character at a time ... yuk.
2513 *
2514 * Or ... I could do a test print with "W", and use +1 or -1 for this
2515 * shift depending on if the leftmost column is blank...
2516 */
2517 ExtTextOut(hdc, x - 1,
2518 y - font_height * (lattr ==
2519 LATTR_BOT) + text_adjust,
2520 ETO_CLIPPED, &line_box, text, len, IpDx);
2521 }
2522 } else {
2523 /* And 'normal' unicode characters */
2524 static WCHAR *wbuf = NULL;
2525 static int wlen = 0;
2526 int i;
2527 if (wlen < len) {
2528 sfree(wbuf);
2529 wlen = len;
2530 wbuf = smalloc(wlen * sizeof(WCHAR));
2531 }
2532 for (i = 0; i < len; i++)
2533 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2534
2535 ExtTextOutW(hdc, x,
2536 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2537 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2538
2539 /* And the shadow bold hack. */
5a73255e 2540 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2541 SetBkMode(hdc, TRANSPARENT);
2542 ExtTextOutW(hdc, x - 1,
2543 y - font_height * (lattr ==
2544 LATTR_BOT) + text_adjust,
2545 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2546 }
374330e2 2547 }
4eeb7d09 2548 if (lattr != LATTR_TOP && (force_manual_underline ||
2549 (und_mode == UND_LINE
2550 && (attr & ATTR_UNDER)))) {
32874aea 2551 HPEN oldpen;
4eeb7d09 2552 int dec = descent;
2553 if (lattr == LATTR_BOT)
2554 dec = dec * 2 - font_height;
2555
32874aea 2556 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2557 MoveToEx(hdc, x, y + dec, NULL);
2558 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2559 oldpen = SelectObject(hdc, oldpen);
2560 DeleteObject(oldpen);
374330e2 2561 }
4eeb7d09 2562}
2563
2564void do_cursor(Context ctx, int x, int y, char *text, int len,
2565 unsigned long attr, int lattr)
2566{
2567
2568 int fnt_width;
2569 int char_width;
2570 HDC hdc = ctx;
2571 int ctype = cfg.cursor_type;
2572
2573 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2574 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2575 do_text(ctx, x, y, text, len, attr, lattr);
2576 return;
2577 }
2578 ctype = 2;
2579 attr |= TATTR_RIGHTCURS;
2580 }
2581
2582 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2583 if (attr & ATTR_WIDE)
2584 char_width *= 2;
2585 x *= fnt_width;
2586 y *= font_height;
5a73255e 2587 x += offset_width;
2588 y += offset_height;
4eeb7d09 2589
2590 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2591 POINT pts[5];
32874aea 2592 HPEN oldpen;
374330e2 2593 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2594 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2595 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2596 pts[1].y = pts[2].y = y + font_height - 1;
2597 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2598 Polyline(hdc, pts, 5);
2599 oldpen = SelectObject(hdc, oldpen);
2600 DeleteObject(oldpen);
4eeb7d09 2601 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2602 int startx, starty, dx, dy, length, i;
4eeb7d09 2603 if (ctype == 1) {
32874aea 2604 startx = x;
2605 starty = y + descent;
2606 dx = 1;
2607 dy = 0;
4eeb7d09 2608 length = char_width;
32874aea 2609 } else {
4e30ff69 2610 int xadjust = 0;
4eeb7d09 2611 if (attr & TATTR_RIGHTCURS)
2612 xadjust = char_width - 1;
32874aea 2613 startx = x + xadjust;
2614 starty = y;
2615 dx = 0;
2616 dy = 1;
2617 length = font_height;
2618 }
4eeb7d09 2619 if (attr & TATTR_ACTCURS) {
32874aea 2620 HPEN oldpen;
2621 oldpen =
2622 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2623 MoveToEx(hdc, startx, starty, NULL);
2624 LineTo(hdc, startx + dx * length, starty + dy * length);
2625 oldpen = SelectObject(hdc, oldpen);
2626 DeleteObject(oldpen);
2627 } else {
2628 for (i = 0; i < length; i++) {
2629 if (i % 2 == 0) {
2630 SetPixel(hdc, startx, starty, colours[23]);
2631 }
2632 startx += dx;
2633 starty += dy;
2634 }
2635 }
4e30ff69 2636 }
374330e2 2637}
2638
5a73255e 2639/* This function gets the actual width of a character in the normal font.
2640 */
2641int CharWidth(Context ctx, int uc) {
2642 HDC hdc = ctx;
2643 int ibuf = 0;
2644
2645 /* If the font max is the same as the font ave width then this
2646 * function is a no-op.
2647 */
2648 if (!font_dualwidth) return 1;
2649
2650 switch (uc & CSET_MASK) {
2651 case ATTR_ASCII:
2652 uc = unitab_line[uc & 0xFF];
2653 break;
2654 case ATTR_LINEDRW:
2655 uc = unitab_xterm[uc & 0xFF];
2656 break;
2657 case ATTR_SCOACS:
2658 uc = unitab_scoacs[uc & 0xFF];
2659 break;
2660 }
2661 if (DIRECT_FONT(uc)) {
2662 if (dbcs_screenfont) return 1;
2663
2664 /* Speedup, I know of no font where ascii is the wrong width */
2665 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2666 return 1;
2667
2668 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2669 SelectObject(hdc, fonts[FONT_NORMAL]);
2670 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2671 another_font(FONT_OEM);
2672 if (!fonts[FONT_OEM]) return 0;
2673
2674 SelectObject(hdc, fonts[FONT_OEM]);
2675 } else
2676 return 0;
2677
2678 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2679 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2680 return 0;
2681 } else {
2682 /* Speedup, I know of no font where ascii is the wrong width */
2683 if (uc >= ' ' && uc <= '~') return 1;
2684
2685 SelectObject(hdc, fonts[FONT_NORMAL]);
2686 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2687 /* Okay that one worked */ ;
2688 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2689 /* This should work on 9x too, but it's "less accurate" */ ;
2690 else
2691 return 0;
2692 }
2693
2694 ibuf += font_width / 2 -1;
2695 ibuf /= font_width;
2696
2697 return ibuf;
2698}
2699
374330e2 2700/*
c9def1b8 2701 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2702 * codes. Returns number of bytes used or zero to drop the message
2703 * or -1 to forward the message to windows.
374330e2 2704 */
3cf144db 2705static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2706 unsigned char *output)
2707{
374330e2 2708 BYTE keystate[256];
32874aea 2709 int scan, left_alt = 0, key_down, shift_state;
2710 int r, i, code;
2711 unsigned char *p = output;
4eeb7d09 2712 static int alt_sum = 0;
374330e2 2713
00e3ba0f 2714 HKL kbd_layout = GetKeyboardLayout(0);
2715
0c50ef57 2716 static WORD keys[3];
0c50ef57 2717 static int compose_char = 0;
2718 static WPARAM compose_key = 0;
32874aea 2719
c9def1b8 2720 r = GetKeyboardState(keystate);
32874aea 2721 if (!r)
2722 memset(keystate, 0, sizeof(keystate));
2723 else {
ec55b220 2724#if 0
4eeb7d09 2725#define SHOW_TOASCII_RESULT
32874aea 2726 { /* Tell us all about key events */
2727 static BYTE oldstate[256];
2728 static int first = 1;
2729 static int scan;
2730 int ch;
2731 if (first)
2732 memcpy(oldstate, keystate, sizeof(oldstate));
2733 first = 0;
2734
2735 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2736 debug(("+"));
2737 } else if ((HIWORD(lParam) & KF_UP)
2738 && scan == (HIWORD(lParam) & 0xFF)) {
2739 debug((". U"));
2740 } else {
2741 debug((".\n"));
2742 if (wParam >= VK_F1 && wParam <= VK_F20)
2743 debug(("K_F%d", wParam + 1 - VK_F1));
2744 else
2745 switch (wParam) {
2746 case VK_SHIFT:
2747 debug(("SHIFT"));
2748 break;
2749 case VK_CONTROL:
2750 debug(("CTRL"));
2751 break;
2752 case VK_MENU:
2753 debug(("ALT"));
2754 break;
2755 default:
2756 debug(("VK_%02x", wParam));
2757 }
2758 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2759 debug(("*"));
2760 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2761
2762 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2763 if (ch >= ' ' && ch <= '~')
2764 debug((", '%c'", ch));
2765 else if (ch)
2766 debug((", $%02x", ch));
2767
2768 if (keys[0])
2769 debug((", KB0=%02x", keys[0]));
2770 if (keys[1])
2771 debug((", KB1=%02x", keys[1]));
2772 if (keys[2])
2773 debug((", KB2=%02x", keys[2]));
2774
2775 if ((keystate[VK_SHIFT] & 0x80) != 0)
2776 debug((", S"));
2777 if ((keystate[VK_CONTROL] & 0x80) != 0)
2778 debug((", C"));
2779 if ((HIWORD(lParam) & KF_EXTENDED))
2780 debug((", E"));
2781 if ((HIWORD(lParam) & KF_UP))
2782 debug((", U"));
2783 }
2784
2785 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2786 else if ((HIWORD(lParam) & KF_UP))
2787 oldstate[wParam & 0xFF] ^= 0x80;
2788 else
2789 oldstate[wParam & 0xFF] ^= 0x81;
2790
2791 for (ch = 0; ch < 256; ch++)
2792 if (oldstate[ch] != keystate[ch])
2793 debug((", M%02x=%02x", ch, keystate[ch]));
2794
2795 memcpy(oldstate, keystate, sizeof(oldstate));
2796 }
ec55b220 2797#endif
2798
32874aea 2799 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2800 keystate[VK_RMENU] = keystate[VK_MENU];
2801 }
2802
c9def1b8 2803
2804 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2805 if ((cfg.funky_type == 3 ||
2806 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2807 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2808
2809 wParam = VK_EXECUTE;
2810
2811 /* UnToggle NUMLock */
32874aea 2812 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2813 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2814 }
2815
2816 /* And write back the 'adjusted' state */
32874aea 2817 SetKeyboardState(keystate);
c9def1b8 2818 }
2819
2820 /* Disable Auto repeat if required */
32874aea 2821 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2822 return 0;
c9def1b8 2823
32874aea 2824 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2825 left_alt = 1;
2826
32874aea 2827 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2828
95bbe1ae 2829 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2830 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2831 if (cfg.ctrlaltkeys)
2832 keystate[VK_MENU] = 0;
2833 else {
2834 keystate[VK_RMENU] = 0x80;
2835 left_alt = 0;
2836 }
2837 }
c9def1b8 2838
5a73255e 2839 alt_pressed = (left_alt && key_down);
2840
c9def1b8 2841 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2842 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2843 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2844
95bbe1ae 2845 /* Note if AltGr was pressed and if it was used as a compose key */
2846 if (!compose_state) {
159eba53 2847 compose_key = 0x100;
95bbe1ae 2848 if (cfg.compose_key) {
32874aea 2849 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2850 compose_key = wParam;
2851 }
2852 if (wParam == VK_APPS)
2853 compose_key = wParam;
2854 }
2855
32874aea 2856 if (wParam == compose_key) {
2857 if (compose_state == 0
2858 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2859 1;
2860 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2861 compose_state = 2;
2862 else
2863 compose_state = 0;
32874aea 2864 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2865 compose_state = 0;
2866
67c339f7 2867 /*
cabfd08c 2868 * Record that we pressed key so the scroll window can be reset, but
2869 * be careful to avoid Shift-UP/Down
2870 */
32874aea 2871 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2872 seen_key_event = 1;
cabfd08c 2873 }
2874
32874aea 2875 if (compose_state > 1 && left_alt)
2876 compose_state = 0;
67c339f7 2877
c9def1b8 2878 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2879 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2880 && cfg.funky_type != 2)
2881 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2882 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2883 int nParam = 0;
32874aea 2884 switch (wParam) {
2885 case VK_INSERT:
2886 nParam = VK_NUMPAD0;
2887 break;
2888 case VK_END:
2889 nParam = VK_NUMPAD1;
2890 break;
2891 case VK_DOWN:
2892 nParam = VK_NUMPAD2;
2893 break;
2894 case VK_NEXT:
2895 nParam = VK_NUMPAD3;
2896 break;
2897 case VK_LEFT:
2898 nParam = VK_NUMPAD4;
2899 break;
2900 case VK_CLEAR:
2901 nParam = VK_NUMPAD5;
2902 break;
2903 case VK_RIGHT:
2904 nParam = VK_NUMPAD6;
2905 break;
2906 case VK_HOME:
2907 nParam = VK_NUMPAD7;
2908 break;
2909 case VK_UP:
2910 nParam = VK_NUMPAD8;
2911 break;
2912 case VK_PRIOR:
2913 nParam = VK_NUMPAD9;
2914 break;
2915 case VK_DELETE:
2916 nParam = VK_DECIMAL;
2917 break;
c9def1b8 2918 }
32874aea 2919 if (nParam) {
2920 if (keystate[VK_NUMLOCK] & 1)
2921 shift_state |= 1;
c9def1b8 2922 wParam = nParam;
2923 }
25d39ef6 2924 }
2925 }
2926
c9def1b8 2927 /* If a key is pressed and AltGr is not active */
32874aea 2928 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2929 /* Okay, prepare for most alts then ... */
2930 if (left_alt)
2931 *p++ = '\033';
374330e2 2932
c9def1b8 2933 /* Lets see if it's a pattern we know all about ... */
2934 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2935 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2936 return 0;
c9def1b8 2937 }
2938 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2939 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2940 return 0;
2941 }
2942 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 2943 term_do_paste();
32874aea 2944 return 0;
2945 }
c9def1b8 2946 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 2947 return -1;
c9def1b8 2948 }
2949 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 2950 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2951 return -1;
c9def1b8 2952 }
8f57d753 2953 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2954 flip_full_screen();
2955 return -1;
2956 }
ec55b220 2957 /* Control-Numlock for app-keypad mode switch */
2958 if (wParam == VK_PAUSE && shift_state == 2) {
2959 app_keypad_keys ^= 1;
2960 return 0;
2961 }
374330e2 2962
c9def1b8 2963 /* Nethack keypad */
2964 if (cfg.nethack_keypad && !left_alt) {
32874aea 2965 switch (wParam) {
2966 case VK_NUMPAD1:
2967 *p++ = shift_state ? 'B' : 'b';
2968 return p - output;
2969 case VK_NUMPAD2:
2970 *p++ = shift_state ? 'J' : 'j';
2971 return p - output;
2972 case VK_NUMPAD3:
2973 *p++ = shift_state ? 'N' : 'n';
2974 return p - output;
2975 case VK_NUMPAD4:
2976 *p++ = shift_state ? 'H' : 'h';
2977 return p - output;
2978 case VK_NUMPAD5:
2979 *p++ = shift_state ? '.' : '.';
2980 return p - output;
2981 case VK_NUMPAD6:
2982 *p++ = shift_state ? 'L' : 'l';
2983 return p - output;
2984 case VK_NUMPAD7:
2985 *p++ = shift_state ? 'Y' : 'y';
2986 return p - output;
2987 case VK_NUMPAD8:
2988 *p++ = shift_state ? 'K' : 'k';
2989 return p - output;
2990 case VK_NUMPAD9:
2991 *p++ = shift_state ? 'U' : 'u';
2992 return p - output;
2993 }
c9def1b8 2994 }
2995
2996 /* Application Keypad */
2997 if (!left_alt) {
32874aea 2998 int xkey = 0;
2999
3000 if (cfg.funky_type == 3 ||
3001 (cfg.funky_type <= 1 &&
3002 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3003 case VK_EXECUTE:
3004 xkey = 'P';
3005 break;
3006 case VK_DIVIDE:
3007 xkey = 'Q';
3008 break;
3009 case VK_MULTIPLY:
3010 xkey = 'R';
3011 break;
3012 case VK_SUBTRACT:
3013 xkey = 'S';
3014 break;
3015 }
3016 if (app_keypad_keys && !cfg.no_applic_k)
3017 switch (wParam) {
3018 case VK_NUMPAD0:
3019 xkey = 'p';
3020 break;
3021 case VK_NUMPAD1:
3022 xkey = 'q';
3023 break;
3024 case VK_NUMPAD2:
3025 xkey = 'r';
3026 break;
3027 case VK_NUMPAD3:
3028 xkey = 's';
3029 break;
3030 case VK_NUMPAD4:
3031 xkey = 't';
3032 break;
3033 case VK_NUMPAD5:
3034 xkey = 'u';
3035 break;
3036 case VK_NUMPAD6:
3037 xkey = 'v';
3038 break;
3039 case VK_NUMPAD7:
3040 xkey = 'w';
3041 break;
3042 case VK_NUMPAD8:
3043 xkey = 'x';
3044 break;
3045 case VK_NUMPAD9:
3046 xkey = 'y';
3047 break;
3048
3049 case VK_DECIMAL:
3050 xkey = 'n';
3051 break;
3052 case VK_ADD:
3053 if (cfg.funky_type == 2) {
3054 if (shift_state)
3055 xkey = 'l';
3056 else
3057 xkey = 'k';
3058 } else if (shift_state)
3059 xkey = 'm';
c9def1b8 3060 else
32874aea 3061 xkey = 'l';
3062 break;
3063
3064 case VK_DIVIDE:
3065 if (cfg.funky_type == 2)
3066 xkey = 'o';
3067 break;
3068 case VK_MULTIPLY:
3069 if (cfg.funky_type == 2)
3070 xkey = 'j';
3071 break;
3072 case VK_SUBTRACT:
3073 if (cfg.funky_type == 2)
3074 xkey = 'm';
3075 break;
3076
3077 case VK_RETURN:
3078 if (HIWORD(lParam) & KF_EXTENDED)
3079 xkey = 'M';
3080 break;
c9def1b8 3081 }
32874aea 3082 if (xkey) {
3083 if (vt52_mode) {
3084 if (xkey >= 'P' && xkey <= 'S')
3085 p += sprintf((char *) p, "\x1B%c", xkey);
3086 else
3087 p += sprintf((char *) p, "\x1B?%c", xkey);
3088 } else
3089 p += sprintf((char *) p, "\x1BO%c", xkey);
3090 return p - output;
c9def1b8 3091 }
3092 }
3093
32874aea 3094 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3095 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3096 *p++ = 0;
3097 return -2;
c9def1b8 3098 }
32874aea 3099 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3100 *p++ = 0x1B;
3101 *p++ = '[';
3102 *p++ = 'Z';
3103 return p - output;
c9def1b8 3104 }
32874aea 3105 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3106 *p++ = 0;
3107 return p - output;
c9def1b8 3108 }
32874aea 3109 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3110 *p++ = 160;
3111 return p - output;
c9def1b8 3112 }
32874aea 3113 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3114 *p++ = 3;
a5f3e637 3115 *p++ = 0;
3116 return -2;
c9def1b8 3117 }
32874aea 3118 if (wParam == VK_PAUSE) { /* Break/Pause */
3119 *p++ = 26;
3120 *p++ = 0;
3121 return -2;
95bbe1ae 3122 }
c9def1b8 3123 /* Control-2 to Control-8 are special */
32874aea 3124 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3125 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3126 return p - output;
3127 }
3128 if (shift_state == 2 && wParam == 0xBD) {
3129 *p++ = 0x1F;
3130 return p - output;
3131 }
3132 if (shift_state == 2 && wParam == 0xDF) {
3133 *p++ = 0x1C;
3134 return p - output;
3135 }
3136 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3137 *p++ = '\r';
3138 *p++ = '\n';
c9def1b8 3139 return p - output;
3140 }
374330e2 3141
c5e9c988 3142 /*
c9def1b8 3143 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3144 * for integer decimal nn.)
3145 *
3146 * We also deal with the weird ones here. Linux VCs replace F1
3147 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3148 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3149 * respectively.
c5e9c988 3150 */
c9def1b8 3151 code = 0;
3152 switch (wParam) {
32874aea 3153 case VK_F1:
3154 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3155 break;
3156 case VK_F2:
3157 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3158 break;
3159 case VK_F3:
3160 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3161 break;
3162 case VK_F4:
3163 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3164 break;
3165 case VK_F5:
3166 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3167 break;
3168 case VK_F6:
3169 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3170 break;
3171 case VK_F7:
3172 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3173 break;
3174 case VK_F8:
3175 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3176 break;
3177 case VK_F9:
3178 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3179 break;
3180 case VK_F10:
3181 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3182 break;
3183 case VK_F11:
3184 code = 23;
3185 break;
3186 case VK_F12:
3187 code = 24;
3188 break;
3189 case VK_F13:
3190 code = 25;
3191 break;
3192 case VK_F14:
3193 code = 26;
3194 break;
3195 case VK_F15:
3196 code = 28;
3197 break;
3198 case VK_F16:
3199 code = 29;
3200 break;
3201 case VK_F17:
3202 code = 31;
3203 break;
3204 case VK_F18:
3205 code = 32;
3206 break;
3207 case VK_F19:
3208 code = 33;
3209 break;
3210 case VK_F20:
3211 code = 34;
3212 break;
dfca2656 3213 }
3214 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3215 case VK_HOME:
3216 code = 1;
3217 break;
3218 case VK_INSERT:
3219 code = 2;
3220 break;
3221 case VK_DELETE:
3222 code = 3;
3223 break;
3224 case VK_END:
3225 code = 4;
3226 break;
3227 case VK_PRIOR:
3228 code = 5;
3229 break;
3230 case VK_NEXT:
3231 code = 6;
3232 break;
374330e2 3233 }
ec55b220 3234 /* Reorder edit keys to physical order */
32874aea 3235 if (cfg.funky_type == 3 && code <= 6)
3236 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3237
f37caa11 3238 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3239 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3240 return p - output;
3241 }
3242
9bc81a2c 3243 if (cfg.funky_type == 5 && /* SCO function keys */
3244 code >= 11 && code <= 34) {
e24b1972 3245 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3246 int index = 0;
3247 switch (wParam) {
3248 case VK_F1: index = 0; break;
3249 case VK_F2: index = 1; break;
3250 case VK_F3: index = 2; break;
3251 case VK_F4: index = 3; break;
3252 case VK_F5: index = 4; break;
3253 case VK_F6: index = 5; break;
3254 case VK_F7: index = 6; break;
3255 case VK_F8: index = 7; break;
3256 case VK_F9: index = 8; break;
3257 case VK_F10: index = 9; break;
3258 case VK_F11: index = 10; break;
3259 case VK_F12: index = 11; break;
3260 }
3261 if (keystate[VK_SHIFT] & 0x80) index += 12;
3262 if (keystate[VK_CONTROL] & 0x80) index += 24;
3263 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3264 return p - output;
3265 }
9bc81a2c 3266 if (cfg.funky_type == 5 && /* SCO small keypad */
3267 code >= 1 && code <= 6) {
3268 char codes[] = "HL.FIG";
3269 if (code == 3) {
3270 *p++ = '\x7F';
3271 } else {
3272 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3273 }
3274 return p - output;
3275 }
f37caa11 3276 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3277 int offt = 0;
32874aea 3278 if (code > 15)
3279 offt++;
3280 if (code > 21)
3281 offt++;
f37caa11 3282 if (vt52_mode)
32874aea 3283 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3284 else
32874aea 3285 p +=
3286 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3287 return p - output;
3288 }
c9def1b8 3289 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3290 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3291 return p - output;
3292 }
3293 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3294 if (vt52_mode)
32874aea 3295 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3296 else
32874aea 3297 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3298 return p - output;
3299 }
3300 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3301 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3302 return p - output;
3303 }
3304 if (code) {
32874aea 3305 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3306 return p - output;
374330e2 3307 }
45dabbc5 3308
c9def1b8 3309 /*
3310 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3311 * some reason seems to send VK_CLEAR to Windows...).
3312 */
3313 {
3314 char xkey = 0;
3315 switch (wParam) {
32874aea 3316 case VK_UP:
3317 xkey = 'A';
3318 break;
3319 case VK_DOWN:
3320 xkey = 'B';
3321 break;
3322 case VK_RIGHT:
3323 xkey = 'C';
3324 break;
3325 case VK_LEFT:
3326 xkey = 'D';
3327 break;
3328 case VK_CLEAR:
3329 xkey = 'G';
3330 break;
c9def1b8 3331 }
32874aea 3332 if (xkey) {
c9def1b8 3333 if (vt52_mode)
32874aea 3334 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3335 else {
3336 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3337 /* VT100 & VT102 manuals both state the app cursor keys
3338 * only work if the app keypad is on.
3339 */
3340 if (!app_keypad_keys)
3341 app_flg = 0;
3342 /* Useful mapping of Ctrl-arrows */
3343 if (shift_state == 2)
3344 app_flg = !app_flg;
3345
3346 if (app_flg)
3347 p += sprintf((char *) p, "\x1BO%c", xkey);
3348 else
3349 p += sprintf((char *) p, "\x1B[%c", xkey);
3350 }
c9def1b8 3351 return p - output;
3352 }
3353 }
0c50ef57 3354
3355 /*
3356 * Finally, deal with Return ourselves. (Win95 seems to
3357 * foul it up when Alt is pressed, for some reason.)
3358 */
32874aea 3359 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3360 *p++ = 0x0D;
a5f3e637 3361 *p++ = 0;
3362 return -2;
0c50ef57 3363 }
4eeb7d09 3364
3365 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3366 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3367 else
3368 alt_sum = 0;
67c339f7 3369 }
374330e2 3370
c9def1b8 3371 /* Okay we've done everything interesting; let windows deal with
3372 * the boring stuff */
3373 {
a9c02454 3374 BOOL capsOn=0;
3375
3376 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3377 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3378 capsOn= !left_alt;
3379 keystate[VK_CAPITAL] = 0;
3380 }
3381
00e3ba0f 3382 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3383#ifdef SHOW_TOASCII_RESULT
3384 if (r == 1 && !key_down) {
3385 if (alt_sum) {
8f22582c 3386 if (in_utf || dbcs_screenfont)
4eeb7d09 3387 debug((", (U+%04x)", alt_sum));
3388 else
3389 debug((", LCH(%d)", alt_sum));
3390 } else {
3391 debug((", ACH(%d)", keys[0]));
3392 }
3393 } else if (r > 0) {
3394 int r1;
3395 debug((", ASC("));
3396 for (r1 = 0; r1 < r; r1++) {
3397 debug(("%s%d", r1 ? "," : "", keys[r1]));
3398 }
3399 debug((")"));
3400 }
3401#endif
32874aea 3402 if (r > 0) {
4eeb7d09 3403 WCHAR keybuf;
256cb87c 3404
3405 /*
3406 * Interrupt an ongoing paste. I'm not sure this is
3407 * sensible, but for the moment it's preferable to
3408 * having to faff about buffering things.
3409 */
3410 term_nopaste();
3411
c9def1b8 3412 p = output;
32874aea 3413 for (i = 0; i < r; i++) {
3414 unsigned char ch = (unsigned char) keys[i];
14963b8f 3415
32874aea 3416 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3417 compose_char = ch;
32874aea 3418 compose_state++;
c9def1b8 3419 continue;
3420 }
32874aea 3421 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3422 int nc;
3423 compose_state = 0;
3424
32874aea 3425 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3426 MessageBeep(MB_ICONHAND);
c9def1b8 3427 return 0;
3428 }
4eeb7d09 3429 keybuf = nc;
760e88b2 3430 luni_send(&keybuf, 1, 1);
4eeb7d09 3431 continue;
c9def1b8 3432 }
374330e2 3433
c9def1b8 3434 compose_state = 0;
374330e2 3435
4eeb7d09 3436 if (!key_down) {
3437 if (alt_sum) {
8f22582c 3438 if (in_utf || dbcs_screenfont) {
4eeb7d09 3439 keybuf = alt_sum;
760e88b2 3440 luni_send(&keybuf, 1, 1);
4eeb7d09 3441 } else {
3442 ch = (char) alt_sum;
5471d09a 3443 /*
3444 * We need not bother about stdin
3445 * backlogs here, because in GUI PuTTY
3446 * we can't do anything about it
3447 * anyway; there's no means of asking
3448 * Windows to hold off on KEYDOWN
3449 * messages. We _have_ to buffer
3450 * everything we're sent.
3451 */
760e88b2 3452 ldisc_send(&ch, 1, 1);
4eeb7d09 3453 }
3454 alt_sum = 0;
3455 } else
760e88b2 3456 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3457 } else {
a9c02454 3458 if(capsOn && ch < 0x80) {
3459 WCHAR cbuf[2];
3460 cbuf[0] = 27;
3461 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3462 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3463 } else {
3464 char cbuf[2];
3465 cbuf[0] = '\033';
3466 cbuf[1] = ch;
760e88b2 3467 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3468 }
c9def1b8 3469 }
bca9517a 3470 show_mouseptr(0);
c9def1b8 3471 }
374330e2 3472
c9def1b8 3473 /* This is so the ALT-Numpad and dead keys work correctly. */
3474 keys[0] = 0;
3475
32874aea 3476 return p - output;
c9def1b8 3477 }
159eba53 3478 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3479 if (!left_alt)
3480 keys[0] = 0;
4eeb7d09 3481 /* If we will be using alt_sum fix the 256s */
8f22582c 3482 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3483 keys[0] = 10;
374330e2 3484 }
3485
dfca2656 3486 /*
3487 * ALT alone may or may not want to bring up the System menu.
3488 * If it's not meant to, we return 0 on presses or releases of
3489 * ALT, to show that we've swallowed the keystroke. Otherwise
3490 * we return -1, which means Windows will give the keystroke
3491 * its default handling (i.e. bring up the System menu).
3492 */
3493 if (wParam == VK_MENU && !cfg.alt_only)
3494 return 0;
374330e2 3495
c9def1b8 3496 return -1;
374330e2 3497}
3498
32874aea 3499void set_title(char *title)
3500{
3501 sfree(window_name);
3502 window_name = smalloc(1 + strlen(title));
3503 strcpy(window_name, title);
37508af4 3504 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3505 SetWindowText(hwnd, title);
374330e2 3506}
3507
32874aea 3508void set_icon(char *title)
3509{
3510 sfree(icon_name);
3511 icon_name = smalloc(1 + strlen(title));
3512 strcpy(icon_name, title);
37508af4 3513 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3514 SetWindowText(hwnd, title);
374330e2 3515}
3516
32874aea 3517void set_sbar(int total, int start, int page)
3518{
374330e2 3519 SCROLLINFO si;
c9def1b8 3520
32874aea 3521 if (!cfg.scrollbar)
3522 return;
c9def1b8 3523
374330e2 3524 si.cbSize = sizeof(si);
c9def1b8 3525 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3526 si.nMin = 0;
3527 si.nMax = total - 1;
3528 si.nPage = page;
3529 si.nPos = start;
c1f5f956 3530 if (hwnd)
32874aea 3531 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3532}
3533
32874aea 3534Context get_ctx(void)
3535{
374330e2 3536 HDC hdc;
3537 if (hwnd) {
32874aea 3538 hdc = GetDC(hwnd);
374330e2 3539 if (hdc && pal)
32874aea 3540 SelectPalette(hdc, pal, FALSE);
374330e2 3541 return hdc;
3542 } else
3543 return NULL;
3544}
3545
32874aea 3546void free_ctx(Context ctx)
3547{
3548 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3549 ReleaseDC(hwnd, ctx);
374330e2 3550}
3551
32874aea 3552static void real_palette_set(int n, int r, int g, int b)
3553{
374330e2 3554 if (pal) {
3555 logpal->palPalEntry[n].peRed = r;
3556 logpal->palPalEntry[n].peGreen = g;
3557 logpal->palPalEntry[n].peBlue = b;
3558 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3559 colours[n] = PALETTERGB(r, g, b);
32874aea 3560 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3561 } else
3562 colours[n] = RGB(r, g, b);
3563}
3564
32874aea 3565void palette_set(int n, int r, int g, int b)
3566{
374330e2 3567 static const int first[21] = {
3568 0, 2, 4, 6, 8, 10, 12, 14,
3569 1, 3, 5, 7, 9, 11, 13, 15,
3570 16, 17, 18, 20, 22
3571 };
32874aea 3572 real_palette_set(first[n], r, g, b);
374330e2 3573 if (first[n] >= 18)
32874aea 3574 real_palette_set(first[n] + 1, r, g, b);
374330e2 3575 if (pal) {
3576 HDC hdc = get_ctx();
32874aea 3577 UnrealizeObject(pal);
3578 RealizePalette(hdc);
3579 free_ctx(hdc);
374330e2 3580 }
3581}
3582
32874aea 3583void palette_reset(void)
3584{
374330e2 3585 int i;
3586
3587 for (i = 0; i < NCOLOURS; i++) {
3588 if (pal) {
3589 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3590 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3591 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3592 logpal->palPalEntry[i].peFlags = 0;
3593 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3594 defpal[i].rgbtGreen,
3595 defpal[i].rgbtBlue);
3596 } else
3597 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3598 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3599 }
3600
3601 if (pal) {
3602 HDC hdc;
32874aea 3603 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3604 hdc = get_ctx();
32874aea 3605 RealizePalette(hdc);
3606 free_ctx(hdc);
374330e2 3607 }
3608}
3609
4eeb7d09 3610void write_aclip(char *data, int len, int must_deselect)
32874aea 3611{
374330e2 3612 HGLOBAL clipdata;
3613 void *lock;
3614
32874aea 3615 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3616 if (!clipdata)
3617 return;
32874aea 3618 lock = GlobalLock(clipdata);
374330e2 3619 if (!lock)
3620 return;
32874aea 3621 memcpy(lock, data, len);
3622 ((unsigned char *) lock)[len] = 0;
3623 GlobalUnlock(clipdata);
374330e2 3624
f0df44da 3625 if (!must_deselect)
32874aea 3626 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3627
32874aea 3628 if (OpenClipboard(hwnd)) {
374330e2 3629 EmptyClipboard();
32874aea 3630 SetClipboardData(CF_TEXT, clipdata);
374330e2 3631 CloseClipboard();
3632 } else
32874aea 3633 GlobalFree(clipdata);
f0df44da 3634
3635 if (!must_deselect)
32874aea 3636 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3637}
3638
4eeb7d09 3639/*
3640 * Note: unlike write_aclip() this will not append a nul.
3641 */
3642void write_clip(wchar_t * data, int len, int must_deselect)
3643{
3644 HGLOBAL clipdata;
3645 HGLOBAL clipdata2;
3646 int len2;
3647 void *lock, *lock2;
3648
3649 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3650
3651 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3652 len * sizeof(wchar_t));
3653 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3654
3655 if (!clipdata || !clipdata2) {
3656 if (clipdata)
3657 GlobalFree(clipdata);
3658 if (clipdata2)
3659 GlobalFree(clipdata2);
3660 return;
3661 }
3662 if (!(lock = GlobalLock(clipdata)))
3663 return;
3664 if (!(lock2 = GlobalLock(clipdata2)))
3665 return;
3666
3667 memcpy(lock, data, len * sizeof(wchar_t));
3668 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3669
3670 GlobalUnlock(clipdata);
3671 GlobalUnlock(clipdata2);
3672
3673 if (!must_deselect)
3674 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3675
3676 if (OpenClipboard(hwnd)) {
3677 EmptyClipboard();
3678 SetClipboardData(CF_UNICODETEXT, clipdata);
3679 SetClipboardData(CF_TEXT, clipdata2);
3680 CloseClipboard();
3681 } else {
3682 GlobalFree(clipdata);
3683 GlobalFree(clipdata2);
3684 }
3685
3686 if (!must_deselect)
3687 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3688}
3689
3690void get_clip(wchar_t ** p, int *len)
32874aea 3691{
374330e2 3692 static HGLOBAL clipdata = NULL;
4eeb7d09 3693 static wchar_t *converted = 0;
3694 wchar_t *p2;
374330e2 3695
4eeb7d09 3696 if (converted) {
3697 sfree(converted);
3698 converted = 0;
3699 }
374330e2 3700 if (!p) {
3701 if (clipdata)
32874aea 3702 GlobalUnlock(clipdata);
374330e2 3703 clipdata = NULL;
3704 return;
4eeb7d09 3705 } else if (OpenClipboard(NULL)) {
2d466ffd 3706 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3707 CloseClipboard();
4eeb7d09 3708 *p = GlobalLock(clipdata);
3709 if (*p) {
3710 for (p2 = *p; *p2; p2++);
3711 *len = p2 - *p;
3712 return;
374330e2 3713 }
2d466ffd 3714 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3715 char *s;
3716 int i;
3717 CloseClipboard();
3718 s = GlobalLock(clipdata);
3719 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3720 *p = converted = smalloc(i * sizeof(wchar_t));
3721 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3722 *len = i - 1;
3723 return;
3724 } else
3725 CloseClipboard();
374330e2 3726 }
3727
3728 *p = NULL;
3729 *len = 0;
3730}
3731
4eeb7d09 3732#if 0
374330e2 3733/*
3734 * Move `lines' lines from position `from' to position `to' in the
3735 * window.
3736 */
32874aea 3737void optimised_move(int to, int from, int lines)
3738{
374330e2 3739 RECT r;
f67b4e85 3740 int min, max;
374330e2 3741
3742 min = (to < from ? to : from);
3743 max = to + from - min;
374330e2 3744
5a73255e 3745 r.left = offset_width;
3746 r.right = offset_width + cols * font_width;
3747 r.top = offset_height + min * font_height;
3748 r.bottom = offset_height + (max + lines) * font_height;
32874aea 3749 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3750}
4eeb7d09 3751#endif
374330e2 3752
3753/*
3754 * Print a message box and perform a fatal exit.
3755 */
32874aea 3756void fatalbox(char *fmt, ...)
3757{
374330e2 3758 va_list ap;
3759 char stuff[200];
3760
3761 va_start(ap, fmt);
3762 vsprintf(stuff, fmt, ap);
3763 va_end(ap);
3764 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3765 exit(1);
3766}
3767
3768/*
f8a28d1f 3769 * Manage window caption / taskbar flashing, if enabled.
3770 * 0 = stop, 1 = maintain, 2 = start
3771 */
3772static void flash_window(int mode)
3773{
3774 static long last_flash = 0;
3775 static int flashing = 0;
3776 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3777 /* stop */
3778 if (flashing) {
3779 FlashWindow(hwnd, FALSE);
3780 flashing = 0;
3781 }
3782
3783 } else if (mode == 2) {
3784 /* start */
3785 if (!flashing) {
3786 last_flash = GetTickCount();
3787 flashing = 1;
3788 FlashWindow(hwnd, TRUE);
3789 }
3790
3791 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3792 /* maintain */
3793 if (flashing) {
3794 long now = GetTickCount();
3795 long fdiff = now - last_flash;
3796 if (fdiff < 0 || fdiff > 450) {
3797 last_flash = now;
3798 FlashWindow(hwnd, TRUE); /* toggle */
3799 }
3800 }
3801 }
3802}
3803
3804/*
374330e2 3805 * Beep.
3806 */
32874aea 3807void beep(int mode)
3808{
03169ad0 3809 if (mode == BELL_DEFAULT) {
eb04402e 3810 /*
3811 * For MessageBeep style bells, we want to be careful of
3812 * timing, because they don't have the nice property of
3813 * PlaySound bells that each one cancels the previous
3814 * active one. So we limit the rate to one per 50ms or so.
3815 */
3816 static long lastbeep = 0;
d51cdf1e 3817 long beepdiff;
eb04402e 3818
d51cdf1e 3819 beepdiff = GetTickCount() - lastbeep;
eb04402e 3820 if (beepdiff >= 0 && beepdiff < 50)
3821 return;
156686ef 3822 MessageBeep(MB_OK);
d51cdf1e 3823 /*
3824 * The above MessageBeep call takes time, so we record the
3825 * time _after_ it finishes rather than before it starts.
3826 */
3827 lastbeep = GetTickCount();
03169ad0 3828 } else if (mode == BELL_WAVEFILE) {
3829 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 3830 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 3831 sprintf(buf, "Unable to play sound file\n%s\n"
3832 "Using default sound instead", cfg.bell_wavefile);
32874aea 3833 MessageBox(hwnd, buf, "PuTTY Sound Error",
3834 MB_OK | MB_ICONEXCLAMATION);
03169ad0 3835 cfg.beep = BELL_DEFAULT;
3836 }
3837 }
f8a28d1f 3838 /* Otherwise, either visual bell or disabled; do nothing here */
3839 if (!has_focus) {
3840 flash_window(2); /* start */
3841 }
374330e2 3842}
8f57d753 3843
3844/*
3845 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3846 * implementation.
a401e5f3 3847 * Revised by <wez@thebrainroom.com>
8f57d753 3848 */
3849static void flip_full_screen(void)
3850{
a401e5f3 3851 want_full_screen = !want_full_screen;
8f57d753 3852
a401e5f3 3853 if (full_screen == want_full_screen)
3854 return;
3855
3856 full_screen = want_full_screen;
3857
3858 old_wind_placement.length = sizeof(old_wind_placement);
3859
3860 if (full_screen) {
3861 int x, y, cx, cy;
fc35d05a 3862#if !defined(NO_MULTIMON) && defined(MONITOR_DEFAULTTONEAREST)
a401e5f3 3863 /* The multi-monitor safe way of doing things */
3864 HMONITOR mon;
3865 MONITORINFO mi;
3866
3867 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
3868 mi.cbSize = sizeof(mi);
3869 GetMonitorInfo(mon, &mi);
3870 x = mi.rcMonitor.left;
3871 y = mi.rcMonitor.top;
3872 cx = mi.rcMonitor.right;
3873 cy = mi.rcMonitor.bottom;
3874#else
3875 /* good old fashioned way of doing it */
3876 x = 0;
3877 y = 0;
8f57d753 3878 cx = GetSystemMetrics(SM_CXSCREEN);
3879 cy = GetSystemMetrics(SM_CYSCREEN);
a401e5f3 3880#endif
3881
3882 /* save rows for when we "restore" back down again */
3883 pre_fs_rows = rows;
3884 pre_fs_cols = cols;
3885
8f57d753 3886 GetWindowPlacement(hwnd, &old_wind_placement);
8f57d753 3887 SetWindowLong(hwnd, GWL_STYLE,
a401e5f3 3888 GetWindowLong(hwnd, GWL_STYLE)
3889 & ~((cfg.scrollbar_in_fullscreen ? 0 : WS_VSCROLL)
3890 | WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3891 /* become topmost */
3892 SetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_FRAMECHANGED);
8f57d753 3893 } else {
a401e5f3 3894 was_full_screen = 1;
3895 SetWindowLong(hwnd, GWL_STYLE,
3896 GetWindowLong(hwnd, GWL_STYLE)
3897 | (cfg.scrollbar ? WS_VSCROLL : 0)
3898 | WS_CAPTION | WS_BORDER | WS_THICKFRAME);
3899 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
3900 SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
8f57d753 3901 SetWindowPlacement(hwnd,&old_wind_placement);
8f57d753 3902 }
a401e5f3 3903 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
3904 MF_BYCOMMAND| full_screen ? MF_CHECKED : MF_UNCHECKED);
8f57d753 3905}