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