49a7893b8f29a60d77b5813101845380b13b0dc0
[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 GetCapture() == hwnd) {
1942 Mouse_Button b;
1943 if (wParam & MK_LBUTTON)
1944 b = MBT_LEFT;
1945 else if (wParam & MK_MBUTTON)
1946 b = MBT_MIDDLE;
1947 else
1948 b = MBT_RIGHT;
1949 term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
1950 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
1951 wParam & MK_CONTROL, is_alt_pressed());
1952 }
1953 return 0;
1954 case WM_NCMOUSEMOVE:
1955 show_mouseptr(1);
1956 noise_ultralight(lParam);
1957 return 0;
1958 case WM_IGNORE_CLIP:
1959 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
1960 break;
1961 case WM_DESTROYCLIPBOARD:
1962 if (!ignore_clip)
1963 term_deselect();
1964 ignore_clip = FALSE;
1965 return 0;
1966 case WM_PAINT:
1967 {
1968 PAINTSTRUCT p;
1969 HideCaret(hwnd);
1970 hdc = BeginPaint(hwnd, &p);
1971 if (pal) {
1972 SelectPalette(hdc, pal, TRUE);
1973 RealizePalette(hdc);
1974 }
1975 term_paint(hdc,
1976 (p.rcPaint.left-offset_width)/font_width,
1977 (p.rcPaint.top-offset_height)/font_height,
1978 (p.rcPaint.right-offset_width-1)/font_width,
1979 (p.rcPaint.bottom-offset_height-1)/font_height);
1980
1981 if (p.fErase ||
1982 p.rcPaint.left < offset_width ||
1983 p.rcPaint.top < offset_height ||
1984 p.rcPaint.right >= offset_width + font_width*cols ||
1985 p.rcPaint.bottom>= offset_height + font_height*rows)
1986 {
1987 HBRUSH fillcolour, oldbrush;
1988 HPEN edge, oldpen;
1989 fillcolour = CreateSolidBrush (
1990 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1991 oldbrush = SelectObject(hdc, fillcolour);
1992 edge = CreatePen(PS_SOLID, 0,
1993 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
1994 oldpen = SelectObject(hdc, edge);
1995
1996 ExcludeClipRect(hdc,
1997 offset_width, offset_height,
1998 offset_width+font_width*cols,
1999 offset_height+font_height*rows);
2000
2001 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2002 p.rcPaint.right, p.rcPaint.bottom);
2003
2004 // SelectClipRgn(hdc, NULL);
2005
2006 SelectObject(hdc, oldbrush);
2007 DeleteObject(fillcolour);
2008 SelectObject(hdc, oldpen);
2009 DeleteObject(edge);
2010 }
2011 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2012 SelectObject(hdc, GetStockObject(WHITE_PEN));
2013 EndPaint(hwnd, &p);
2014 ShowCaret(hwnd);
2015 }
2016 return 0;
2017 case WM_NETEVENT:
2018 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2019 * but the only one that's likely to try to overload us is FD_READ.
2020 * This means buffering just one is fine.
2021 */
2022 if (pending_netevent)
2023 enact_pending_netevent();
2024
2025 pending_netevent = TRUE;
2026 pend_netevent_wParam = wParam;
2027 pend_netevent_lParam = lParam;
2028 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2029 enact_pending_netevent();
2030
2031 time(&last_movement);
2032 return 0;
2033 case WM_SETFOCUS:
2034 has_focus = TRUE;
2035 CreateCaret(hwnd, caretbm, font_width, font_height);
2036 ShowCaret(hwnd);
2037 flash_window(0); /* stop */
2038 compose_state = 0;
2039 term_out();
2040 term_update();
2041 break;
2042 case WM_KILLFOCUS:
2043 show_mouseptr(1);
2044 has_focus = FALSE;
2045 DestroyCaret();
2046 caret_x = caret_y = -1; /* ensure caret is replaced next time */
2047 term_out();
2048 term_update();
2049 break;
2050 case WM_ENTERSIZEMOVE:
2051 #ifdef RDB_DEBUG_PATCH
2052 debug((27, "WM_ENTERSIZEMOVE"));
2053 #endif
2054 EnableSizeTip(1);
2055 resizing = TRUE;
2056 need_backend_resize = FALSE;
2057 break;
2058 case WM_EXITSIZEMOVE:
2059 EnableSizeTip(0);
2060 resizing = FALSE;
2061 #ifdef RDB_DEBUG_PATCH
2062 debug((27, "WM_EXITSIZEMOVE"));
2063 #endif
2064 if (need_backend_resize) {
2065 term_size(cfg.height, cfg.width, cfg.savelines);
2066 InvalidateRect(hwnd, NULL, TRUE);
2067 }
2068 break;
2069 case WM_SIZING:
2070 /*
2071 * This does two jobs:
2072 * 1) Keep the sizetip uptodate
2073 * 2) Make sure the window size is _stepped_ in units of the font size.
2074 */
2075 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2076 int width, height, w, h, ew, eh;
2077 LPRECT r = (LPRECT) lParam;
2078
2079 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2080 (cfg.height != rows || cfg.width != cols )) {
2081 /*
2082 * Great! It seems that both the terminal size and the
2083 * font size have been changed and the user is now dragging.
2084 *
2085 * It will now be difficult to get back to the configured
2086 * font size!
2087 *
2088 * This would be easier but it seems to be too confusing.
2089
2090 term_size(cfg.height, cfg.width, cfg.savelines);
2091 reset_window(2);
2092 */
2093 cfg.height=rows; cfg.width=cols;
2094
2095 InvalidateRect(hwnd, NULL, TRUE);
2096 need_backend_resize = TRUE;
2097 }
2098
2099 width = r->right - r->left - extra_width;
2100 height = r->bottom - r->top - extra_height;
2101 w = (width + font_width / 2) / font_width;
2102 if (w < 1)
2103 w = 1;
2104 h = (height + font_height / 2) / font_height;
2105 if (h < 1)
2106 h = 1;
2107 UpdateSizeTip(hwnd, w, h);
2108 ew = width - w * font_width;
2109 eh = height - h * font_height;
2110 if (ew != 0) {
2111 if (wParam == WMSZ_LEFT ||
2112 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2113 r->left += ew;
2114 else
2115 r->right -= ew;
2116 }
2117 if (eh != 0) {
2118 if (wParam == WMSZ_TOP ||
2119 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2120 r->top += eh;
2121 else
2122 r->bottom -= eh;
2123 }
2124 if (ew || eh)
2125 return 1;
2126 else
2127 return 0;
2128 } else {
2129 int width, height, w, h, rv = 0;
2130 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2131 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2132 LPRECT r = (LPRECT) lParam;
2133
2134 width = r->right - r->left - ex_width;
2135 height = r->bottom - r->top - ex_height;
2136
2137 w = (width + cols/2)/cols;
2138 h = (height + rows/2)/rows;
2139 if ( r->right != r->left + w*cols + ex_width)
2140 rv = 1;
2141
2142 if (wParam == WMSZ_LEFT ||
2143 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2144 r->left = r->right - w*cols - ex_width;
2145 else
2146 r->right = r->left + w*cols + ex_width;
2147
2148 if (r->bottom != r->top + h*rows + ex_height)
2149 rv = 1;
2150
2151 if (wParam == WMSZ_TOP ||
2152 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2153 r->top = r->bottom - h*rows - ex_height;
2154 else
2155 r->bottom = r->top + h*rows + ex_height;
2156
2157 return rv;
2158 }
2159 /* break; (never reached) */
2160 case WM_FULLSCR_ON_MAX:
2161 fullscr_on_max = TRUE;
2162 break;
2163 case WM_MOVE:
2164 sys_cursor_update();
2165 break;
2166 case WM_SIZE:
2167 #ifdef RDB_DEBUG_PATCH
2168 debug((27, "WM_SIZE %s (%d,%d)",
2169 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2170 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2171 (wParam == SIZE_RESTORED && resizing) ? "to":
2172 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2173 "...",
2174 LOWORD(lParam), HIWORD(lParam)));
2175 #endif
2176 if (wParam == SIZE_MINIMIZED)
2177 SetWindowText(hwnd,
2178 cfg.win_name_always ? window_name : icon_name);
2179 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2180 SetWindowText(hwnd, window_name);
2181 if (wParam == SIZE_RESTORED)
2182 clear_full_screen();
2183 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2184 make_full_screen();
2185 fullscr_on_max = FALSE;
2186 }
2187
2188 if (cfg.resize_action == RESIZE_DISABLED) {
2189 /* A resize, well it better be a minimize. */
2190 reset_window(-1);
2191 } else {
2192
2193 int width, height, w, h;
2194
2195 width = LOWORD(lParam);
2196 height = HIWORD(lParam);
2197
2198 if (!resizing) {
2199 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2200 was_zoomed = 1;
2201 prev_rows = rows;
2202 prev_cols = cols;
2203 if (cfg.resize_action == RESIZE_TERM) {
2204 w = width / font_width;
2205 if (w < 1) w = 1;
2206 h = height / font_height;
2207 if (h < 1) h = 1;
2208
2209 term_size(h, w, cfg.savelines);
2210 }
2211 reset_window(0);
2212 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2213 was_zoomed = 0;
2214 if (cfg.resize_action == RESIZE_TERM)
2215 term_size(prev_rows, prev_cols, cfg.savelines);
2216 if (cfg.resize_action != RESIZE_FONT)
2217 reset_window(2);
2218 else
2219 reset_window(0);
2220 }
2221 /* This is an unexpected resize, these will normally happen
2222 * if the window is too large. Probably either the user
2223 * selected a huge font or the screen size has changed.
2224 *
2225 * This is also called with minimize.
2226 */
2227 else reset_window(-1);
2228 }
2229
2230 /*
2231 * Don't call back->size in mid-resize. (To prevent
2232 * massive numbers of resize events getting sent
2233 * down the connection during an NT opaque drag.)
2234 */
2235 if (resizing) {
2236 if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
2237 need_backend_resize = TRUE;
2238 w = (width-cfg.window_border*2) / font_width;
2239 if (w < 1) w = 1;
2240 h = (height-cfg.window_border*2) / font_height;
2241 if (h < 1) h = 1;
2242
2243 cfg.height = h;
2244 cfg.width = w;
2245 } else
2246 reset_window(0);
2247 }
2248 }
2249 sys_cursor_update();
2250 return 0;
2251 case WM_VSCROLL:
2252 switch (LOWORD(wParam)) {
2253 case SB_BOTTOM:
2254 term_scroll(-1, 0);
2255 break;
2256 case SB_TOP:
2257 term_scroll(+1, 0);
2258 break;
2259 case SB_LINEDOWN:
2260 term_scroll(0, +1);
2261 break;
2262 case SB_LINEUP:
2263 term_scroll(0, -1);
2264 break;
2265 case SB_PAGEDOWN:
2266 term_scroll(0, +rows / 2);
2267 break;
2268 case SB_PAGEUP:
2269 term_scroll(0, -rows / 2);
2270 break;
2271 case SB_THUMBPOSITION:
2272 case SB_THUMBTRACK:
2273 term_scroll(1, HIWORD(wParam));
2274 break;
2275 }
2276 break;
2277 case WM_PALETTECHANGED:
2278 if ((HWND) wParam != hwnd && pal != NULL) {
2279 HDC hdc = get_ctx();
2280 if (hdc) {
2281 if (RealizePalette(hdc) > 0)
2282 UpdateColors(hdc);
2283 free_ctx(hdc);
2284 }
2285 }
2286 break;
2287 case WM_QUERYNEWPALETTE:
2288 if (pal != NULL) {
2289 HDC hdc = get_ctx();
2290 if (hdc) {
2291 if (RealizePalette(hdc) > 0)
2292 UpdateColors(hdc);
2293 free_ctx(hdc);
2294 return TRUE;
2295 }
2296 }
2297 return FALSE;
2298 case WM_KEYDOWN:
2299 case WM_SYSKEYDOWN:
2300 case WM_KEYUP:
2301 case WM_SYSKEYUP:
2302 /*
2303 * Add the scan code and keypress timing to the random
2304 * number noise.
2305 */
2306 noise_ultralight(lParam);
2307
2308 /*
2309 * We don't do TranslateMessage since it disassociates the
2310 * resulting CHAR message from the KEYDOWN that sparked it,
2311 * which we occasionally don't want. Instead, we process
2312 * KEYDOWN, and call the Win32 translator functions so that
2313 * we get the translations under _our_ control.
2314 */
2315 {
2316 unsigned char buf[20];
2317 int len;
2318
2319 if (wParam == VK_PROCESSKEY) {
2320 MSG m;
2321 m.hwnd = hwnd;
2322 m.message = WM_KEYDOWN;
2323 m.wParam = wParam;
2324 m.lParam = lParam & 0xdfff;
2325 TranslateMessage(&m);
2326 } else {
2327 len = TranslateKey(message, wParam, lParam, buf);
2328 if (len == -1)
2329 return DefWindowProc(hwnd, message, wParam, lParam);
2330
2331 if (len != 0) {
2332 /*
2333 * Interrupt an ongoing paste. I'm not sure
2334 * this is sensible, but for the moment it's
2335 * preferable to having to faff about buffering
2336 * things.
2337 */
2338 term_nopaste();
2339
2340 /*
2341 * We need not bother about stdin backlogs
2342 * here, because in GUI PuTTY we can't do
2343 * anything about it anyway; there's no means
2344 * of asking Windows to hold off on KEYDOWN
2345 * messages. We _have_ to buffer everything
2346 * we're sent.
2347 */
2348 ldisc_send(buf, len, 1);
2349 show_mouseptr(0);
2350 }
2351 }
2352 }
2353 net_pending_errors();
2354 return 0;
2355 case WM_INPUTLANGCHANGE:
2356 /* wParam == Font number */
2357 /* lParam == Locale */
2358 set_input_locale((HKL)lParam);
2359 sys_cursor_update();
2360 break;
2361 case WM_IME_NOTIFY:
2362 if(wParam == IMN_SETOPENSTATUS) {
2363 HIMC hImc = ImmGetContext(hwnd);
2364 ImmSetCompositionFont(hImc, &lfont);
2365 ImmReleaseContext(hwnd, hImc);
2366 return 0;
2367 }
2368 break;
2369 case WM_IME_COMPOSITION:
2370 {
2371 HIMC hIMC;
2372 int n;
2373 char *buff;
2374
2375 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2376 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2377
2378 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2379 break; /* fall back to DefWindowProc */
2380
2381 hIMC = ImmGetContext(hwnd);
2382 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2383
2384 if (n > 0) {
2385 int i;
2386 buff = (char*) smalloc(n);
2387 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2388 /*
2389 * Jaeyoun Chung reports that Korean character
2390 * input doesn't work correctly if we do a single
2391 * luni_send() covering the whole of buff. So
2392 * instead we luni_send the characters one by one.
2393 */
2394 for (i = 0; i < n; i += 2)
2395 luni_send((unsigned short *)(buff+i), 1, 1);
2396 free(buff);
2397 }
2398 ImmReleaseContext(hwnd, hIMC);
2399 return 1;
2400 }
2401
2402 case WM_IME_CHAR:
2403 if (wParam & 0xFF00) {
2404 unsigned char buf[2];
2405
2406 buf[1] = wParam;
2407 buf[0] = wParam >> 8;
2408 lpage_send(kbd_codepage, buf, 2, 1);
2409 } else {
2410 char c = (unsigned char) wParam;
2411 lpage_send(kbd_codepage, &c, 1, 1);
2412 }
2413 return (0);
2414 case WM_CHAR:
2415 case WM_SYSCHAR:
2416 /*
2417 * Nevertheless, we are prepared to deal with WM_CHAR
2418 * messages, should they crop up. So if someone wants to
2419 * post the things to us as part of a macro manoeuvre,
2420 * we're ready to cope.
2421 */
2422 {
2423 char c = (unsigned char)wParam;
2424 lpage_send(CP_ACP, &c, 1, 1);
2425 }
2426 return 0;
2427 case WM_SETCURSOR:
2428 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2429 SetCursor(LoadCursor(NULL, IDC_ARROW));
2430 return TRUE;
2431 }
2432 default:
2433 if (message == wm_mousewheel) {
2434 int shift_pressed=0, control_pressed=0, alt_pressed=0;
2435
2436 if (message == WM_MOUSEWHEEL) {
2437 wheel_accumulator += (short)HIWORD(wParam);
2438 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2439 control_pressed=LOWORD(wParam) & MK_CONTROL;
2440 } else {
2441 BYTE keys[256];
2442 wheel_accumulator += (int)wParam;
2443 if (GetKeyboardState(keys)!=0) {
2444 shift_pressed=keys[VK_SHIFT]&0x80;
2445 control_pressed=keys[VK_CONTROL]&0x80;
2446 }
2447 }
2448
2449 /* process events when the threshold is reached */
2450 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2451 int b;
2452
2453 /* reduce amount for next time */
2454 if (wheel_accumulator > 0) {
2455 b = MBT_WHEEL_UP;
2456 wheel_accumulator -= WHEEL_DELTA;
2457 } else if (wheel_accumulator < 0) {
2458 b = MBT_WHEEL_DOWN;
2459 wheel_accumulator += WHEEL_DELTA;
2460 } else
2461 break;
2462
2463 if (send_raw_mouse &&
2464 !(cfg.mouse_override && shift_pressed)) {
2465 /* send a mouse-down followed by a mouse up */
2466 term_mouse(b,
2467 MA_CLICK,
2468 TO_CHR_X(X_POS(lParam)),
2469 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2470 control_pressed, is_alt_pressed());
2471 term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2472 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2473 control_pressed, is_alt_pressed());
2474 } else {
2475 /* trigger a scroll */
2476 term_scroll(0,
2477 b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
2478 }
2479 }
2480 return 0;
2481 }
2482 }
2483
2484 return DefWindowProc(hwnd, message, wParam, lParam);
2485 }
2486
2487 /*
2488 * Move the system caret. (We maintain one, even though it's
2489 * invisible, for the benefit of blind people: apparently some
2490 * helper software tracks the system caret, so we should arrange to
2491 * have one.)
2492 */
2493 void sys_cursor(int x, int y)
2494 {
2495 int cx, cy;
2496
2497 if (!has_focus) return;
2498
2499 /*
2500 * Avoid gratuitously re-updating the cursor position and IMM
2501 * window if there's no actual change required.
2502 */
2503 cx = x * font_width + offset_width;
2504 cy = y * font_height + offset_height;
2505 if (cx == caret_x && cy == caret_y)
2506 return;
2507 caret_x = cx;
2508 caret_y = cy;
2509
2510 sys_cursor_update();
2511 }
2512
2513 static void sys_cursor_update(void)
2514 {
2515 COMPOSITIONFORM cf;
2516 HIMC hIMC;
2517
2518 if (!has_focus) return;
2519
2520 if (caret_x < 0 || caret_y < 0)
2521 return;
2522
2523 SetCaretPos(caret_x, caret_y);
2524
2525 /* IMM calls on Win98 and beyond only */
2526 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2527
2528 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2529 osVersion.dwMinorVersion == 0) return; /* 95 */
2530
2531 /* we should have the IMM functions */
2532 hIMC = ImmGetContext(hwnd);
2533 cf.dwStyle = CFS_POINT;
2534 cf.ptCurrentPos.x = caret_x;
2535 cf.ptCurrentPos.y = caret_y;
2536 ImmSetCompositionWindow(hIMC, &cf);
2537
2538 ImmReleaseContext(hwnd, hIMC);
2539 }
2540
2541 /*
2542 * Draw a line of text in the window, at given character
2543 * coordinates, in given attributes.
2544 *
2545 * We are allowed to fiddle with the contents of `text'.
2546 */
2547 void do_text(Context ctx, int x, int y, char *text, int len,
2548 unsigned long attr, int lattr)
2549 {
2550 COLORREF fg, bg, t;
2551 int nfg, nbg, nfont;
2552 HDC hdc = ctx;
2553 RECT line_box;
2554 int force_manual_underline = 0;
2555 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2556 int char_width = fnt_width;
2557 int text_adjust = 0;
2558 static int *IpDx = 0, IpDxLEN = 0;
2559
2560 if (attr & ATTR_WIDE)
2561 char_width *= 2;
2562
2563 if (len > IpDxLEN || IpDx[0] != char_width) {
2564 int i;
2565 if (len > IpDxLEN) {
2566 sfree(IpDx);
2567 IpDx = smalloc((len + 16) * sizeof(int));
2568 IpDxLEN = (len + 16);
2569 }
2570 for (i = 0; i < IpDxLEN; i++)
2571 IpDx[i] = char_width;
2572 }
2573
2574 /* Only want the left half of double width lines */
2575 if (lattr != LATTR_NORM && x*2 >= cols)
2576 return;
2577
2578 x *= fnt_width;
2579 y *= font_height;
2580 x += offset_width;
2581 y += offset_height;
2582
2583 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
2584 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2585 attr ^= ATTR_CUR_XOR;
2586 }
2587
2588 nfont = 0;
2589 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2590 /* Assume a poorman font is borken in other ways too. */
2591 lattr = LATTR_WIDE;
2592 } else
2593 switch (lattr) {
2594 case LATTR_NORM:
2595 break;
2596 case LATTR_WIDE:
2597 nfont |= FONT_WIDE;
2598 break;
2599 default:
2600 nfont |= FONT_WIDE + FONT_HIGH;
2601 break;
2602 }
2603 if (attr & ATTR_NARROW)
2604 nfont |= FONT_NARROW;
2605
2606 /* Special hack for the VT100 linedraw glyphs. */
2607 if ((attr & CSET_MASK) == 0x2300) {
2608 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2609 switch ((unsigned char) (text[0])) {
2610 case 0xBA:
2611 text_adjust = -2 * font_height / 5;
2612 break;
2613 case 0xBB:
2614 text_adjust = -1 * font_height / 5;
2615 break;
2616 case 0xBC:
2617 text_adjust = font_height / 5;
2618 break;
2619 case 0xBD:
2620 text_adjust = 2 * font_height / 5;
2621 break;
2622 }
2623 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2624 text_adjust *= 2;
2625 attr &= ~CSET_MASK;
2626 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2627 attr |= (unitab_xterm['q'] & CSET_MASK);
2628 if (attr & ATTR_UNDER) {
2629 attr &= ~ATTR_UNDER;
2630 force_manual_underline = 1;
2631 }
2632 }
2633 }
2634
2635 /* Anything left as an original character set is unprintable. */
2636 if (DIRECT_CHAR(attr)) {
2637 attr &= ~CSET_MASK;
2638 attr |= 0xFF00;
2639 memset(text, 0xFD, len);
2640 }
2641
2642 /* OEM CP */
2643 if ((attr & CSET_MASK) == ATTR_OEMCP)
2644 nfont |= FONT_OEM;
2645
2646 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2647 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2648 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2649 nfont |= FONT_BOLD;
2650 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2651 nfont |= FONT_UNDERLINE;
2652 another_font(nfont);
2653 if (!fonts[nfont]) {
2654 if (nfont & FONT_UNDERLINE)
2655 force_manual_underline = 1;
2656 /* Don't do the same for manual bold, it could be bad news. */
2657
2658 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2659 }
2660 another_font(nfont);
2661 if (!fonts[nfont])
2662 nfont = FONT_NORMAL;
2663 if (attr & ATTR_REVERSE) {
2664 t = nfg;
2665 nfg = nbg;
2666 nbg = t;
2667 }
2668 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2669 nfg++;
2670 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2671 nbg++;
2672 fg = colours[nfg];
2673 bg = colours[nbg];
2674 SelectObject(hdc, fonts[nfont]);
2675 SetTextColor(hdc, fg);
2676 SetBkColor(hdc, bg);
2677 SetBkMode(hdc, OPAQUE);
2678 line_box.left = x;
2679 line_box.top = y;
2680 line_box.right = x + char_width * len;
2681 line_box.bottom = y + font_height;
2682
2683 /* Only want the left half of double width lines */
2684 if (line_box.right > font_width*cols+offset_width)
2685 line_box.right = font_width*cols+offset_width;
2686
2687 /* We're using a private area for direct to font. (512 chars.) */
2688 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2689 /* Ho Hum, dbcs fonts are a PITA! */
2690 /* To display on W9x I have to convert to UCS */
2691 static wchar_t *uni_buf = 0;
2692 static int uni_len = 0;
2693 int nlen, mptr;
2694 if (len > uni_len) {
2695 sfree(uni_buf);
2696 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2697 }
2698
2699 for(nlen = mptr = 0; mptr<len; mptr++) {
2700 uni_buf[nlen] = 0xFFFD;
2701 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2702 IpDx[nlen] += char_width;
2703 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2704 text+mptr, 2, uni_buf+nlen, 1);
2705 mptr++;
2706 }
2707 else
2708 {
2709 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2710 text+mptr, 1, uni_buf+nlen, 1);
2711 }
2712 nlen++;
2713 }
2714 if (nlen <= 0)
2715 return; /* Eeek! */
2716
2717 ExtTextOutW(hdc, x,
2718 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2719 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2720 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2721 SetBkMode(hdc, TRANSPARENT);
2722 ExtTextOutW(hdc, x - 1,
2723 y - font_height * (lattr ==
2724 LATTR_BOT) + text_adjust,
2725 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2726 }
2727
2728 IpDx[0] = -1;
2729 } else if (DIRECT_FONT(attr)) {
2730 ExtTextOut(hdc, x,
2731 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2732 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2733 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2734 SetBkMode(hdc, TRANSPARENT);
2735
2736 /* GRR: This draws the character outside it's box and can leave
2737 * 'droppings' even with the clip box! I suppose I could loop it
2738 * one character at a time ... yuk.
2739 *
2740 * Or ... I could do a test print with "W", and use +1 or -1 for this
2741 * shift depending on if the leftmost column is blank...
2742 */
2743 ExtTextOut(hdc, x - 1,
2744 y - font_height * (lattr ==
2745 LATTR_BOT) + text_adjust,
2746 ETO_CLIPPED, &line_box, text, len, IpDx);
2747 }
2748 } else {
2749 /* And 'normal' unicode characters */
2750 static WCHAR *wbuf = NULL;
2751 static int wlen = 0;
2752 int i;
2753 if (wlen < len) {
2754 sfree(wbuf);
2755 wlen = len;
2756 wbuf = smalloc(wlen * sizeof(WCHAR));
2757 }
2758 for (i = 0; i < len; i++)
2759 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2760
2761 ExtTextOutW(hdc, x,
2762 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2763 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2764
2765 /* And the shadow bold hack. */
2766 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2767 SetBkMode(hdc, TRANSPARENT);
2768 ExtTextOutW(hdc, x - 1,
2769 y - font_height * (lattr ==
2770 LATTR_BOT) + text_adjust,
2771 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2772 }
2773 }
2774 if (lattr != LATTR_TOP && (force_manual_underline ||
2775 (und_mode == UND_LINE
2776 && (attr & ATTR_UNDER)))) {
2777 HPEN oldpen;
2778 int dec = descent;
2779 if (lattr == LATTR_BOT)
2780 dec = dec * 2 - font_height;
2781
2782 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2783 MoveToEx(hdc, x, y + dec, NULL);
2784 LineTo(hdc, x + len * char_width, y + dec);
2785 oldpen = SelectObject(hdc, oldpen);
2786 DeleteObject(oldpen);
2787 }
2788 }
2789
2790 void do_cursor(Context ctx, int x, int y, char *text, int len,
2791 unsigned long attr, int lattr)
2792 {
2793
2794 int fnt_width;
2795 int char_width;
2796 HDC hdc = ctx;
2797 int ctype = cfg.cursor_type;
2798
2799 if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
2800 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2801 do_text(ctx, x, y, text, len, attr, lattr);
2802 return;
2803 }
2804 ctype = 2;
2805 attr |= TATTR_RIGHTCURS;
2806 }
2807
2808 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2809 if (attr & ATTR_WIDE)
2810 char_width *= 2;
2811 x *= fnt_width;
2812 y *= font_height;
2813 x += offset_width;
2814 y += offset_height;
2815
2816 if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
2817 POINT pts[5];
2818 HPEN oldpen;
2819 pts[0].x = pts[1].x = pts[4].x = x;
2820 pts[2].x = pts[3].x = x + char_width - 1;
2821 pts[0].y = pts[3].y = pts[4].y = y;
2822 pts[1].y = pts[2].y = y + font_height - 1;
2823 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2824 Polyline(hdc, pts, 5);
2825 oldpen = SelectObject(hdc, oldpen);
2826 DeleteObject(oldpen);
2827 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2828 int startx, starty, dx, dy, length, i;
2829 if (ctype == 1) {
2830 startx = x;
2831 starty = y + descent;
2832 dx = 1;
2833 dy = 0;
2834 length = char_width;
2835 } else {
2836 int xadjust = 0;
2837 if (attr & TATTR_RIGHTCURS)
2838 xadjust = char_width - 1;
2839 startx = x + xadjust;
2840 starty = y;
2841 dx = 0;
2842 dy = 1;
2843 length = font_height;
2844 }
2845 if (attr & TATTR_ACTCURS) {
2846 HPEN oldpen;
2847 oldpen =
2848 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2849 MoveToEx(hdc, startx, starty, NULL);
2850 LineTo(hdc, startx + dx * length, starty + dy * length);
2851 oldpen = SelectObject(hdc, oldpen);
2852 DeleteObject(oldpen);
2853 } else {
2854 for (i = 0; i < length; i++) {
2855 if (i % 2 == 0) {
2856 SetPixel(hdc, startx, starty, colours[23]);
2857 }
2858 startx += dx;
2859 starty += dy;
2860 }
2861 }
2862 }
2863 }
2864
2865 /* This function gets the actual width of a character in the normal font.
2866 */
2867 int CharWidth(Context ctx, int uc) {
2868 HDC hdc = ctx;
2869 int ibuf = 0;
2870
2871 /* If the font max is the same as the font ave width then this
2872 * function is a no-op.
2873 */
2874 if (!font_dualwidth) return 1;
2875
2876 switch (uc & CSET_MASK) {
2877 case ATTR_ASCII:
2878 uc = unitab_line[uc & 0xFF];
2879 break;
2880 case ATTR_LINEDRW:
2881 uc = unitab_xterm[uc & 0xFF];
2882 break;
2883 case ATTR_SCOACS:
2884 uc = unitab_scoacs[uc & 0xFF];
2885 break;
2886 }
2887 if (DIRECT_FONT(uc)) {
2888 if (dbcs_screenfont) return 1;
2889
2890 /* Speedup, I know of no font where ascii is the wrong width */
2891 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
2892 return 1;
2893
2894 if ( (uc & CSET_MASK) == ATTR_ACP ) {
2895 SelectObject(hdc, fonts[FONT_NORMAL]);
2896 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
2897 another_font(FONT_OEM);
2898 if (!fonts[FONT_OEM]) return 0;
2899
2900 SelectObject(hdc, fonts[FONT_OEM]);
2901 } else
2902 return 0;
2903
2904 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
2905 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
2906 return 0;
2907 } else {
2908 /* Speedup, I know of no font where ascii is the wrong width */
2909 if (uc >= ' ' && uc <= '~') return 1;
2910
2911 SelectObject(hdc, fonts[FONT_NORMAL]);
2912 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
2913 /* Okay that one worked */ ;
2914 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
2915 /* This should work on 9x too, but it's "less accurate" */ ;
2916 else
2917 return 0;
2918 }
2919
2920 ibuf += font_width / 2 -1;
2921 ibuf /= font_width;
2922
2923 return ibuf;
2924 }
2925
2926 /*
2927 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
2928 * codes. Returns number of bytes used or zero to drop the message
2929 * or -1 to forward the message to windows.
2930 */
2931 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
2932 unsigned char *output)
2933 {
2934 BYTE keystate[256];
2935 int scan, left_alt = 0, key_down, shift_state;
2936 int r, i, code;
2937 unsigned char *p = output;
2938 static int alt_sum = 0;
2939
2940 HKL kbd_layout = GetKeyboardLayout(0);
2941
2942 static WORD keys[3];
2943 static int compose_char = 0;
2944 static WPARAM compose_key = 0;
2945
2946 r = GetKeyboardState(keystate);
2947 if (!r)
2948 memset(keystate, 0, sizeof(keystate));
2949 else {
2950 #if 0
2951 #define SHOW_TOASCII_RESULT
2952 { /* Tell us all about key events */
2953 static BYTE oldstate[256];
2954 static int first = 1;
2955 static int scan;
2956 int ch;
2957 if (first)
2958 memcpy(oldstate, keystate, sizeof(oldstate));
2959 first = 0;
2960
2961 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
2962 debug(("+"));
2963 } else if ((HIWORD(lParam) & KF_UP)
2964 && scan == (HIWORD(lParam) & 0xFF)) {
2965 debug((". U"));
2966 } else {
2967 debug((".\n"));
2968 if (wParam >= VK_F1 && wParam <= VK_F20)
2969 debug(("K_F%d", wParam + 1 - VK_F1));
2970 else
2971 switch (wParam) {
2972 case VK_SHIFT:
2973 debug(("SHIFT"));
2974 break;
2975 case VK_CONTROL:
2976 debug(("CTRL"));
2977 break;
2978 case VK_MENU:
2979 debug(("ALT"));
2980 break;
2981 default:
2982 debug(("VK_%02x", wParam));
2983 }
2984 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
2985 debug(("*"));
2986 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
2987
2988 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
2989 if (ch >= ' ' && ch <= '~')
2990 debug((", '%c'", ch));
2991 else if (ch)
2992 debug((", $%02x", ch));
2993
2994 if (keys[0])
2995 debug((", KB0=%02x", keys[0]));
2996 if (keys[1])
2997 debug((", KB1=%02x", keys[1]));
2998 if (keys[2])
2999 debug((", KB2=%02x", keys[2]));
3000
3001 if ((keystate[VK_SHIFT] & 0x80) != 0)
3002 debug((", S"));
3003 if ((keystate[VK_CONTROL] & 0x80) != 0)
3004 debug((", C"));
3005 if ((HIWORD(lParam) & KF_EXTENDED))
3006 debug((", E"));
3007 if ((HIWORD(lParam) & KF_UP))
3008 debug((", U"));
3009 }
3010
3011 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3012 else if ((HIWORD(lParam) & KF_UP))
3013 oldstate[wParam & 0xFF] ^= 0x80;
3014 else
3015 oldstate[wParam & 0xFF] ^= 0x81;
3016
3017 for (ch = 0; ch < 256; ch++)
3018 if (oldstate[ch] != keystate[ch])
3019 debug((", M%02x=%02x", ch, keystate[ch]));
3020
3021 memcpy(oldstate, keystate, sizeof(oldstate));
3022 }
3023 #endif
3024
3025 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3026 keystate[VK_RMENU] = keystate[VK_MENU];
3027 }
3028
3029
3030 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3031 if ((cfg.funky_type == 3 ||
3032 (cfg.funky_type <= 1 && app_keypad_keys && !cfg.no_applic_k))
3033 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3034
3035 wParam = VK_EXECUTE;
3036
3037 /* UnToggle NUMLock */
3038 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3039 keystate[VK_NUMLOCK] ^= 1;
3040 }
3041
3042 /* And write back the 'adjusted' state */
3043 SetKeyboardState(keystate);
3044 }
3045
3046 /* Disable Auto repeat if required */
3047 if (repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3048 return 0;
3049
3050 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3051 left_alt = 1;
3052
3053 key_down = ((HIWORD(lParam) & KF_UP) == 0);
3054
3055 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3056 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3057 if (cfg.ctrlaltkeys)
3058 keystate[VK_MENU] = 0;
3059 else {
3060 keystate[VK_RMENU] = 0x80;
3061 left_alt = 0;
3062 }
3063 }
3064
3065 alt_pressed = (left_alt && key_down);
3066
3067 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3068 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3069 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3070
3071 /* Note if AltGr was pressed and if it was used as a compose key */
3072 if (!compose_state) {
3073 compose_key = 0x100;
3074 if (cfg.compose_key) {
3075 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3076 compose_key = wParam;
3077 }
3078 if (wParam == VK_APPS)
3079 compose_key = wParam;
3080 }
3081
3082 if (wParam == compose_key) {
3083 if (compose_state == 0
3084 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3085 1;
3086 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3087 compose_state = 2;
3088 else
3089 compose_state = 0;
3090 } else if (compose_state == 1 && wParam != VK_CONTROL)
3091 compose_state = 0;
3092
3093 /*
3094 * Record that we pressed key so the scroll window can be reset, but
3095 * be careful to avoid Shift-UP/Down
3096 */
3097 if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
3098 wParam != VK_MENU && wParam != VK_CONTROL) {
3099 seen_key_event = 1;
3100 }
3101
3102 if (compose_state > 1 && left_alt)
3103 compose_state = 0;
3104
3105 /* Sanitize the number pad if not using a PC NumPad */
3106 if (left_alt || (app_keypad_keys && !cfg.no_applic_k
3107 && cfg.funky_type != 2)
3108 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3109 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3110 int nParam = 0;
3111 switch (wParam) {
3112 case VK_INSERT:
3113 nParam = VK_NUMPAD0;
3114 break;
3115 case VK_END:
3116 nParam = VK_NUMPAD1;
3117 break;
3118 case VK_DOWN:
3119 nParam = VK_NUMPAD2;
3120 break;
3121 case VK_NEXT:
3122 nParam = VK_NUMPAD3;
3123 break;
3124 case VK_LEFT:
3125 nParam = VK_NUMPAD4;
3126 break;
3127 case VK_CLEAR:
3128 nParam = VK_NUMPAD5;
3129 break;
3130 case VK_RIGHT:
3131 nParam = VK_NUMPAD6;
3132 break;
3133 case VK_HOME:
3134 nParam = VK_NUMPAD7;
3135 break;
3136 case VK_UP:
3137 nParam = VK_NUMPAD8;
3138 break;
3139 case VK_PRIOR:
3140 nParam = VK_NUMPAD9;
3141 break;
3142 case VK_DELETE:
3143 nParam = VK_DECIMAL;
3144 break;
3145 }
3146 if (nParam) {
3147 if (keystate[VK_NUMLOCK] & 1)
3148 shift_state |= 1;
3149 wParam = nParam;
3150 }
3151 }
3152 }
3153
3154 /* If a key is pressed and AltGr is not active */
3155 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3156 /* Okay, prepare for most alts then ... */
3157 if (left_alt)
3158 *p++ = '\033';
3159
3160 /* Lets see if it's a pattern we know all about ... */
3161 if (wParam == VK_PRIOR && shift_state == 1) {
3162 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3163 return 0;
3164 }
3165 if (wParam == VK_NEXT && shift_state == 1) {
3166 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3167 return 0;
3168 }
3169 if (wParam == VK_INSERT && shift_state == 1) {
3170 term_do_paste();
3171 return 0;
3172 }
3173 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3174 return -1;
3175 }
3176 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3177 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3178 return -1;
3179 }
3180 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3181 (cfg.resize_action != RESIZE_DISABLED)) {
3182 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3183 flip_full_screen();
3184 return -1;
3185 }
3186 /* Control-Numlock for app-keypad mode switch */
3187 if (wParam == VK_PAUSE && shift_state == 2) {
3188 app_keypad_keys ^= 1;
3189 return 0;
3190 }
3191
3192 /* Nethack keypad */
3193 if (cfg.nethack_keypad && !left_alt) {
3194 switch (wParam) {
3195 case VK_NUMPAD1:
3196 *p++ = shift_state ? 'B' : 'b';
3197 return p - output;
3198 case VK_NUMPAD2:
3199 *p++ = shift_state ? 'J' : 'j';
3200 return p - output;
3201 case VK_NUMPAD3:
3202 *p++ = shift_state ? 'N' : 'n';
3203 return p - output;
3204 case VK_NUMPAD4:
3205 *p++ = shift_state ? 'H' : 'h';
3206 return p - output;
3207 case VK_NUMPAD5:
3208 *p++ = shift_state ? '.' : '.';
3209 return p - output;
3210 case VK_NUMPAD6:
3211 *p++ = shift_state ? 'L' : 'l';
3212 return p - output;
3213 case VK_NUMPAD7:
3214 *p++ = shift_state ? 'Y' : 'y';
3215 return p - output;
3216 case VK_NUMPAD8:
3217 *p++ = shift_state ? 'K' : 'k';
3218 return p - output;
3219 case VK_NUMPAD9:
3220 *p++ = shift_state ? 'U' : 'u';
3221 return p - output;
3222 }
3223 }
3224
3225 /* Application Keypad */
3226 if (!left_alt) {
3227 int xkey = 0;
3228
3229 if (cfg.funky_type == 3 ||
3230 (cfg.funky_type <= 1 &&
3231 app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3232 case VK_EXECUTE:
3233 xkey = 'P';
3234 break;
3235 case VK_DIVIDE:
3236 xkey = 'Q';
3237 break;
3238 case VK_MULTIPLY:
3239 xkey = 'R';
3240 break;
3241 case VK_SUBTRACT:
3242 xkey = 'S';
3243 break;
3244 }
3245 if (app_keypad_keys && !cfg.no_applic_k)
3246 switch (wParam) {
3247 case VK_NUMPAD0:
3248 xkey = 'p';
3249 break;
3250 case VK_NUMPAD1:
3251 xkey = 'q';
3252 break;
3253 case VK_NUMPAD2:
3254 xkey = 'r';
3255 break;
3256 case VK_NUMPAD3:
3257 xkey = 's';
3258 break;
3259 case VK_NUMPAD4:
3260 xkey = 't';
3261 break;
3262 case VK_NUMPAD5:
3263 xkey = 'u';
3264 break;
3265 case VK_NUMPAD6:
3266 xkey = 'v';
3267 break;
3268 case VK_NUMPAD7:
3269 xkey = 'w';
3270 break;
3271 case VK_NUMPAD8:
3272 xkey = 'x';
3273 break;
3274 case VK_NUMPAD9:
3275 xkey = 'y';
3276 break;
3277
3278 case VK_DECIMAL:
3279 xkey = 'n';
3280 break;
3281 case VK_ADD:
3282 if (cfg.funky_type == 2) {
3283 if (shift_state)
3284 xkey = 'l';
3285 else
3286 xkey = 'k';
3287 } else if (shift_state)
3288 xkey = 'm';
3289 else
3290 xkey = 'l';
3291 break;
3292
3293 case VK_DIVIDE:
3294 if (cfg.funky_type == 2)
3295 xkey = 'o';
3296 break;
3297 case VK_MULTIPLY:
3298 if (cfg.funky_type == 2)
3299 xkey = 'j';
3300 break;
3301 case VK_SUBTRACT:
3302 if (cfg.funky_type == 2)
3303 xkey = 'm';
3304 break;
3305
3306 case VK_RETURN:
3307 if (HIWORD(lParam) & KF_EXTENDED)
3308 xkey = 'M';
3309 break;
3310 }
3311 if (xkey) {
3312 if (vt52_mode) {
3313 if (xkey >= 'P' && xkey <= 'S')
3314 p += sprintf((char *) p, "\x1B%c", xkey);
3315 else
3316 p += sprintf((char *) p, "\x1B?%c", xkey);
3317 } else
3318 p += sprintf((char *) p, "\x1BO%c", xkey);
3319 return p - output;
3320 }
3321 }
3322
3323 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3324 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3325 *p++ = 0;
3326 return -2;
3327 }
3328 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3329 *p++ = 0x1B;
3330 *p++ = '[';
3331 *p++ = 'Z';
3332 return p - output;
3333 }
3334 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3335 *p++ = 0;
3336 return p - output;
3337 }
3338 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3339 *p++ = 160;
3340 return p - output;
3341 }
3342 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3343 *p++ = 3;
3344 *p++ = 0;
3345 return -2;
3346 }
3347 if (wParam == VK_PAUSE) { /* Break/Pause */
3348 *p++ = 26;
3349 *p++ = 0;
3350 return -2;
3351 }
3352 /* Control-2 to Control-8 are special */
3353 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3354 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3355 return p - output;
3356 }
3357 if (shift_state == 2 && wParam == 0xBD) {
3358 *p++ = 0x1F;
3359 return p - output;
3360 }
3361 if (shift_state == 2 && wParam == 0xDF) {
3362 *p++ = 0x1C;
3363 return p - output;
3364 }
3365 if (shift_state == 0 && wParam == VK_RETURN && cr_lf_return) {
3366 *p++ = '\r';
3367 *p++ = '\n';
3368 return p - output;
3369 }
3370
3371 /*
3372 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3373 * for integer decimal nn.)
3374 *
3375 * We also deal with the weird ones here. Linux VCs replace F1
3376 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3377 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3378 * respectively.
3379 */
3380 code = 0;
3381 switch (wParam) {
3382 case VK_F1:
3383 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3384 break;
3385 case VK_F2:
3386 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3387 break;
3388 case VK_F3:
3389 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3390 break;
3391 case VK_F4:
3392 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3393 break;
3394 case VK_F5:
3395 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3396 break;
3397 case VK_F6:
3398 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3399 break;
3400 case VK_F7:
3401 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3402 break;
3403 case VK_F8:
3404 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3405 break;
3406 case VK_F9:
3407 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3408 break;
3409 case VK_F10:
3410 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3411 break;
3412 case VK_F11:
3413 code = 23;
3414 break;
3415 case VK_F12:
3416 code = 24;
3417 break;
3418 case VK_F13:
3419 code = 25;
3420 break;
3421 case VK_F14:
3422 code = 26;
3423 break;
3424 case VK_F15:
3425 code = 28;
3426 break;
3427 case VK_F16:
3428 code = 29;
3429 break;
3430 case VK_F17:
3431 code = 31;
3432 break;
3433 case VK_F18:
3434 code = 32;
3435 break;
3436 case VK_F19:
3437 code = 33;
3438 break;
3439 case VK_F20:
3440 code = 34;
3441 break;
3442 }
3443 if ((shift_state&2) == 0) switch (wParam) {
3444 case VK_HOME:
3445 code = 1;
3446 break;
3447 case VK_INSERT:
3448 code = 2;
3449 break;
3450 case VK_DELETE:
3451 code = 3;
3452 break;
3453 case VK_END:
3454 code = 4;
3455 break;
3456 case VK_PRIOR:
3457 code = 5;
3458 break;
3459 case VK_NEXT:
3460 code = 6;
3461 break;
3462 }
3463 /* Reorder edit keys to physical order */
3464 if (cfg.funky_type == 3 && code <= 6)
3465 code = "\0\2\1\4\5\3\6"[code];
3466
3467 if (vt52_mode && code > 0 && code <= 6) {
3468 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3469 return p - output;
3470 }
3471
3472 if (cfg.funky_type == 5 && /* SCO function keys */
3473 code >= 11 && code <= 34) {
3474 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3475 int index = 0;
3476 switch (wParam) {
3477 case VK_F1: index = 0; break;
3478 case VK_F2: index = 1; break;
3479 case VK_F3: index = 2; break;
3480 case VK_F4: index = 3; break;
3481 case VK_F5: index = 4; break;
3482 case VK_F6: index = 5; break;
3483 case VK_F7: index = 6; break;
3484 case VK_F8: index = 7; break;
3485 case VK_F9: index = 8; break;
3486 case VK_F10: index = 9; break;
3487 case VK_F11: index = 10; break;
3488 case VK_F12: index = 11; break;
3489 }
3490 if (keystate[VK_SHIFT] & 0x80) index += 12;
3491 if (keystate[VK_CONTROL] & 0x80) index += 24;
3492 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3493 return p - output;
3494 }
3495 if (cfg.funky_type == 5 && /* SCO small keypad */
3496 code >= 1 && code <= 6) {
3497 char codes[] = "HL.FIG";
3498 if (code == 3) {
3499 *p++ = '\x7F';
3500 } else {
3501 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3502 }
3503 return p - output;
3504 }
3505 if ((vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3506 int offt = 0;
3507 if (code > 15)
3508 offt++;
3509 if (code > 21)
3510 offt++;
3511 if (vt52_mode)
3512 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3513 else
3514 p +=
3515 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3516 return p - output;
3517 }
3518 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3519 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3520 return p - output;
3521 }
3522 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3523 if (vt52_mode)
3524 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3525 else
3526 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3527 return p - output;
3528 }
3529 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3530 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3531 return p - output;
3532 }
3533 if (code) {
3534 p += sprintf((char *) p, "\x1B[%d~", code);
3535 return p - output;
3536 }
3537
3538 /*
3539 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3540 * some reason seems to send VK_CLEAR to Windows...).
3541 */
3542 {
3543 char xkey = 0;
3544 switch (wParam) {
3545 case VK_UP:
3546 xkey = 'A';
3547 break;
3548 case VK_DOWN:
3549 xkey = 'B';
3550 break;
3551 case VK_RIGHT:
3552 xkey = 'C';
3553 break;
3554 case VK_LEFT:
3555 xkey = 'D';
3556 break;
3557 case VK_CLEAR:
3558 xkey = 'G';
3559 break;
3560 }
3561 if (xkey) {
3562 if (vt52_mode)
3563 p += sprintf((char *) p, "\x1B%c", xkey);
3564 else {
3565 int app_flg = (app_cursor_keys && !cfg.no_applic_c);
3566 #if 0
3567 /*
3568 * RDB: VT100 & VT102 manuals both state the
3569 * app cursor keys only work if the app keypad
3570 * is on.
3571 *
3572 * SGT: That may well be true, but xterm
3573 * disagrees and so does at least one
3574 * application, so I've #if'ed this out and the
3575 * behaviour is back to PuTTY's original: app
3576 * cursor and app keypad are independently
3577 * switchable modes. If anyone complains about
3578 * _this_ I'll have to put in a configurable
3579 * option.
3580 */
3581 if (!app_keypad_keys)
3582 app_flg = 0;
3583 #endif
3584 /* Useful mapping of Ctrl-arrows */
3585 if (shift_state == 2)
3586 app_flg = !app_flg;
3587
3588 if (app_flg)
3589 p += sprintf((char *) p, "\x1BO%c", xkey);
3590 else
3591 p += sprintf((char *) p, "\x1B[%c", xkey);
3592 }
3593 return p - output;
3594 }
3595 }
3596
3597 /*
3598 * Finally, deal with Return ourselves. (Win95 seems to
3599 * foul it up when Alt is pressed, for some reason.)
3600 */
3601 if (wParam == VK_RETURN) { /* Return */
3602 *p++ = 0x0D;
3603 *p++ = 0;
3604 return -2;
3605 }
3606
3607 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3608 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3609 else
3610 alt_sum = 0;
3611 }
3612
3613 /* Okay we've done everything interesting; let windows deal with
3614 * the boring stuff */
3615 {
3616 BOOL capsOn=0;
3617
3618 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3619 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3620 capsOn= !left_alt;
3621 keystate[VK_CAPITAL] = 0;
3622 }
3623
3624 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3625 #ifdef SHOW_TOASCII_RESULT
3626 if (r == 1 && !key_down) {
3627 if (alt_sum) {
3628 if (in_utf || dbcs_screenfont)
3629 debug((", (U+%04x)", alt_sum));
3630 else
3631 debug((", LCH(%d)", alt_sum));
3632 } else {
3633 debug((", ACH(%d)", keys[0]));
3634 }
3635 } else if (r > 0) {
3636 int r1;
3637 debug((", ASC("));
3638 for (r1 = 0; r1 < r; r1++) {
3639 debug(("%s%d", r1 ? "," : "", keys[r1]));
3640 }
3641 debug((")"));
3642 }
3643 #endif
3644 if (r > 0) {
3645 WCHAR keybuf;
3646
3647 /*
3648 * Interrupt an ongoing paste. I'm not sure this is
3649 * sensible, but for the moment it's preferable to
3650 * having to faff about buffering things.
3651 */
3652 term_nopaste();
3653
3654 p = output;
3655 for (i = 0; i < r; i++) {
3656 unsigned char ch = (unsigned char) keys[i];
3657
3658 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3659 compose_char = ch;
3660 compose_state++;
3661 continue;
3662 }
3663 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3664 int nc;
3665 compose_state = 0;
3666
3667 if ((nc = check_compose(compose_char, ch)) == -1) {
3668 MessageBeep(MB_ICONHAND);
3669 return 0;
3670 }
3671 keybuf = nc;
3672 luni_send(&keybuf, 1, 1);
3673 continue;
3674 }
3675
3676 compose_state = 0;
3677
3678 if (!key_down) {
3679 if (alt_sum) {
3680 if (in_utf || dbcs_screenfont) {
3681 keybuf = alt_sum;
3682 luni_send(&keybuf, 1, 1);
3683 } else {
3684 ch = (char) alt_sum;
3685 /*
3686 * We need not bother about stdin
3687 * backlogs here, because in GUI PuTTY
3688 * we can't do anything about it
3689 * anyway; there's no means of asking
3690 * Windows to hold off on KEYDOWN
3691 * messages. We _have_ to buffer
3692 * everything we're sent.
3693 */
3694 ldisc_send(&ch, 1, 1);
3695 }
3696 alt_sum = 0;
3697 } else
3698 lpage_send(kbd_codepage, &ch, 1, 1);
3699 } else {
3700 if(capsOn && ch < 0x80) {
3701 WCHAR cbuf[2];
3702 cbuf[0] = 27;
3703 cbuf[1] = xlat_uskbd2cyrllic(ch);
3704 luni_send(cbuf+!left_alt, 1+!!left_alt, 1);
3705 } else {
3706 char cbuf[2];
3707 cbuf[0] = '\033';
3708 cbuf[1] = ch;
3709 lpage_send(kbd_codepage, cbuf+!left_alt, 1+!!left_alt, 1);
3710 }
3711 }
3712 show_mouseptr(0);
3713 }
3714
3715 /* This is so the ALT-Numpad and dead keys work correctly. */
3716 keys[0] = 0;
3717
3718 return p - output;
3719 }
3720 /* If we're definitly not building up an ALT-54321 then clear it */
3721 if (!left_alt)
3722 keys[0] = 0;
3723 /* If we will be using alt_sum fix the 256s */
3724 else if (keys[0] && (in_utf || dbcs_screenfont))
3725 keys[0] = 10;
3726 }
3727
3728 /*
3729 * ALT alone may or may not want to bring up the System menu.
3730 * If it's not meant to, we return 0 on presses or releases of
3731 * ALT, to show that we've swallowed the keystroke. Otherwise
3732 * we return -1, which means Windows will give the keystroke
3733 * its default handling (i.e. bring up the System menu).
3734 */
3735 if (wParam == VK_MENU && !cfg.alt_only)
3736 return 0;
3737
3738 return -1;
3739 }
3740
3741 void set_title(char *title)
3742 {
3743 sfree(window_name);
3744 window_name = smalloc(1 + strlen(title));
3745 strcpy(window_name, title);
3746 if (cfg.win_name_always || !IsIconic(hwnd))
3747 SetWindowText(hwnd, title);
3748 }
3749
3750 void set_icon(char *title)
3751 {
3752 sfree(icon_name);
3753 icon_name = smalloc(1 + strlen(title));
3754 strcpy(icon_name, title);
3755 if (!cfg.win_name_always && IsIconic(hwnd))
3756 SetWindowText(hwnd, title);
3757 }
3758
3759 void set_sbar(int total, int start, int page)
3760 {
3761 SCROLLINFO si;
3762
3763 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3764 return;
3765
3766 si.cbSize = sizeof(si);
3767 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3768 si.nMin = 0;
3769 si.nMax = total - 1;
3770 si.nPage = page;
3771 si.nPos = start;
3772 if (hwnd)
3773 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3774 }
3775
3776 Context get_ctx(void)
3777 {
3778 HDC hdc;
3779 if (hwnd) {
3780 hdc = GetDC(hwnd);
3781 if (hdc && pal)
3782 SelectPalette(hdc, pal, FALSE);
3783 return hdc;
3784 } else
3785 return NULL;
3786 }
3787
3788 void free_ctx(Context ctx)
3789 {
3790 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3791 ReleaseDC(hwnd, ctx);
3792 }
3793
3794 static void real_palette_set(int n, int r, int g, int b)
3795 {
3796 if (pal) {
3797 logpal->palPalEntry[n].peRed = r;
3798 logpal->palPalEntry[n].peGreen = g;
3799 logpal->palPalEntry[n].peBlue = b;
3800 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3801 colours[n] = PALETTERGB(r, g, b);
3802 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3803 } else
3804 colours[n] = RGB(r, g, b);
3805 }
3806
3807 void palette_set(int n, int r, int g, int b)
3808 {
3809 static const int first[21] = {
3810 0, 2, 4, 6, 8, 10, 12, 14,
3811 1, 3, 5, 7, 9, 11, 13, 15,
3812 16, 17, 18, 20, 22
3813 };
3814 real_palette_set(first[n], r, g, b);
3815 if (first[n] >= 18)
3816 real_palette_set(first[n] + 1, r, g, b);
3817 if (pal) {
3818 HDC hdc = get_ctx();
3819 UnrealizeObject(pal);
3820 RealizePalette(hdc);
3821 free_ctx(hdc);
3822 }
3823 }
3824
3825 void palette_reset(void)
3826 {
3827 int i;
3828
3829 for (i = 0; i < NCOLOURS; i++) {
3830 if (pal) {
3831 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3832 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3833 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3834 logpal->palPalEntry[i].peFlags = 0;
3835 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3836 defpal[i].rgbtGreen,
3837 defpal[i].rgbtBlue);
3838 } else
3839 colours[i] = RGB(defpal[i].rgbtRed,
3840 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
3841 }
3842
3843 if (pal) {
3844 HDC hdc;
3845 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3846 hdc = get_ctx();
3847 RealizePalette(hdc);
3848 free_ctx(hdc);
3849 }
3850 }
3851
3852 void write_aclip(char *data, int len, int must_deselect)
3853 {
3854 HGLOBAL clipdata;
3855 void *lock;
3856
3857 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
3858 if (!clipdata)
3859 return;
3860 lock = GlobalLock(clipdata);
3861 if (!lock)
3862 return;
3863 memcpy(lock, data, len);
3864 ((unsigned char *) lock)[len] = 0;
3865 GlobalUnlock(clipdata);
3866
3867 if (!must_deselect)
3868 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
3869
3870 if (OpenClipboard(hwnd)) {
3871 EmptyClipboard();
3872 SetClipboardData(CF_TEXT, clipdata);
3873 CloseClipboard();
3874 } else
3875 GlobalFree(clipdata);
3876
3877 if (!must_deselect)
3878 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
3879 }
3880
3881 /*
3882 * Note: unlike write_aclip() this will not append a nul.
3883 */
3884 void write_clip(wchar_t * data, int len, int must_deselect)
3885 {
3886 HGLOBAL clipdata, clipdata2, clipdata3;
3887 int len2;
3888 void *lock, *lock2, *lock3;
3889
3890 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
3891
3892 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
3893 len * sizeof(wchar_t));
3894 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
3895
3896 if (!clipdata || !clipdata2) {
3897 if (clipdata)
3898 GlobalFree(clipdata);
3899 if (clipdata2)
3900 GlobalFree(clipdata2);
3901 return;
3902 }
3903 if (!(lock = GlobalLock(clipdata)))
3904 return;
3905 if (!(lock2 = GlobalLock(clipdata2)))
3906 return;
3907
3908 memcpy(lock, data, len * sizeof(wchar_t));
3909 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
3910
3911 if (cfg.rtf_paste) {
3912 wchar_t unitab[256];
3913 char *rtf = NULL;
3914 unsigned char *tdata = (unsigned char *)lock2;
3915 wchar_t *udata = (wchar_t *)lock;
3916 int rtflen = 0, uindex = 0, tindex = 0;
3917 int rtfsize = 0;
3918 int multilen, blen, alen, totallen, i;
3919 char before[16], after[4];
3920
3921 get_unitab(CP_ACP, unitab, 0);
3922
3923 rtfsize = 100 + strlen(cfg.font);
3924 rtf = smalloc(rtfsize);
3925 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
3926 GetACP(), cfg.font);
3927 rtflen = strlen(rtf);
3928
3929 /*
3930 * We want to construct a piece of RTF that specifies the
3931 * same Unicode text. To do this we will read back in
3932 * parallel from the Unicode data in `udata' and the
3933 * non-Unicode data in `tdata'. For each character in
3934 * `tdata' which becomes the right thing in `udata' when
3935 * looked up in `unitab', we just copy straight over from
3936 * tdata. For each one that doesn't, we must WCToMB it
3937 * individually and produce a \u escape sequence.
3938 *
3939 * It would probably be more robust to just bite the bullet
3940 * and WCToMB each individual Unicode character one by one,
3941 * then MBToWC each one back to see if it was an accurate
3942 * translation; but that strikes me as a horrifying number
3943 * of Windows API calls so I want to see if this faster way
3944 * will work. If it screws up badly we can always revert to
3945 * the simple and slow way.
3946 */
3947 while (tindex < len2 && uindex < len &&
3948 tdata[tindex] && udata[uindex]) {
3949 if (tindex + 1 < len2 &&
3950 tdata[tindex] == '\r' &&
3951 tdata[tindex+1] == '\n') {
3952 tindex++;
3953 uindex++;
3954 }
3955 if (unitab[tdata[tindex]] == udata[uindex]) {
3956 multilen = 1;
3957 before[0] = '\0';
3958 after[0] = '\0';
3959 blen = alen = 0;
3960 } else {
3961 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
3962 NULL, 0, NULL, NULL);
3963 if (multilen != 1) {
3964 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
3965 udata[uindex]);
3966 alen = 1; strcpy(after, "}");
3967 } else {
3968 blen = sprintf(before, "\\u%d", udata[uindex]);
3969 alen = 0; after[0] = '\0';
3970 }
3971 }
3972 assert(tindex + multilen <= len2);
3973 totallen = blen + alen;
3974 for (i = 0; i < multilen; i++) {
3975 if (tdata[tindex+i] == '\\' ||
3976 tdata[tindex+i] == '{' ||
3977 tdata[tindex+i] == '}')
3978 totallen += 2;
3979 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
3980 totallen += 6; /* \par\r\n */
3981 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
3982 totallen += 4;
3983 else
3984 totallen++;
3985 }
3986
3987 if (rtfsize < rtflen + totallen + 3) {
3988 rtfsize = rtflen + totallen + 512;
3989 rtf = srealloc(rtf, rtfsize);
3990 }
3991
3992 strcpy(rtf + rtflen, before); rtflen += blen;
3993 for (i = 0; i < multilen; i++) {
3994 if (tdata[tindex+i] == '\\' ||
3995 tdata[tindex+i] == '{' ||
3996 tdata[tindex+i] == '}') {
3997 rtf[rtflen++] = '\\';
3998 rtf[rtflen++] = tdata[tindex+i];
3999 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4000 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4001 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4002 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4003 } else {
4004 rtf[rtflen++] = tdata[tindex+i];
4005 }
4006 }
4007 strcpy(rtf + rtflen, after); rtflen += alen;
4008
4009 tindex += multilen;
4010 uindex++;
4011 }
4012
4013 strcpy(rtf + rtflen, "}");
4014 rtflen += 2;
4015
4016 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4017 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4018 strcpy(lock3, rtf);
4019 GlobalUnlock(clipdata3);
4020 }
4021 sfree(rtf);
4022 } else
4023 clipdata3 = NULL;
4024
4025 GlobalUnlock(clipdata);
4026 GlobalUnlock(clipdata2);
4027
4028 if (!must_deselect)
4029 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4030
4031 if (OpenClipboard(hwnd)) {
4032 EmptyClipboard();
4033 SetClipboardData(CF_UNICODETEXT, clipdata);
4034 SetClipboardData(CF_TEXT, clipdata2);
4035 if (clipdata3)
4036 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4037 CloseClipboard();
4038 } else {
4039 GlobalFree(clipdata);
4040 GlobalFree(clipdata2);
4041 }
4042
4043 if (!must_deselect)
4044 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4045 }
4046
4047 void get_clip(wchar_t ** p, int *len)
4048 {
4049 static HGLOBAL clipdata = NULL;
4050 static wchar_t *converted = 0;
4051 wchar_t *p2;
4052
4053 if (converted) {
4054 sfree(converted);
4055 converted = 0;
4056 }
4057 if (!p) {
4058 if (clipdata)
4059 GlobalUnlock(clipdata);
4060 clipdata = NULL;
4061 return;
4062 } else if (OpenClipboard(NULL)) {
4063 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4064 CloseClipboard();
4065 *p = GlobalLock(clipdata);
4066 if (*p) {
4067 for (p2 = *p; *p2; p2++);
4068 *len = p2 - *p;
4069 return;
4070 }
4071 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4072 char *s;
4073 int i;
4074 CloseClipboard();
4075 s = GlobalLock(clipdata);
4076 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4077 *p = converted = smalloc(i * sizeof(wchar_t));
4078 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4079 *len = i - 1;
4080 return;
4081 } else
4082 CloseClipboard();
4083 }
4084
4085 *p = NULL;
4086 *len = 0;
4087 }
4088
4089 #if 0
4090 /*
4091 * Move `lines' lines from position `from' to position `to' in the
4092 * window.
4093 */
4094 void optimised_move(int to, int from, int lines)
4095 {
4096 RECT r;
4097 int min, max;
4098
4099 min = (to < from ? to : from);
4100 max = to + from - min;
4101
4102 r.left = offset_width;
4103 r.right = offset_width + cols * font_width;
4104 r.top = offset_height + min * font_height;
4105 r.bottom = offset_height + (max + lines) * font_height;
4106 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4107 }
4108 #endif
4109
4110 /*
4111 * Print a message box and perform a fatal exit.
4112 */
4113 void fatalbox(char *fmt, ...)
4114 {
4115 va_list ap;
4116 char stuff[200];
4117
4118 va_start(ap, fmt);
4119 vsprintf(stuff, fmt, ap);
4120 va_end(ap);
4121 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4122 exit(1);
4123 }
4124
4125 /*
4126 * Manage window caption / taskbar flashing, if enabled.
4127 * 0 = stop, 1 = maintain, 2 = start
4128 */
4129 static void flash_window(int mode)
4130 {
4131 static long last_flash = 0;
4132 static int flashing = 0;
4133 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4134 /* stop */
4135 if (flashing) {
4136 FlashWindow(hwnd, FALSE);
4137 flashing = 0;
4138 }
4139
4140 } else if (mode == 2) {
4141 /* start */
4142 if (!flashing) {
4143 last_flash = GetTickCount();
4144 flashing = 1;
4145 FlashWindow(hwnd, TRUE);
4146 }
4147
4148 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4149 /* maintain */
4150 if (flashing) {
4151 long now = GetTickCount();
4152 long fdiff = now - last_flash;
4153 if (fdiff < 0 || fdiff > 450) {
4154 last_flash = now;
4155 FlashWindow(hwnd, TRUE); /* toggle */
4156 }
4157 }
4158 }
4159 }
4160
4161 /*
4162 * Beep.
4163 */
4164 void beep(int mode)
4165 {
4166 if (mode == BELL_DEFAULT) {
4167 /*
4168 * For MessageBeep style bells, we want to be careful of
4169 * timing, because they don't have the nice property of
4170 * PlaySound bells that each one cancels the previous
4171 * active one. So we limit the rate to one per 50ms or so.
4172 */
4173 static long lastbeep = 0;
4174 long beepdiff;
4175
4176 beepdiff = GetTickCount() - lastbeep;
4177 if (beepdiff >= 0 && beepdiff < 50)
4178 return;
4179 MessageBeep(MB_OK);
4180 /*
4181 * The above MessageBeep call takes time, so we record the
4182 * time _after_ it finishes rather than before it starts.
4183 */
4184 lastbeep = GetTickCount();
4185 } else if (mode == BELL_WAVEFILE) {
4186 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4187 char buf[sizeof(cfg.bell_wavefile) + 80];
4188 sprintf(buf, "Unable to play sound file\n%s\n"
4189 "Using default sound instead", cfg.bell_wavefile);
4190 MessageBox(hwnd, buf, "PuTTY Sound Error",
4191 MB_OK | MB_ICONEXCLAMATION);
4192 cfg.beep = BELL_DEFAULT;
4193 }
4194 }
4195 /* Otherwise, either visual bell or disabled; do nothing here */
4196 if (!has_focus) {
4197 flash_window(2); /* start */
4198 }
4199 }
4200
4201 /*
4202 * Minimise or restore the window in response to a server-side
4203 * request.
4204 */
4205 void set_iconic(int iconic)
4206 {
4207 if (IsIconic(hwnd)) {
4208 if (!iconic)
4209 ShowWindow(hwnd, SW_RESTORE);
4210 } else {
4211 if (iconic)
4212 ShowWindow(hwnd, SW_MINIMIZE);
4213 }
4214 }
4215
4216 /*
4217 * Move the window in response to a server-side request.
4218 */
4219 void move_window(int x, int y)
4220 {
4221 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4222 }
4223
4224 /*
4225 * Move the window to the top or bottom of the z-order in response
4226 * to a server-side request.
4227 */
4228 void set_zorder(int top)
4229 {
4230 if (cfg.alwaysontop)
4231 return; /* ignore */
4232 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4233 SWP_NOMOVE | SWP_NOSIZE);
4234 }
4235
4236 /*
4237 * Refresh the window in response to a server-side request.
4238 */
4239 void refresh_window(void)
4240 {
4241 InvalidateRect(hwnd, NULL, TRUE);
4242 }
4243
4244 /*
4245 * Maximise or restore the window in response to a server-side
4246 * request.
4247 */
4248 void set_zoomed(int zoomed)
4249 {
4250 if (IsZoomed(hwnd)) {
4251 if (!zoomed)
4252 ShowWindow(hwnd, SW_RESTORE);
4253 } else {
4254 if (zoomed)
4255 ShowWindow(hwnd, SW_MAXIMIZE);
4256 }
4257 }
4258
4259 /*
4260 * Report whether the window is iconic, for terminal reports.
4261 */
4262 int is_iconic(void)
4263 {
4264 return IsIconic(hwnd);
4265 }
4266
4267 /*
4268 * Report the window's position, for terminal reports.
4269 */
4270 void get_window_pos(int *x, int *y)
4271 {
4272 RECT r;
4273 GetWindowRect(hwnd, &r);
4274 *x = r.left;
4275 *y = r.top;
4276 }
4277
4278 /*
4279 * Report the window's pixel size, for terminal reports.
4280 */
4281 void get_window_pixels(int *x, int *y)
4282 {
4283 RECT r;
4284 GetWindowRect(hwnd, &r);
4285 *x = r.right - r.left;
4286 *y = r.bottom - r.top;
4287 }
4288
4289 /*
4290 * Return the window or icon title.
4291 */
4292 char *get_window_title(int icon)
4293 {
4294 return icon ? icon_name : window_name;
4295 }
4296
4297 /*
4298 * See if we're in full-screen mode.
4299 */
4300 int is_full_screen()
4301 {
4302 if (!IsZoomed(hwnd))
4303 return FALSE;
4304 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4305 return FALSE;
4306 return TRUE;
4307 }
4308
4309 /*
4310 * Go full-screen. This should only be called when we are already
4311 * maximised.
4312 */
4313 void make_full_screen()
4314 {
4315 DWORD style;
4316 int x, y, w, h;
4317
4318 assert(IsZoomed(hwnd));
4319
4320 /* Remove the window furniture. */
4321 style = GetWindowLong(hwnd, GWL_STYLE);
4322 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4323 if (cfg.scrollbar_in_fullscreen)
4324 style |= WS_VSCROLL;
4325 else
4326 style &= ~WS_VSCROLL;
4327 SetWindowLong(hwnd, GWL_STYLE, style);
4328
4329 /* Resize ourselves to exactly cover the nearest monitor. */
4330 #ifdef MONITOR_DEFAULTTONEAREST
4331 {
4332 HMONITOR mon;
4333 MONITORINFO mi;
4334 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4335 mi.cbSize = sizeof(mi);
4336 GetMonitorInfo(mon, &mi);
4337 x = mi.rcMonitor.left;
4338 y = mi.rcMonitor.top;
4339 w = mi.rcMonitor.right;
4340 h = mi.rcMonitor.bottom;
4341 }
4342 #else
4343 x = y = 0;
4344 w = GetSystemMetrics(SM_CXSCREEN);
4345 h = GetSystemMetrics(SM_CYSCREEN);
4346 #endif
4347 SetWindowPos(hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED);
4348
4349 /* Tick the menu item in the System menu. */
4350 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4351 MF_CHECKED);
4352 }
4353
4354 /*
4355 * Clear the full-screen attributes.
4356 */
4357 void clear_full_screen()
4358 {
4359 DWORD oldstyle, style;
4360
4361 /* Reinstate the window furniture. */
4362 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4363 style |= WS_CAPTION | WS_BORDER;
4364 if (cfg.resize_action == RESIZE_DISABLED)
4365 style &= ~WS_THICKFRAME;
4366 else
4367 style |= WS_THICKFRAME;
4368 if (cfg.scrollbar)
4369 style |= WS_VSCROLL;
4370 else
4371 style &= ~WS_VSCROLL;
4372 if (style != oldstyle) {
4373 SetWindowLong(hwnd, GWL_STYLE, style);
4374 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4375 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4376 SWP_FRAMECHANGED);
4377 }
4378
4379 /* Untick the menu item in the System menu. */
4380 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4381 MF_UNCHECKED);
4382 }
4383
4384 /*
4385 * Toggle full-screen mode.
4386 */
4387 void flip_full_screen()
4388 {
4389 if (is_full_screen()) {
4390 ShowWindow(hwnd, SW_RESTORE);
4391 } else if (IsZoomed(hwnd)) {
4392 make_full_screen();
4393 } else {
4394 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4395 ShowWindow(hwnd, SW_MAXIMIZE);
4396 }
4397 }