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