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