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