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