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