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