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