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