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