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