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