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