Fix pasting of newlines in local line editing mode. Possibly not a
[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 /*
2146 * We need not bother about stdin backlogs
2147 * here, because in GUI PuTTY we can't do
2148 * anything about it anyway; there's no means
2149 * of asking Windows to hold off on KEYDOWN
2150 * messages. We _have_ to buffer everything
2151 * we're sent.
2152 */
760e88b2 2153 ldisc_send(buf, len, 1);
32874aea 2154 show_mouseptr(0);
bca9517a 2155 }
3cf144db 2156 }
374330e2 2157 }
2158 return 0;
4eeb7d09 2159 case WM_INPUTLANGCHANGE:
3cf144db 2160 {
4eeb7d09 2161 /* wParam == Font number */
2162 /* lParam == Locale */
2163 char lbuf[20];
2164 HKL NewInputLocale = (HKL) lParam;
2165
2166 // lParam == GetKeyboardLayout(0);
2167
2168 GetLocaleInfo(LOWORD(NewInputLocale),
2169 LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
2170
2171 kbd_codepage = atoi(lbuf);
2172 }
2173 break;
88485e4d 2174 case WM_IME_COMPOSITION:
2175 {
2176 HIMC hIMC;
2177 int n;
2178 char *buff;
2179
2180 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2181 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2182
2183 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2184 break; /* fall back to DefWindowProc */
2185
2186 hIMC = ImmGetContext(hwnd);
2187 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2188
2189 if (n > 0) {
2190 buff = (char*) smalloc(n);
2191 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
760e88b2 2192 luni_send((unsigned short *)buff, n / 2, 1);
88485e4d 2193 free(buff);
2194 }
2195 ImmReleaseContext(hwnd, hIMC);
2196 return 1;
2197 }
2198
4eeb7d09 2199 case WM_IME_CHAR:
2200 if (wParam & 0xFF00) {
3cf144db 2201 unsigned char buf[2];
2202
2203 buf[1] = wParam;
2204 buf[0] = wParam >> 8;
760e88b2 2205 lpage_send(kbd_codepage, buf, 2, 1);
4eeb7d09 2206 } else {
2207 char c = (unsigned char) wParam;
760e88b2 2208 lpage_send(kbd_codepage, &c, 1, 1);
3cf144db 2209 }
4eeb7d09 2210 return (0);
374330e2 2211 case WM_CHAR:
2212 case WM_SYSCHAR:
2213 /*
2214 * Nevertheless, we are prepared to deal with WM_CHAR
2215 * messages, should they crop up. So if someone wants to
2216 * post the things to us as part of a macro manoeuvre,
2217 * we're ready to cope.
2218 */
32874aea 2219 {
4eeb7d09 2220 char c = (unsigned char)wParam;
760e88b2 2221 lpage_send(CP_ACP, &c, 1, 1);
374330e2 2222 }
2223 return 0;
32874aea 2224 case WM_SETCURSOR:
ca3ab3ee 2225 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
32874aea 2226 SetCursor(LoadCursor(NULL, IDC_ARROW));
2227 return TRUE;
2228 }
374330e2 2229 }
2230
32874aea 2231 return DefWindowProc(hwnd, message, wParam, lParam);
374330e2 2232}
2233
2234/*
ec8679e9 2235 * Move the system caret. (We maintain one, even though it's
2236 * invisible, for the benefit of blind people: apparently some
2237 * helper software tracks the system caret, so we should arrange to
2238 * have one.)
2239 */
32874aea 2240void sys_cursor(int x, int y)
2241{
88485e4d 2242 COMPOSITIONFORM cf;
2243 HIMC hIMC;
2244
2245 if (!has_focus) return;
2246
5a73255e 2247 SetCaretPos(x * font_width + offset_width,
2248 y * font_height + offset_height);
88485e4d 2249
2250 /* IMM calls on Win98 and beyond only */
2251 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2252
2253 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2254 osVersion.dwMinorVersion == 0) return; /* 95 */
2255
2256 /* we should have the IMM functions */
2257 hIMC = ImmGetContext(hwnd);
2258 cf.dwStyle = CFS_POINT;
2259 cf.ptCurrentPos.x = x * font_width;
2260 cf.ptCurrentPos.y = y * font_height;
2261 ImmSetCompositionWindow(hIMC, &cf);
2262
2263 ImmReleaseContext(hwnd, hIMC);
ec8679e9 2264}
2265
2266/*
374330e2 2267 * Draw a line of text in the window, at given character
2268 * coordinates, in given attributes.
2269 *
2270 * We are allowed to fiddle with the contents of `text'.
2271 */
32874aea 2272void do_text(Context ctx, int x, int y, char *text, int len,
2273 unsigned long attr, int lattr)
2274{
374330e2 2275 COLORREF fg, bg, t;
2276 int nfg, nbg, nfont;
2277 HDC hdc = ctx;
59ad2c03 2278 RECT line_box;
2279 int force_manual_underline = 0;
32874aea 2280 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
4eeb7d09 2281 int char_width = fnt_width;
2282 int text_adjust = 0;
2283 static int *IpDx = 0, IpDxLEN = 0;
2284
2285 if (attr & ATTR_WIDE)
2286 char_width *= 2;
59ad2c03 2287
4eeb7d09 2288 if (len > IpDxLEN || IpDx[0] != char_width) {
59ad2c03 2289 int i;
32874aea 2290 if (len > IpDxLEN) {
59ad2c03 2291 sfree(IpDx);
32874aea 2292 IpDx = smalloc((len + 16) * sizeof(int));
2293 IpDxLEN = (len + 16);
59ad2c03 2294 }
32874aea 2295 for (i = 0; i < IpDxLEN; i++)
4eeb7d09 2296 IpDx[i] = char_width;
59ad2c03 2297 }
374330e2 2298
5a73255e 2299 /* Only want the left half of double width lines */
2300 if (lattr != LATTR_NORM && x*2 >= cols)
2301 return;
2302
c9def1b8 2303 x *= fnt_width;
374330e2 2304 y *= font_height;
5a73255e 2305 x += offset_width;
2306 y += offset_height;
374330e2 2307
4eeb7d09 2308 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2309 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
374330e2 2310 attr ^= ATTR_CUR_XOR;
2311 }
2312
2313 nfont = 0;
4eeb7d09 2314 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2315 /* Assume a poorman font is borken in other ways too. */
2316 lattr = LATTR_WIDE;
2317 } else
2318 switch (lattr) {
2319 case LATTR_NORM:
2320 break;
2321 case LATTR_WIDE:
2322 nfont |= FONT_WIDE;
374330e2 2323 break;
4eeb7d09 2324 default:
2325 nfont |= FONT_WIDE + FONT_HIGH;
2326 break;
2327 }
5a73255e 2328 if (attr & ATTR_NARROW)
2329 nfont |= FONT_NARROW;
4eeb7d09 2330
2331 /* Special hack for the VT100 linedraw glyphs. */
2332 if ((attr & CSET_MASK) == 0x2300) {
5a73255e 2333 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
4eeb7d09 2334 switch ((unsigned char) (text[0])) {
2335 case 0xBA:
2336 text_adjust = -2 * font_height / 5;
2337 break;
2338 case 0xBB:
2339 text_adjust = -1 * font_height / 5;
2340 break;
2341 case 0xBC:
2342 text_adjust = font_height / 5;
2343 break;
2344 case 0xBD:
2345 text_adjust = 2 * font_height / 5;
32874aea 2346 break;
59ad2c03 2347 }
4eeb7d09 2348 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2349 text_adjust *= 2;
2350 attr &= ~CSET_MASK;
2351 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2352 attr |= (unitab_xterm['q'] & CSET_MASK);
2353 if (attr & ATTR_UNDER) {
2354 attr &= ~ATTR_UNDER;
2355 force_manual_underline = 1;
2356 }
374330e2 2357 }
2358 }
2359
4eeb7d09 2360 /* Anything left as an original character set is unprintable. */
2361 if (DIRECT_CHAR(attr)) {
2362 attr &= ~CSET_MASK;
2363 attr |= 0xFF00;
5a73255e 2364 memset(text, 0xFD, len);
4eeb7d09 2365 }
2366
2367 /* OEM CP */
2368 if ((attr & CSET_MASK) == ATTR_OEMCP)
2369 nfont |= FONT_OEM;
2370
374330e2 2371 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2372 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2373 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2374 nfont |= FONT_BOLD;
2375 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2376 nfont |= FONT_UNDERLINE;
4eeb7d09 2377 another_font(nfont);
32874aea 2378 if (!fonts[nfont]) {
2379 if (nfont & FONT_UNDERLINE)
59ad2c03 2380 force_manual_underline = 1;
2381 /* Don't do the same for manual bold, it could be bad news. */
2382
32874aea 2383 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
59ad2c03 2384 }
4eeb7d09 2385 another_font(nfont);
2386 if (!fonts[nfont])
2387 nfont = FONT_NORMAL;
374330e2 2388 if (attr & ATTR_REVERSE) {
32874aea 2389 t = nfg;
2390 nfg = nbg;
2391 nbg = t;
374330e2 2392 }
2393 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2394 nfg++;
59ad2c03 2395 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2396 nbg++;
374330e2 2397 fg = colours[nfg];
2398 bg = colours[nbg];
32874aea 2399 SelectObject(hdc, fonts[nfont]);
2400 SetTextColor(hdc, fg);
2401 SetBkColor(hdc, bg);
2402 SetBkMode(hdc, OPAQUE);
2403 line_box.left = x;
2404 line_box.top = y;
4eeb7d09 2405 line_box.right = x + char_width * len;
32874aea 2406 line_box.bottom = y + font_height;
4eeb7d09 2407
5a73255e 2408 /* Only want the left half of double width lines */
2409 if (line_box.right > font_width*cols+offset_width)
2410 line_box.right = font_width*cols+offset_width;
2411
4eeb7d09 2412 /* We're using a private area for direct to font. (512 chars.) */
2413 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2414 /* Ho Hum, dbcs fonts are a PITA! */
2415 /* To display on W9x I have to convert to UCS */
2416 static wchar_t *uni_buf = 0;
2417 static int uni_len = 0;
5a73255e 2418 int nlen, mptr;
4eeb7d09 2419 if (len > uni_len) {
2420 sfree(uni_buf);
2421 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2422 }
4eeb7d09 2423
5a73255e 2424 for(nlen = mptr = 0; mptr<len; mptr++) {
2425 uni_buf[nlen] = 0xFFFD;
2426 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2427 IpDx[nlen] += char_width;
2428 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2429 text+mptr, 2, uni_buf+nlen, 1);
2430 mptr++;
2431 }
2432 else
2433 {
2434 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2435 text+mptr, 1, uni_buf+nlen, 1);
2436 }
2437 nlen++;
2438 }
4eeb7d09 2439 if (nlen <= 0)
2440 return; /* Eeek! */
2441
2442 ExtTextOutW(hdc, x,
2443 y - font_height * (lattr == LATTR_BOT) + text_adjust,
5a73255e 2444 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2445 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2446 SetBkMode(hdc, TRANSPARENT);
2447 ExtTextOutW(hdc, x - 1,
2448 y - font_height * (lattr ==
2449 LATTR_BOT) + text_adjust,
5a73255e 2450 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
4eeb7d09 2451 }
5a73255e 2452
2453 IpDx[0] = -1;
4eeb7d09 2454 } else if (DIRECT_FONT(attr)) {
2455 ExtTextOut(hdc, x,
2456 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2457 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2458 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2459 SetBkMode(hdc, TRANSPARENT);
2460
2461 /* GRR: This draws the character outside it's box and can leave
2462 * 'droppings' even with the clip box! I suppose I could loop it
2463 * one character at a time ... yuk.
2464 *
2465 * Or ... I could do a test print with "W", and use +1 or -1 for this
2466 * shift depending on if the leftmost column is blank...
2467 */
2468 ExtTextOut(hdc, x - 1,
2469 y - font_height * (lattr ==
2470 LATTR_BOT) + text_adjust,
2471 ETO_CLIPPED, &line_box, text, len, IpDx);
2472 }
2473 } else {
2474 /* And 'normal' unicode characters */
2475 static WCHAR *wbuf = NULL;
2476 static int wlen = 0;
2477 int i;
2478 if (wlen < len) {
2479 sfree(wbuf);
2480 wlen = len;
2481 wbuf = smalloc(wlen * sizeof(WCHAR));
2482 }
2483 for (i = 0; i < len; i++)
2484 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2485
2486 ExtTextOutW(hdc, x,
2487 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2488 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2489
2490 /* And the shadow bold hack. */
5a73255e 2491 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
4eeb7d09 2492 SetBkMode(hdc, TRANSPARENT);
2493 ExtTextOutW(hdc, x - 1,
2494 y - font_height * (lattr ==
2495 LATTR_BOT) + text_adjust,
2496 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2497 }
374330e2 2498 }
4eeb7d09 2499 if (lattr != LATTR_TOP && (force_manual_underline ||
2500 (und_mode == UND_LINE
2501 && (attr & ATTR_UNDER)))) {
32874aea 2502 HPEN oldpen;
4eeb7d09 2503 int dec = descent;
2504 if (lattr == LATTR_BOT)
2505 dec = dec * 2 - font_height;
2506
32874aea 2507 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
4eeb7d09 2508 MoveToEx(hdc, x, y + dec, NULL);
2509 LineTo(hdc, x + len * char_width, y + dec);
32874aea 2510 oldpen = SelectObject(hdc, oldpen);
2511 DeleteObject(oldpen);
374330e2 2512 }
4eeb7d09 2513}
2514
2515void do_cursor(Context ctx, int x, int y, char *text, int len,
2516 unsigned long attr, int lattr)
2517{
2518
2519 int fnt_width;
2520 int char_width;
2521 HDC hdc = ctx;
2522 int ctype = cfg.cursor_type;
2523
2524 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2525 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2526 do_text(ctx, x, y, text, len, attr, lattr);
2527 return;
2528 }
2529 ctype = 2;
2530 attr |= TATTR_RIGHTCURS;
2531 }
2532
2533 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2534 if (attr & ATTR_WIDE)
2535 char_width *= 2;
2536 x *= fnt_width;
2537 y *= font_height;
5a73255e 2538 x += offset_width;
2539 y += offset_height;
4eeb7d09 2540
2541 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
374330e2 2542 POINT pts[5];
32874aea 2543 HPEN oldpen;
374330e2 2544 pts[0].x = pts[1].x = pts[4].x = x;
4eeb7d09 2545 pts[2].x = pts[3].x = x + char_width - 1;
374330e2 2546 pts[0].y = pts[3].y = pts[4].y = y;
32874aea 2547 pts[1].y = pts[2].y = y + font_height - 1;
2548 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2549 Polyline(hdc, pts, 5);
2550 oldpen = SelectObject(hdc, oldpen);
2551 DeleteObject(oldpen);
4eeb7d09 2552 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
32874aea 2553 int startx, starty, dx, dy, length, i;
4eeb7d09 2554 if (ctype == 1) {
32874aea 2555 startx = x;
2556 starty = y + descent;
2557 dx = 1;
2558 dy = 0;
4eeb7d09 2559 length = char_width;
32874aea 2560 } else {
4e30ff69 2561 int xadjust = 0;
4eeb7d09 2562 if (attr & TATTR_RIGHTCURS)
2563 xadjust = char_width - 1;
32874aea 2564 startx = x + xadjust;
2565 starty = y;
2566 dx = 0;
2567 dy = 1;
2568 length = font_height;
2569 }
4eeb7d09 2570 if (attr & TATTR_ACTCURS) {
32874aea 2571 HPEN oldpen;
2572 oldpen =
2573 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2574 MoveToEx(hdc, startx, starty, NULL);
2575 LineTo(hdc, startx + dx * length, starty + dy * length);
2576 oldpen = SelectObject(hdc, oldpen);
2577 DeleteObject(oldpen);
2578 } else {
2579 for (i = 0; i < length; i++) {
2580 if (i % 2 == 0) {
2581 SetPixel(hdc, startx, starty, colours[23]);
2582 }
2583 startx += dx;
2584 starty += dy;
2585 }
2586 }
4e30ff69 2587 }
374330e2 2588}
2589
5a73255e 2590/* This function gets the actual width of a character in the normal font.
2591 */
2592int CharWidth(Context ctx, int uc) {
2593 HDC hdc = ctx;
2594 int ibuf = 0;
2595
2596 /* If the font max is the same as the font ave width then this
2597 * function is a no-op.
2598 */
2599 if (!font_dualwidth) return 1;
2600
2601 switch (uc & CSET_MASK) {
2602 case ATTR_ASCII:
2603 uc = unitab_line[uc & 0xFF];
2604 break;
2605 case ATTR_LINEDRW:
2606 uc = unitab_xterm[uc & 0xFF];
2607 break;
2608 case ATTR_SCOACS:
2609 uc = unitab_scoacs[uc & 0xFF];
2610 break;
2611 }
2612 if (DIRECT_FONT(uc)) {
2613 if (dbcs_screenfont) return 1;
2614
2615 /* Speedup, I know of no font where ascii is the wrong width */
2616 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2617 return 1;
2618
2619 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2620 SelectObject(hdc, fonts[FONT_NORMAL]);
2621 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2622 another_font(FONT_OEM);
2623 if (!fonts[FONT_OEM]) return 0;
2624
2625 SelectObject(hdc, fonts[FONT_OEM]);
2626 } else
2627 return 0;
2628
2629 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2630 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2631 return 0;
2632 } else {
2633 /* Speedup, I know of no font where ascii is the wrong width */
2634 if (uc >= ' ' && uc <= '~') return 1;
2635
2636 SelectObject(hdc, fonts[FONT_NORMAL]);
2637 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2638 /* Okay that one worked */ ;
2639 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2640 /* This should work on 9x too, but it's "less accurate" */ ;
2641 else
2642 return 0;
2643 }
2644
2645 ibuf += font_width / 2 -1;
2646 ibuf /= font_width;
2647
2648 return ibuf;
2649}
2650
374330e2 2651/*
c9def1b8 2652 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2653 * codes. Returns number of bytes used or zero to drop the message
2654 * or -1 to forward the message to windows.
374330e2 2655 */
3cf144db 2656static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
32874aea 2657 unsigned char *output)
2658{
374330e2 2659 BYTE keystate[256];
32874aea 2660 int scan, left_alt = 0, key_down, shift_state;
2661 int r, i, code;
2662 unsigned char *p = output;
4eeb7d09 2663 static int alt_sum = 0;
374330e2 2664
00e3ba0f 2665 HKL kbd_layout = GetKeyboardLayout(0);
2666
0c50ef57 2667 static WORD keys[3];
0c50ef57 2668 static int compose_char = 0;
2669 static WPARAM compose_key = 0;
32874aea 2670
c9def1b8 2671 r = GetKeyboardState(keystate);
32874aea 2672 if (!r)
2673 memset(keystate, 0, sizeof(keystate));
2674 else {
ec55b220 2675#if 0
4eeb7d09 2676#define SHOW_TOASCII_RESULT
32874aea 2677 { /* Tell us all about key events */
2678 static BYTE oldstate[256];
2679 static int first = 1;
2680 static int scan;
2681 int ch;
2682 if (first)
2683 memcpy(oldstate, keystate, sizeof(oldstate));
2684 first = 0;
2685
2686 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2687 debug(("+"));
2688 } else if ((HIWORD(lParam) & KF_UP)
2689 && scan == (HIWORD(lParam) & 0xFF)) {
2690 debug((". U"));
2691 } else {
2692 debug((".\n"));
2693 if (wParam >= VK_F1 && wParam <= VK_F20)
2694 debug(("K_F%d", wParam + 1 - VK_F1));
2695 else
2696 switch (wParam) {
2697 case VK_SHIFT:
2698 debug(("SHIFT"));
2699 break;
2700 case VK_CONTROL:
2701 debug(("CTRL"));
2702 break;
2703 case VK_MENU:
2704 debug(("ALT"));
2705 break;
2706 default:
2707 debug(("VK_%02x", wParam));
2708 }
2709 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2710 debug(("*"));
2711 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2712
2713 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2714 if (ch >= ' ' && ch <= '~')
2715 debug((", '%c'", ch));
2716 else if (ch)
2717 debug((", $%02x", ch));
2718
2719 if (keys[0])
2720 debug((", KB0=%02x", keys[0]));
2721 if (keys[1])
2722 debug((", KB1=%02x", keys[1]));
2723 if (keys[2])
2724 debug((", KB2=%02x", keys[2]));
2725
2726 if ((keystate[VK_SHIFT] & 0x80) != 0)
2727 debug((", S"));
2728 if ((keystate[VK_CONTROL] & 0x80) != 0)
2729 debug((", C"));
2730 if ((HIWORD(lParam) & KF_EXTENDED))
2731 debug((", E"));
2732 if ((HIWORD(lParam) & KF_UP))
2733 debug((", U"));
2734 }
2735
2736 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
2737 else if ((HIWORD(lParam) & KF_UP))
2738 oldstate[wParam & 0xFF] ^= 0x80;
2739 else
2740 oldstate[wParam & 0xFF] ^= 0x81;
2741
2742 for (ch = 0; ch < 256; ch++)
2743 if (oldstate[ch] != keystate[ch])
2744 debug((", M%02x=%02x", ch, keystate[ch]));
2745
2746 memcpy(oldstate, keystate, sizeof(oldstate));
2747 }
ec55b220 2748#endif
2749
32874aea 2750 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
2cba1186 2751 keystate[VK_RMENU] = keystate[VK_MENU];
2752 }
2753
c9def1b8 2754
2755 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
32874aea 2756 if ((cfg.funky_type == 3 ||
2757 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
2758 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
c9def1b8 2759
2760 wParam = VK_EXECUTE;
2761
2762 /* UnToggle NUMLock */
32874aea 2763 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
2764 keystate[VK_NUMLOCK] ^= 1;
c9def1b8 2765 }
2766
2767 /* And write back the 'adjusted' state */
32874aea 2768 SetKeyboardState(keystate);
c9def1b8 2769 }
2770
2771 /* Disable Auto repeat if required */
32874aea 2772 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
2773 return 0;
c9def1b8 2774
32874aea 2775 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
c9def1b8 2776 left_alt = 1;
2777
32874aea 2778 key_down = ((HIWORD(lParam) & KF_UP) == 0);
c9def1b8 2779
95bbe1ae 2780 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
32874aea 2781 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
95bbe1ae 2782 if (cfg.ctrlaltkeys)
2783 keystate[VK_MENU] = 0;
2784 else {
2785 keystate[VK_RMENU] = 0x80;
2786 left_alt = 0;
2787 }
2788 }
c9def1b8 2789
5a73255e 2790 alt_pressed = (left_alt && key_down);
2791
c9def1b8 2792 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
32874aea 2793 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
2794 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
374330e2 2795
95bbe1ae 2796 /* Note if AltGr was pressed and if it was used as a compose key */
2797 if (!compose_state) {
159eba53 2798 compose_key = 0x100;
95bbe1ae 2799 if (cfg.compose_key) {
32874aea 2800 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
95bbe1ae 2801 compose_key = wParam;
2802 }
2803 if (wParam == VK_APPS)
2804 compose_key = wParam;
2805 }
2806
32874aea 2807 if (wParam == compose_key) {
2808 if (compose_state == 0
2809 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
2810 1;
2811 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
95bbe1ae 2812 compose_state = 2;
2813 else
2814 compose_state = 0;
32874aea 2815 } else if (compose_state == 1 && wParam != VK_CONTROL)
95bbe1ae 2816 compose_state = 0;
2817
67c339f7 2818 /*
cabfd08c 2819 * Record that we pressed key so the scroll window can be reset, but
2820 * be careful to avoid Shift-UP/Down
2821 */
32874aea 2822 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT) {
2823 seen_key_event = 1;
cabfd08c 2824 }
2825
c9def1b8 2826 /* Make sure we're not pasting */
32874aea 2827 if (key_down)
2828 term_nopaste();
67c339f7 2829
32874aea 2830 if (compose_state > 1 && left_alt)
2831 compose_state = 0;
67c339f7 2832
c9def1b8 2833 /* Sanitize the number pad if not using a PC NumPad */
32874aea 2834 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
2835 && cfg.funky_type != 2)
2836 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
2837 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
c9def1b8 2838 int nParam = 0;
32874aea 2839 switch (wParam) {
2840 case VK_INSERT:
2841 nParam = VK_NUMPAD0;
2842 break;
2843 case VK_END:
2844 nParam = VK_NUMPAD1;
2845 break;
2846 case VK_DOWN:
2847 nParam = VK_NUMPAD2;
2848 break;
2849 case VK_NEXT:
2850 nParam = VK_NUMPAD3;
2851 break;
2852 case VK_LEFT:
2853 nParam = VK_NUMPAD4;
2854 break;
2855 case VK_CLEAR:
2856 nParam = VK_NUMPAD5;
2857 break;
2858 case VK_RIGHT:
2859 nParam = VK_NUMPAD6;
2860 break;
2861 case VK_HOME:
2862 nParam = VK_NUMPAD7;
2863 break;
2864 case VK_UP:
2865 nParam = VK_NUMPAD8;
2866 break;
2867 case VK_PRIOR:
2868 nParam = VK_NUMPAD9;
2869 break;
2870 case VK_DELETE:
2871 nParam = VK_DECIMAL;
2872 break;
c9def1b8 2873 }
32874aea 2874 if (nParam) {
2875 if (keystate[VK_NUMLOCK] & 1)
2876 shift_state |= 1;
c9def1b8 2877 wParam = nParam;
2878 }
25d39ef6 2879 }
2880 }
2881
c9def1b8 2882 /* If a key is pressed and AltGr is not active */
32874aea 2883 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
2884 /* Okay, prepare for most alts then ... */
2885 if (left_alt)
2886 *p++ = '\033';
374330e2 2887
c9def1b8 2888 /* Lets see if it's a pattern we know all about ... */
2889 if (wParam == VK_PRIOR && shift_state == 1) {
32874aea 2890 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
2891 return 0;
c9def1b8 2892 }
2893 if (wParam == VK_NEXT && shift_state == 1) {
32874aea 2894 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
2895 return 0;
2896 }
2897 if (wParam == VK_INSERT && shift_state == 1) {
568dd02f 2898 term_do_paste();
32874aea 2899 return 0;
2900 }
c9def1b8 2901 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
32874aea 2902 return -1;
c9def1b8 2903 }
2904 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
32874aea 2905 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
2906 return -1;
c9def1b8 2907 }
8f57d753 2908 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter) {
2909 flip_full_screen();
2910 return -1;
2911 }
ec55b220 2912 /* Control-Numlock for app-keypad mode switch */
2913 if (wParam == VK_PAUSE && shift_state == 2) {
2914 app_keypad_keys ^= 1;
2915 return 0;
2916 }
374330e2 2917
c9def1b8 2918 /* Nethack keypad */
2919 if (cfg.nethack_keypad && !left_alt) {
32874aea 2920 switch (wParam) {
2921 case VK_NUMPAD1:
2922 *p++ = shift_state ? 'B' : 'b';
2923 return p - output;
2924 case VK_NUMPAD2:
2925 *p++ = shift_state ? 'J' : 'j';
2926 return p - output;
2927 case VK_NUMPAD3:
2928 *p++ = shift_state ? 'N' : 'n';
2929 return p - output;
2930 case VK_NUMPAD4:
2931 *p++ = shift_state ? 'H' : 'h';
2932 return p - output;
2933 case VK_NUMPAD5:
2934 *p++ = shift_state ? '.' : '.';
2935 return p - output;
2936 case VK_NUMPAD6:
2937 *p++ = shift_state ? 'L' : 'l';
2938 return p - output;
2939 case VK_NUMPAD7:
2940 *p++ = shift_state ? 'Y' : 'y';
2941 return p - output;
2942 case VK_NUMPAD8:
2943 *p++ = shift_state ? 'K' : 'k';
2944 return p - output;
2945 case VK_NUMPAD9:
2946 *p++ = shift_state ? 'U' : 'u';
2947 return p - output;
2948 }
c9def1b8 2949 }
2950
2951 /* Application Keypad */
2952 if (!left_alt) {
32874aea 2953 int xkey = 0;
2954
2955 if (cfg.funky_type == 3 ||
2956 (cfg.funky_type <= 1 &&
2957 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
2958 case VK_EXECUTE:
2959 xkey = 'P';
2960 break;
2961 case VK_DIVIDE:
2962 xkey = 'Q';
2963 break;
2964 case VK_MULTIPLY:
2965 xkey = 'R';
2966 break;
2967 case VK_SUBTRACT:
2968 xkey = 'S';
2969 break;
2970 }
2971 if (app_keypad_keys && !cfg.no_applic_k)
2972 switch (wParam) {
2973 case VK_NUMPAD0:
2974 xkey = 'p';
2975 break;
2976 case VK_NUMPAD1:
2977 xkey = 'q';
2978 break;
2979 case VK_NUMPAD2:
2980 xkey = 'r';
2981 break;
2982 case VK_NUMPAD3:
2983 xkey = 's';
2984 break;
2985 case VK_NUMPAD4:
2986 xkey = 't';
2987 break;
2988 case VK_NUMPAD5:
2989 xkey = 'u';
2990 break;
2991 case VK_NUMPAD6:
2992 xkey = 'v';
2993 break;
2994 case VK_NUMPAD7:
2995 xkey = 'w';
2996 break;
2997 case VK_NUMPAD8:
2998 xkey = 'x';
2999 break;
3000 case VK_NUMPAD9:
3001 xkey = 'y';
3002 break;
3003
3004 case VK_DECIMAL:
3005 xkey = 'n';
3006 break;
3007 case VK_ADD:
3008 if (cfg.funky_type == 2) {
3009 if (shift_state)
3010 xkey = 'l';
3011 else
3012 xkey = 'k';
3013 } else if (shift_state)
3014 xkey = 'm';
c9def1b8 3015 else
32874aea 3016 xkey = 'l';
3017 break;
3018
3019 case VK_DIVIDE:
3020 if (cfg.funky_type == 2)
3021 xkey = 'o';
3022 break;
3023 case VK_MULTIPLY:
3024 if (cfg.funky_type == 2)
3025 xkey = 'j';
3026 break;
3027 case VK_SUBTRACT:
3028 if (cfg.funky_type == 2)
3029 xkey = 'm';
3030 break;
3031
3032 case VK_RETURN:
3033 if (HIWORD(lParam) & KF_EXTENDED)
3034 xkey = 'M';
3035 break;
c9def1b8 3036 }
32874aea 3037 if (xkey) {
3038 if (vt52_mode) {
3039 if (xkey >= 'P' && xkey <= 'S')
3040 p += sprintf((char *) p, "\x1B%c", xkey);
3041 else
3042 p += sprintf((char *) p, "\x1B?%c", xkey);
3043 } else
3044 p += sprintf((char *) p, "\x1BO%c", xkey);
3045 return p - output;
c9def1b8 3046 }
3047 }
3048
32874aea 3049 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
c9def1b8 3050 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
a5f3e637 3051 *p++ = 0;
3052 return -2;
c9def1b8 3053 }
32874aea 3054 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3055 *p++ = 0x1B;
3056 *p++ = '[';
3057 *p++ = 'Z';
3058 return p - output;
c9def1b8 3059 }
32874aea 3060 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3061 *p++ = 0;
3062 return p - output;
c9def1b8 3063 }
32874aea 3064 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3065 *p++ = 160;
3066 return p - output;
c9def1b8 3067 }
32874aea 3068 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3069 *p++ = 3;
a5f3e637 3070 *p++ = 0;
3071 return -2;
c9def1b8 3072 }
32874aea 3073 if (wParam == VK_PAUSE) { /* Break/Pause */
3074 *p++ = 26;
3075 *p++ = 0;
3076 return -2;
95bbe1ae 3077 }
c9def1b8 3078 /* Control-2 to Control-8 are special */
32874aea 3079 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3080 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
c9def1b8 3081 return p - output;
3082 }
3083 if (shift_state == 2 && wParam == 0xBD) {
3084 *p++ = 0x1F;
3085 return p - output;
3086 }
3087 if (shift_state == 2 && wParam == 0xDF) {
3088 *p++ = 0x1C;
3089 return p - output;
3090 }
3091 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
32874aea 3092 *p++ = '\r';
3093 *p++ = '\n';
c9def1b8 3094 return p - output;
3095 }
374330e2 3096
c5e9c988 3097 /*
c9def1b8 3098 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3099 * for integer decimal nn.)
3100 *
3101 * We also deal with the weird ones here. Linux VCs replace F1
3102 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3103 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3104 * respectively.
c5e9c988 3105 */
c9def1b8 3106 code = 0;
3107 switch (wParam) {
32874aea 3108 case VK_F1:
3109 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3110 break;
3111 case VK_F2:
3112 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3113 break;
3114 case VK_F3:
3115 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3116 break;
3117 case VK_F4:
3118 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3119 break;
3120 case VK_F5:
3121 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3122 break;
3123 case VK_F6:
3124 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3125 break;
3126 case VK_F7:
3127 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3128 break;
3129 case VK_F8:
3130 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3131 break;
3132 case VK_F9:
3133 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3134 break;
3135 case VK_F10:
3136 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3137 break;
3138 case VK_F11:
3139 code = 23;
3140 break;
3141 case VK_F12:
3142 code = 24;
3143 break;
3144 case VK_F13:
3145 code = 25;
3146 break;
3147 case VK_F14:
3148 code = 26;
3149 break;
3150 case VK_F15:
3151 code = 28;
3152 break;
3153 case VK_F16:
3154 code = 29;
3155 break;
3156 case VK_F17:
3157 code = 31;
3158 break;
3159 case VK_F18:
3160 code = 32;
3161 break;
3162 case VK_F19:
3163 code = 33;
3164 break;
3165 case VK_F20:
3166 code = 34;
3167 break;
dfca2656 3168 }
3169 if ((shift_state&2) == 0) switch (wParam) {
32874aea 3170 case VK_HOME:
3171 code = 1;
3172 break;
3173 case VK_INSERT:
3174 code = 2;
3175 break;
3176 case VK_DELETE:
3177 code = 3;
3178 break;
3179 case VK_END:
3180 code = 4;
3181 break;
3182 case VK_PRIOR:
3183 code = 5;
3184 break;
3185 case VK_NEXT:
3186 code = 6;
3187 break;
374330e2 3188 }
ec55b220 3189 /* Reorder edit keys to physical order */
32874aea 3190 if (cfg.funky_type == 3 && code <= 6)
3191 code = "\0\2\1\4\5\3\6"[code];
ec55b220 3192
f37caa11 3193 if (vt52_mode && code > 0 && code <= 6) {
32874aea 3194 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
f37caa11 3195 return p - output;
3196 }
3197
9bc81a2c 3198 if (cfg.funky_type == 5 && /* SCO function keys */
3199 code >= 11 && code <= 34) {
e24b1972 3200 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3201 int index = 0;
3202 switch (wParam) {
3203 case VK_F1: index = 0; break;
3204 case VK_F2: index = 1; break;
3205 case VK_F3: index = 2; break;
3206 case VK_F4: index = 3; break;
3207 case VK_F5: index = 4; break;
3208 case VK_F6: index = 5; break;
3209 case VK_F7: index = 6; break;
3210 case VK_F8: index = 7; break;
3211 case VK_F9: index = 8; break;
3212 case VK_F10: index = 9; break;
3213 case VK_F11: index = 10; break;
3214 case VK_F12: index = 11; break;
3215 }
3216 if (keystate[VK_SHIFT] & 0x80) index += 12;
3217 if (keystate[VK_CONTROL] & 0x80) index += 24;
3218 p += sprintf((char *) p, "\x1B[%c", codes[index]);
f37caa11 3219 return p - output;
3220 }
9bc81a2c 3221 if (cfg.funky_type == 5 && /* SCO small keypad */
3222 code >= 1 && code <= 6) {
3223 char codes[] = "HL.FIG";
3224 if (code == 3) {
3225 *p++ = '\x7F';
3226 } else {
3227 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3228 }
3229 return p - output;
3230 }
f37caa11 3231 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3232 int offt = 0;
32874aea 3233 if (code > 15)
3234 offt++;
3235 if (code > 21)
3236 offt++;
f37caa11 3237 if (vt52_mode)
32874aea 3238 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
f37caa11 3239 else
32874aea 3240 p +=
3241 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
f37caa11 3242 return p - output;
3243 }
c9def1b8 3244 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
32874aea 3245 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
c9def1b8 3246 return p - output;
3247 }
3248 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
ec55b220 3249 if (vt52_mode)
32874aea 3250 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
ec55b220 3251 else
32874aea 3252 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
c9def1b8 3253 return p - output;
3254 }
3255 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
32874aea 3256 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
c9def1b8 3257 return p - output;
3258 }
3259 if (code) {
32874aea 3260 p += sprintf((char *) p, "\x1B[%d~", code);
374330e2 3261 return p - output;
374330e2 3262 }
45dabbc5 3263
c9def1b8 3264 /*
3265 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3266 * some reason seems to send VK_CLEAR to Windows...).
3267 */
3268 {
3269 char xkey = 0;
3270 switch (wParam) {
32874aea 3271 case VK_UP:
3272 xkey = 'A';
3273 break;
3274 case VK_DOWN:
3275 xkey = 'B';
3276 break;
3277 case VK_RIGHT:
3278 xkey = 'C';
3279 break;
3280 case VK_LEFT:
3281 xkey = 'D';
3282 break;
3283 case VK_CLEAR:
3284 xkey = 'G';
3285 break;
c9def1b8 3286 }
32874aea 3287 if (xkey) {
c9def1b8 3288 if (vt52_mode)
32874aea 3289 p += sprintf((char *) p, "\x1B%c", xkey);
e864f84f 3290 else {
3291 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3292 /* VT100 & VT102 manuals both state the app cursor keys
3293 * only work if the app keypad is on.
3294 */
3295 if (!app_keypad_keys)
3296 app_flg = 0;
3297 /* Useful mapping of Ctrl-arrows */
3298 if (shift_state == 2)
3299 app_flg = !app_flg;
3300
3301 if (app_flg)
3302 p += sprintf((char *) p, "\x1BO%c", xkey);
3303 else
3304 p += sprintf((char *) p, "\x1B[%c", xkey);
3305 }
c9def1b8 3306 return p - output;
3307 }
3308 }
0c50ef57 3309
3310 /*
3311 * Finally, deal with Return ourselves. (Win95 seems to
3312 * foul it up when Alt is pressed, for some reason.)
3313 */
32874aea 3314 if (wParam == VK_RETURN) { /* Return */
0c50ef57 3315 *p++ = 0x0D;
a5f3e637 3316 *p++ = 0;
3317 return -2;
0c50ef57 3318 }
4eeb7d09 3319
3320 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3321 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3322 else
3323 alt_sum = 0;
67c339f7 3324 }
374330e2 3325
c9def1b8 3326 /* Okay we've done everything interesting; let windows deal with
3327 * the boring stuff */
3328 {
a9c02454 3329 BOOL capsOn=0;
3330
3331 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3332 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3333 capsOn= !left_alt;
3334 keystate[VK_CAPITAL] = 0;
3335 }
3336
00e3ba0f 3337 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
4eeb7d09 3338#ifdef SHOW_TOASCII_RESULT
3339 if (r == 1 && !key_down) {
3340 if (alt_sum) {
8f22582c 3341 if (in_utf || dbcs_screenfont)
4eeb7d09 3342 debug((", (U+%04x)", alt_sum));
3343 else
3344 debug((", LCH(%d)", alt_sum));
3345 } else {
3346 debug((", ACH(%d)", keys[0]));
3347 }
3348 } else if (r > 0) {
3349 int r1;
3350 debug((", ASC("));
3351 for (r1 = 0; r1 < r; r1++) {
3352 debug(("%s%d", r1 ? "," : "", keys[r1]));
3353 }
3354 debug((")"));
3355 }
3356#endif
32874aea 3357 if (r > 0) {
4eeb7d09 3358 WCHAR keybuf;
c9def1b8 3359 p = output;
32874aea 3360 for (i = 0; i < r; i++) {
3361 unsigned char ch = (unsigned char) keys[i];
14963b8f 3362
32874aea 3363 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3364 compose_char = ch;
32874aea 3365 compose_state++;
c9def1b8 3366 continue;
3367 }
32874aea 3368 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
c9def1b8 3369 int nc;
3370 compose_state = 0;
3371
32874aea 3372 if ((nc = check_compose(compose_char, ch)) == -1) {
fe50e814 3373 MessageBeep(MB_ICONHAND);
c9def1b8 3374 return 0;
3375 }
4eeb7d09 3376 keybuf = nc;
760e88b2 3377 luni_send(&keybuf, 1, 1);
4eeb7d09 3378 continue;
c9def1b8 3379 }
374330e2 3380
c9def1b8 3381 compose_state = 0;
374330e2 3382
4eeb7d09 3383 if (!key_down) {
3384 if (alt_sum) {
8f22582c 3385 if (in_utf || dbcs_screenfont) {
4eeb7d09 3386 keybuf = alt_sum;
760e88b2 3387 luni_send(&keybuf, 1, 1);
4eeb7d09 3388 } else {
3389 ch = (char) alt_sum;
5471d09a 3390 /*
3391 * We need not bother about stdin
3392 * backlogs here, because in GUI PuTTY
3393 * we can't do anything about it
3394 * anyway; there's no means of asking
3395 * Windows to hold off on KEYDOWN
3396 * messages. We _have_ to buffer
3397 * everything we're sent.
3398 */
760e88b2 3399 ldisc_send(&ch, 1, 1);
4eeb7d09 3400 }
3401 alt_sum = 0;
3402 } else
760e88b2 3403 lpage_send(kbd_codepage, &ch, 1, 1);
4eeb7d09 3404 } else {
a9c02454 3405 if(capsOn && ch < 0x80) {
3406 WCHAR cbuf[2];
3407 cbuf[0] = 27;
3408 cbuf[1] = xlat_uskbd2cyrllic(ch);
760e88b2 3409 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3410 } else {
3411 char cbuf[2];
3412 cbuf[0] = '\033';
3413 cbuf[1] = ch;
760e88b2 3414 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
a9c02454 3415 }
c9def1b8 3416 }
bca9517a 3417 show_mouseptr(0);
c9def1b8 3418 }
374330e2 3419
c9def1b8 3420 /* This is so the ALT-Numpad and dead keys work correctly. */
3421 keys[0] = 0;
3422
32874aea 3423 return p - output;
c9def1b8 3424 }
159eba53 3425 /* If we're definitly not building up an ALT-54321 then clear it */
32874aea 3426 if (!left_alt)
3427 keys[0] = 0;
4eeb7d09 3428 /* If we will be using alt_sum fix the 256s */
8f22582c 3429 else if (keys[0] && (in_utf || dbcs_screenfont))
4eeb7d09 3430 keys[0] = 10;
374330e2 3431 }
3432
dfca2656 3433 /*
3434 * ALT alone may or may not want to bring up the System menu.
3435 * If it's not meant to, we return 0 on presses or releases of
3436 * ALT, to show that we've swallowed the keystroke. Otherwise
3437 * we return -1, which means Windows will give the keystroke
3438 * its default handling (i.e. bring up the System menu).
3439 */
3440 if (wParam == VK_MENU && !cfg.alt_only)
3441 return 0;
374330e2 3442
c9def1b8 3443 return -1;
374330e2 3444}
3445
32874aea 3446void set_title(char *title)
3447{
3448 sfree(window_name);
3449 window_name = smalloc(1 + strlen(title));
3450 strcpy(window_name, title);
37508af4 3451 if (cfg.win_name_always || !IsIconic(hwnd))
32874aea 3452 SetWindowText(hwnd, title);
374330e2 3453}
3454
32874aea 3455void set_icon(char *title)
3456{
3457 sfree(icon_name);
3458 icon_name = smalloc(1 + strlen(title));
3459 strcpy(icon_name, title);
37508af4 3460 if (!cfg.win_name_always && IsIconic(hwnd))
32874aea 3461 SetWindowText(hwnd, title);
374330e2 3462}
3463
32874aea 3464void set_sbar(int total, int start, int page)
3465{
374330e2 3466 SCROLLINFO si;
c9def1b8 3467
32874aea 3468 if (!cfg.scrollbar)
3469 return;
c9def1b8 3470
374330e2 3471 si.cbSize = sizeof(si);
c9def1b8 3472 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
374330e2 3473 si.nMin = 0;
3474 si.nMax = total - 1;
3475 si.nPage = page;
3476 si.nPos = start;
c1f5f956 3477 if (hwnd)
32874aea 3478 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
374330e2 3479}
3480
32874aea 3481Context get_ctx(void)
3482{
374330e2 3483 HDC hdc;
3484 if (hwnd) {
32874aea 3485 hdc = GetDC(hwnd);
374330e2 3486 if (hdc && pal)
32874aea 3487 SelectPalette(hdc, pal, FALSE);
374330e2 3488 return hdc;
3489 } else
3490 return NULL;
3491}
3492
32874aea 3493void free_ctx(Context ctx)
3494{
3495 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3496 ReleaseDC(hwnd, ctx);
374330e2 3497}
3498
32874aea 3499static void real_palette_set(int n, int r, int g, int b)
3500{
374330e2 3501 if (pal) {
3502 logpal->palPalEntry[n].peRed = r;
3503 logpal->palPalEntry[n].peGreen = g;
3504 logpal->palPalEntry[n].peBlue = b;
3505 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3506 colours[n] = PALETTERGB(r, g, b);
32874aea 3507 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3508 } else
3509 colours[n] = RGB(r, g, b);
3510}
3511
32874aea 3512void palette_set(int n, int r, int g, int b)
3513{
374330e2 3514 static const int first[21] = {
3515 0, 2, 4, 6, 8, 10, 12, 14,
3516 1, 3, 5, 7, 9, 11, 13, 15,
3517 16, 17, 18, 20, 22
3518 };
32874aea 3519 real_palette_set(first[n], r, g, b);
374330e2 3520 if (first[n] >= 18)
32874aea 3521 real_palette_set(first[n] + 1, r, g, b);
374330e2 3522 if (pal) {
3523 HDC hdc = get_ctx();
32874aea 3524 UnrealizeObject(pal);
3525 RealizePalette(hdc);
3526 free_ctx(hdc);
374330e2 3527 }
3528}
3529
32874aea 3530void palette_reset(void)
3531{
374330e2 3532 int i;
3533
3534 for (i = 0; i < NCOLOURS; i++) {
3535 if (pal) {
3536 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3537 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3538 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3539 logpal->palPalEntry[i].peFlags = 0;
3540 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3541 defpal[i].rgbtGreen,
3542 defpal[i].rgbtBlue);
3543 } else
3544 colours[i] = RGB(defpal[i].rgbtRed,
32874aea 3545 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
374330e2 3546 }
3547
3548 if (pal) {
3549 HDC hdc;
32874aea 3550 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
374330e2 3551 hdc = get_ctx();
32874aea 3552 RealizePalette(hdc);
3553 free_ctx(hdc);
374330e2 3554 }
3555}
3556
4eeb7d09 3557void write_aclip(char *data, int len, int must_deselect)
32874aea 3558{
374330e2 3559 HGLOBAL clipdata;
3560 void *lock;
3561
32874aea 3562 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
374330e2 3563 if (!clipdata)
3564 return;
32874aea 3565 lock = GlobalLock(clipdata);
374330e2 3566 if (!lock)
3567 return;
32874aea 3568 memcpy(lock, data, len);
3569 ((unsigned char *) lock)[len] = 0;
3570 GlobalUnlock(clipdata);
374330e2 3571
f0df44da 3572 if (!must_deselect)
32874aea 3573 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
f0df44da 3574
32874aea 3575 if (OpenClipboard(hwnd)) {
374330e2 3576 EmptyClipboard();
32874aea 3577 SetClipboardData(CF_TEXT, clipdata);
374330e2 3578 CloseClipboard();
3579 } else
32874aea 3580 GlobalFree(clipdata);
f0df44da 3581
3582 if (!must_deselect)
32874aea 3583 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
374330e2 3584}
3585
4eeb7d09 3586/*
3587 * Note: unlike write_aclip() this will not append a nul.
3588 */
3589void write_clip(wchar_t * data, int len, int must_deselect)
3590{
3591 HGLOBAL clipdata;
3592 HGLOBAL clipdata2;
3593 int len2;
3594 void *lock, *lock2;
3595
3596 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3597
3598 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3599 len * sizeof(wchar_t));
3600 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3601
3602 if (!clipdata || !clipdata2) {
3603 if (clipdata)
3604 GlobalFree(clipdata);
3605 if (clipdata2)
3606 GlobalFree(clipdata2);
3607 return;
3608 }
3609 if (!(lock = GlobalLock(clipdata)))
3610 return;
3611 if (!(lock2 = GlobalLock(clipdata2)))
3612 return;
3613
3614 memcpy(lock, data, len * sizeof(wchar_t));
3615 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3616
3617 GlobalUnlock(clipdata);
3618 GlobalUnlock(clipdata2);
3619
3620 if (!must_deselect)
3621 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3622
3623 if (OpenClipboard(hwnd)) {
3624 EmptyClipboard();
3625 SetClipboardData(CF_UNICODETEXT, clipdata);
3626 SetClipboardData(CF_TEXT, clipdata2);
3627 CloseClipboard();
3628 } else {
3629 GlobalFree(clipdata);
3630 GlobalFree(clipdata2);
3631 }
3632
3633 if (!must_deselect)
3634 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3635}
3636
3637void get_clip(wchar_t ** p, int *len)
32874aea 3638{
374330e2 3639 static HGLOBAL clipdata = NULL;
4eeb7d09 3640 static wchar_t *converted = 0;
3641 wchar_t *p2;
374330e2 3642
4eeb7d09 3643 if (converted) {
3644 sfree(converted);
3645 converted = 0;
3646 }
374330e2 3647 if (!p) {
3648 if (clipdata)
32874aea 3649 GlobalUnlock(clipdata);
374330e2 3650 clipdata = NULL;
3651 return;
4eeb7d09 3652 } else if (OpenClipboard(NULL)) {
2d466ffd 3653 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
374330e2 3654 CloseClipboard();
4eeb7d09 3655 *p = GlobalLock(clipdata);
3656 if (*p) {
3657 for (p2 = *p; *p2; p2++);
3658 *len = p2 - *p;
3659 return;
374330e2 3660 }
2d466ffd 3661 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4eeb7d09 3662 char *s;
3663 int i;
3664 CloseClipboard();
3665 s = GlobalLock(clipdata);
3666 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
3667 *p = converted = smalloc(i * sizeof(wchar_t));
3668 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
3669 *len = i - 1;
3670 return;
3671 } else
3672 CloseClipboard();
374330e2 3673 }
3674
3675 *p = NULL;
3676 *len = 0;
3677}
3678
4eeb7d09 3679#if 0
374330e2 3680/*
3681 * Move `lines' lines from position `from' to position `to' in the
3682 * window.
3683 */
32874aea 3684void optimised_move(int to, int from, int lines)
3685{
374330e2 3686 RECT r;
f67b4e85 3687 int min, max;
374330e2 3688
3689 min = (to < from ? to : from);
3690 max = to + from - min;
374330e2 3691
5a73255e 3692 r.left = offset_width;
3693 r.right = offset_width + cols * font_width;
3694 r.top = offset_height + min * font_height;
3695 r.bottom = offset_height + (max + lines) * font_height;
32874aea 3696 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
374330e2 3697}
4eeb7d09 3698#endif
374330e2 3699
3700/*
3701 * Print a message box and perform a fatal exit.
3702 */
32874aea 3703void fatalbox(char *fmt, ...)
3704{
374330e2 3705 va_list ap;
3706 char stuff[200];
3707
3708 va_start(ap, fmt);
3709 vsprintf(stuff, fmt, ap);
3710 va_end(ap);
3711 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
3712 exit(1);
3713}
3714
3715/*
f8a28d1f 3716 * Manage window caption / taskbar flashing, if enabled.
3717 * 0 = stop, 1 = maintain, 2 = start
3718 */
3719static void flash_window(int mode)
3720{
3721 static long last_flash = 0;
3722 static int flashing = 0;
3723 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
3724 /* stop */
3725 if (flashing) {
3726 FlashWindow(hwnd, FALSE);
3727 flashing = 0;
3728 }
3729
3730 } else if (mode == 2) {
3731 /* start */
3732 if (!flashing) {
3733 last_flash = GetTickCount();
3734 flashing = 1;
3735 FlashWindow(hwnd, TRUE);
3736 }
3737
3738 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
3739 /* maintain */
3740 if (flashing) {
3741 long now = GetTickCount();
3742 long fdiff = now - last_flash;
3743 if (fdiff < 0 || fdiff > 450) {
3744 last_flash = now;
3745 FlashWindow(hwnd, TRUE); /* toggle */
3746 }
3747 }
3748 }
3749}
3750
3751/*
374330e2 3752 * Beep.
3753 */
32874aea 3754void beep(int mode)
3755{
03169ad0 3756 if (mode == BELL_DEFAULT) {
eb04402e 3757 /*
3758 * For MessageBeep style bells, we want to be careful of
3759 * timing, because they don't have the nice property of
3760 * PlaySound bells that each one cancels the previous
3761 * active one. So we limit the rate to one per 50ms or so.
3762 */
3763 static long lastbeep = 0;
d51cdf1e 3764 long beepdiff;
eb04402e 3765
d51cdf1e 3766 beepdiff = GetTickCount() - lastbeep;
eb04402e 3767 if (beepdiff >= 0 && beepdiff < 50)
3768 return;
156686ef 3769 MessageBeep(MB_OK);
d51cdf1e 3770 /*
3771 * The above MessageBeep call takes time, so we record the
3772 * time _after_ it finishes rather than before it starts.
3773 */
3774 lastbeep = GetTickCount();
03169ad0 3775 } else if (mode == BELL_WAVEFILE) {
3776 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
32874aea 3777 char buf[sizeof(cfg.bell_wavefile) + 80];
03169ad0 3778 sprintf(buf, "Unable to play sound file\n%s\n"
3779 "Using default sound instead", cfg.bell_wavefile);
32874aea 3780 MessageBox(hwnd, buf, "PuTTY Sound Error",
3781 MB_OK | MB_ICONEXCLAMATION);
03169ad0 3782 cfg.beep = BELL_DEFAULT;
3783 }
3784 }
f8a28d1f 3785 /* Otherwise, either visual bell or disabled; do nothing here */
3786 if (!has_focus) {
3787 flash_window(2); /* start */
3788 }
374330e2 3789}
8f57d753 3790
3791/*
3792 * Toggle full screen mode. Thanks to cwis@nerim.fr for the
3793 * implementation.
3794 */
3795static void flip_full_screen(void)
3796{
3797 if (!full_screen) {
3798 int cx, cy;
3799
3800 cx = GetSystemMetrics(SM_CXSCREEN);
3801 cy = GetSystemMetrics(SM_CYSCREEN);
3802 GetWindowPlacement(hwnd, &old_wind_placement);
3803 old_wind_style = GetWindowLong(hwnd, GWL_STYLE);
3804 SetWindowLong(hwnd, GWL_STYLE,
3805 old_wind_style & ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME));
3806 SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, SWP_SHOWWINDOW);
3807 full_screen = 1;
3808 } else {
3809 SetWindowLong(hwnd, GWL_STYLE, old_wind_style);
3810 SetWindowPlacement(hwnd,&old_wind_placement);
3811 full_screen = 0;
3812 }
3813}