16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 #define IDM_SHOWLOG 0x0010
35 #define IDM_NEWSESS 0x0020
36 #define IDM_DUPSESS 0x0030
37 #define IDM_RECONF 0x0040
38 #define IDM_CLRSB 0x0050
39 #define IDM_RESET 0x0060
40 #define IDM_TEL_AYT 0x0070
41 #define IDM_TEL_BRK 0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC 0x00a0
44 #define IDM_TEL_EL 0x00b0
45 #define IDM_TEL_GA 0x00c0
46 #define IDM_TEL_NOP 0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO 0x00f0
49 #define IDM_TEL_IP 0x0100
50 #define IDM_TEL_SUSP 0x0110
51 #define IDM_TEL_EOR 0x0120
52 #define IDM_TEL_EOF 0x0130
53 #define IDM_HELP 0x0140
54 #define IDM_ABOUT 0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL 0x0170
57 #define IDM_FULLSCREEN 0x0180
59 #define IDM_SESSLGP 0x0250 /* log type printable */
60 #define IDM_SESSLGA 0x0260 /* log type all chars */
61 #define IDM_SESSLGE 0x0270 /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
68 /* Needed for Chinese support and apparently not always defined. */
70 #define VK_PROCESSKEY 0xE5
73 /* Mouse wheel support. */
75 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
78 #define WHEEL_DELTA 120
81 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
82 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
83 unsigned char *output);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL);
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width, extra_height;
99 static int font_width, font_height, font_dualwidth;
100 static int offset_width, offset_height;
101 static int was_zoomed = 0;
102 static int prev_rows, prev_cols;
104 static int pending_netevent = 0;
105 static WPARAM pend_netevent_wParam = 0;
106 static LPARAM pend_netevent_lParam = 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode);
109 static void sys_cursor_update(void);
110 static int is_shift_pressed(void);
111 static int get_fullscreen_rect(RECT * ss);
113 static time_t last_movement = 0;
115 static int caret_x = -1, caret_y = -1;
117 static int kbd_codepage;
120 static Backend *back;
121 static void *backhandle;
123 static int session_closed;
125 Config cfg; /* exported to windlg.c */
127 extern struct sesslist sesslist; /* imported from windlg.c */
129 #define FONT_NORMAL 0
131 #define FONT_UNDERLINE 2
132 #define FONT_BOLDUND 3
133 #define FONT_WIDE 0x04
134 #define FONT_HIGH 0x08
135 #define FONT_NARROW 0x10
137 #define FONT_OEM 0x20
138 #define FONT_OEMBOLD 0x21
139 #define FONT_OEMUND 0x22
140 #define FONT_OEMBOLDUND 0x23
142 #define FONT_MAXNO 0x2F
144 static HFONT fonts[FONT_MAXNO];
145 static LOGFONT lfont;
146 static int fontflag[FONT_MAXNO];
148 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
156 static COLORREF colours[NCOLOURS];
158 static LPLOGPALETTE logpal;
159 static RGBTRIPLE defpal[NCOLOURS];
163 static HBITMAP caretbm;
165 static int dbltime, lasttime, lastact;
166 static Mouse_Button lastbtn;
168 /* this allows xterm-style mouse handling. */
169 static int send_raw_mouse = 0;
170 static int wheel_accumulator = 0;
172 static char *window_name, *icon_name;
174 static int compose_state = 0;
176 static int wsa_started = 0;
178 static OSVERSIONINFO osVersion;
180 static UINT wm_mousewheel = WM_MOUSEWHEEL;
182 /* Dummy routine, only required in plink. */
183 void ldisc_update(void *frontend, int echo, int edit)
187 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
189 static char appname[] = "PuTTY";
194 int guess_width, guess_height;
197 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
199 winsock_ver = MAKEWORD(1, 1);
200 if (WSAStartup(winsock_ver, &wsadata)) {
201 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
202 MB_OK | MB_ICONEXCLAMATION);
205 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
206 MessageBox(NULL, "WinSock version is incompatible with 1.1",
207 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
212 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
215 InitCommonControls();
217 /* Ensure a Maximize setting in Explorer doesn't maximise the
222 ZeroMemory(&osVersion, sizeof(osVersion));
223 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
224 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
225 MessageBox(NULL, "Windows refuses to report a version",
226 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
232 * If we're running a version of Windows that doesn't support
233 * WM_MOUSEWHEEL, find out what message number we should be
236 if (osVersion.dwMajorVersion < 4 ||
237 (osVersion.dwMajorVersion == 4 &&
238 osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
239 wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
242 * See if we can find our Help file.
245 char b[2048], *p, *q, *r;
247 GetModuleFileName(NULL, b, sizeof(b) - 1);
249 p = strrchr(b, '\\');
250 if (p && p >= r) r = p+1;
252 if (q && q >= r) r = q+1;
253 strcpy(r, "putty.hlp");
254 if ( (fp = fopen(b, "r")) != NULL) {
255 help_path = dupstr(b);
259 strcpy(r, "putty.cnt");
260 if ( (fp = fopen(b, "r")) != NULL) {
261 help_has_contents = TRUE;
264 help_has_contents = FALSE;
268 * Process the command line.
274 default_protocol = DEFAULT_PROTOCOL;
275 default_port = DEFAULT_PORT;
276 cfg.logtype = LGTYP_NONE;
278 do_defaults(NULL, &cfg);
283 * Process a couple of command-line options which are more
284 * easily dealt with before the line is broken up into
285 * words. These are the soon-to-be-defunct @sessionname and
286 * the internal-use-only &sharedmemoryhandle, neither of
287 * which are combined with anything else.
289 while (*p && isspace(*p))
293 while (i > 1 && isspace(p[i - 1]))
296 do_defaults(p + 1, &cfg);
297 if (!*cfg.host && !do_config()) {
301 } else if (*p == '&') {
303 * An initial & means we've been given a command line
304 * containing the hex value of a HANDLE for a file
305 * mapping object, which we must then extract as a
310 if (sscanf(p + 1, "%p", &filemap) == 1 &&
311 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
312 0, 0, sizeof(Config))) != NULL) {
315 CloseHandle(filemap);
316 } else if (!do_config()) {
322 * Otherwise, break up the command line and deal with
328 split_into_argv(cmdline, &argc, &argv, NULL);
330 for (i = 0; i < argc; i++) {
334 ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
337 cmdline_error("option \"%s\" requires an argument", p);
338 } else if (ret == 2) {
339 i++; /* skip next argument */
340 } else if (ret == 1) {
341 continue; /* nothing further needs doing */
342 } else if (!strcmp(p, "-cleanup")) {
344 * `putty -cleanup'. Remove all registry
345 * entries associated with PuTTY, and also find
346 * and delete the random seed file.
349 "This procedure will remove ALL Registry\n"
350 "entries associated with PuTTY, and will\n"
351 "also remove the PuTTY random seed file.\n"
353 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
354 "SESSIONS. Are you really sure you want\n"
357 MB_YESNO | MB_ICONWARNING) == IDYES) {
361 } else if (*p != '-') {
365 * If we already have a host name, treat
366 * this argument as a port number. NB we
367 * have to treat this as a saved -P
368 * argument, so that it will be deferred
369 * until it's a good moment to run it.
371 int ret = cmdline_process_param("-P", p, 1, &cfg);
373 } else if (!strncmp(q, "telnet:", 7)) {
375 * If the hostname starts with "telnet:",
376 * set the protocol to Telnet and process
377 * the string as a Telnet URL.
382 if (q[0] == '/' && q[1] == '/')
384 cfg.protocol = PROT_TELNET;
386 while (*p && *p != ':' && *p != '/')
395 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
396 cfg.host[sizeof(cfg.host) - 1] = '\0';
400 * Otherwise, treat this argument as a host
403 while (*p && !isspace(*p))
407 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
408 cfg.host[sizeof(cfg.host) - 1] = '\0';
412 cmdline_error("unknown option \"%s\"", p);
417 cmdline_run_saved(&cfg);
419 if (!*cfg.host && !do_config()) {
425 * Trim leading whitespace off the hostname if it's there.
428 int space = strspn(cfg.host, " \t");
429 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
432 /* See if host is of the form user@host */
433 if (cfg.host[0] != '\0') {
434 char *atsign = strchr(cfg.host, '@');
435 /* Make sure we're not overflowing the user field */
437 if (atsign - cfg.host < sizeof cfg.username) {
438 strncpy(cfg.username, cfg.host, atsign - cfg.host);
439 cfg.username[atsign - cfg.host] = '\0';
441 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
446 * Trim a colon suffix off the hostname if it's there.
448 cfg.host[strcspn(cfg.host, ":")] = '\0';
451 * Remove any remaining whitespace from the hostname.
455 while (cfg.host[p2] != '\0') {
456 if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
457 cfg.host[p1] = cfg.host[p2];
467 * Select protocol. This is farmed out into a table in a
468 * separate file to enable an ssh-free variant.
473 for (i = 0; backends[i].backend != NULL; i++)
474 if (backends[i].protocol == cfg.protocol) {
475 back = backends[i].backend;
479 MessageBox(NULL, "Unsupported protocol number found",
480 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
486 /* Check for invalid Port number (i.e. zero) */
488 MessageBox(NULL, "Invalid Port Number",
489 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
496 wndclass.lpfnWndProc = WndProc;
497 wndclass.cbClsExtra = 0;
498 wndclass.cbWndExtra = 0;
499 wndclass.hInstance = inst;
500 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
501 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
502 wndclass.hbrBackground = NULL;
503 wndclass.lpszMenuName = NULL;
504 wndclass.lpszClassName = appname;
506 RegisterClass(&wndclass);
511 term = term_init(&cfg, NULL);
512 logctx = log_init(NULL, &cfg);
513 term_provide_logctx(term, logctx);
518 * Guess some defaults for the window size. This all gets
519 * updated later, so we don't really care too much. However, we
520 * do want the font width/height guesses to correspond to a
521 * large font rather than a small one...
528 term_size(term, cfg.height, cfg.width, cfg.savelines);
529 guess_width = extra_width + font_width * term->cols;
530 guess_height = extra_height + font_height * term->rows;
533 get_fullscreen_rect(&r);
534 if (guess_width > r.right - r.left)
535 guess_width = r.right - r.left;
536 if (guess_height > r.bottom - r.top)
537 guess_height = r.bottom - r.top;
541 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
544 winmode &= ~(WS_VSCROLL);
545 if (cfg.resize_action == RESIZE_DISABLED)
546 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
548 exwinmode |= WS_EX_TOPMOST;
550 exwinmode |= WS_EX_CLIENTEDGE;
551 hwnd = CreateWindowEx(exwinmode, appname, appname,
552 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
553 guess_width, guess_height,
554 NULL, NULL, inst, NULL);
558 * Initialise the fonts, simultaneously correcting the guesses
559 * for font_{width,height}.
564 * Correct the guesses for extra_{width,height}.
568 GetWindowRect(hwnd, &wr);
569 GetClientRect(hwnd, &cr);
570 offset_width = offset_height = cfg.window_border;
571 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
572 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
576 * Resize the window, now we know what size we _really_ want it
579 guess_width = extra_width + font_width * term->cols;
580 guess_height = extra_height + font_height * term->rows;
581 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
582 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
585 * Set up a caret bitmap, with no content.
589 int size = (font_width + 15) / 16 * 2 * font_height;
590 bits = smalloc(size);
591 memset(bits, 0, size);
592 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
595 CreateCaret(hwnd, caretbm, font_width, font_height);
598 * Initialise the scroll bar.
603 si.cbSize = sizeof(si);
604 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
606 si.nMax = term->rows - 1;
607 si.nPage = term->rows;
609 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
613 * Start up the telnet connection.
617 char msg[1024], *title;
620 error = back->init((void *)term, &backhandle, &cfg,
621 cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
622 back->provide_logctx(backhandle, logctx);
624 sprintf(msg, "Unable to open connection to\n"
625 "%.800s\n" "%s", cfg.host, error);
626 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
629 window_name = icon_name = NULL;
631 title = cfg.wintitle;
633 sprintf(msg, "%s - PuTTY", realhost);
637 set_title(NULL, title);
638 set_icon(NULL, title);
642 * Connect the terminal to the backend for resize purposes.
644 term_provide_resize_fn(term, back->size, backhandle);
647 * Set up a line discipline.
649 ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
651 session_closed = FALSE;
654 * Prepare the mouse handler.
656 lastact = MA_NOTHING;
657 lastbtn = MBT_NOTHING;
658 dbltime = GetDoubleClickTime();
661 * Set up the session-control options on the system menu.
664 HMENU m = GetSystemMenu(hwnd, FALSE);
668 AppendMenu(m, MF_SEPARATOR, 0, 0);
669 if (cfg.protocol == PROT_TELNET) {
671 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
672 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
673 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
674 AppendMenu(p, MF_SEPARATOR, 0, 0);
675 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
676 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
677 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
678 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
679 AppendMenu(p, MF_SEPARATOR, 0, 0);
680 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
681 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
682 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
683 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
684 AppendMenu(p, MF_SEPARATOR, 0, 0);
685 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
686 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
687 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
689 AppendMenu(m, MF_SEPARATOR, 0, 0);
691 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
692 AppendMenu(m, MF_SEPARATOR, 0, 0);
693 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
694 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
696 get_sesslist(&sesslist, TRUE);
698 i < ((sesslist.nsessions < 256) ? sesslist.nsessions : 256);
700 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
701 sesslist.sessions[i]);
702 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
703 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
704 AppendMenu(m, MF_SEPARATOR, 0, 0);
705 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
706 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
707 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
708 AppendMenu(m, MF_SEPARATOR, 0, 0);
709 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
710 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
711 AppendMenu(m, MF_SEPARATOR, 0, 0);
713 AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
714 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
718 * Set up the initial input locale.
720 set_input_locale(GetKeyboardLayout(0));
723 * Open the initial log file if there is one.
728 * Finally show the window!
730 ShowWindow(hwnd, show);
731 SetForegroundWindow(hwnd);
734 * Set the palette up.
740 term->has_focus = (GetForegroundWindow() == hwnd);
743 if (GetMessage(&msg, NULL, 0, 0) == 1) {
744 int timer_id = 0, long_timer = 0;
746 while (msg.message != WM_QUIT) {
747 /* Sometimes DispatchMessage calls routines that use their own
748 * GetMessage loop, setup this timer so we get some control back.
750 * Also call term_update() from the timer so that if the host
751 * is sending data flat out we still do redraws.
753 if (timer_id && long_timer) {
754 KillTimer(hwnd, timer_id);
755 long_timer = timer_id = 0;
758 timer_id = SetTimer(hwnd, 1, 20, NULL);
759 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
760 DispatchMessage(&msg);
762 /* Make sure we blink everything that needs it. */
765 /* Send the paste buffer if there's anything to send */
768 /* If there's nothing new in the queue then we can do everything
769 * we've delayed, reading the socket, writing, and repainting
772 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
775 if (pending_netevent) {
776 enact_pending_netevent();
778 /* Force the cursor blink on */
781 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
785 /* Okay there is now nothing to do so we make sure the screen is
786 * completely up to date then tell windows to call us in a little
790 KillTimer(hwnd, timer_id);
794 if (GetCapture() != hwnd ||
796 !(cfg.mouse_override && is_shift_pressed())))
801 flash_window(1); /* maintain */
803 /* The messages seem unreliable; especially if we're being tricky */
804 term->has_focus = (GetForegroundWindow() == hwnd);
807 /* Hmm, term_update didn't want to do an update too soon ... */
808 timer_id = SetTimer(hwnd, 1, 50, NULL);
809 else if (!term->has_focus)
810 timer_id = SetTimer(hwnd, 1, 500, NULL);
812 timer_id = SetTimer(hwnd, 1, 100, NULL);
815 /* There's no point rescanning everything in the message queue
816 * so we do an apparently unnecessary wait here
819 if (GetMessage(&msg, NULL, 0, 0) != 1)
824 cleanup_exit(msg.wParam); /* this doesn't return... */
825 return msg.wParam; /* ... but optimiser doesn't know */
831 void cleanup_exit(int code)
844 if (cfg.protocol == PROT_SSH) {
855 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
857 char *do_select(SOCKET skt, int startup)
862 events = (FD_CONNECT | FD_READ | FD_WRITE |
863 FD_OOB | FD_CLOSE | FD_ACCEPT);
868 return "do_select(): internal error (hwnd==NULL)";
869 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
870 switch (WSAGetLastError()) {
872 return "Network is down";
874 return "WSAAsyncSelect(): unknown error";
881 * set or clear the "raw mouse message" mode
883 void set_raw_mouse_mode(void *frontend, int activate)
885 activate = activate && !cfg.no_mouse_rep;
886 send_raw_mouse = activate;
887 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
891 * Print a message box and close the connection.
893 void connection_fatal(void *frontend, char *fmt, ...)
899 vsprintf(stuff, fmt, ap);
901 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
902 if (cfg.close_on_exit == COE_ALWAYS)
905 session_closed = TRUE;
906 SetWindowText(hwnd, "PuTTY (inactive)");
911 * Report an error at the command-line parsing stage.
913 void cmdline_error(char *fmt, ...)
919 vsprintf(stuff, fmt, ap);
921 MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
926 * Actually do the job requested by a WM_NETEVENT
928 static void enact_pending_netevent(void)
930 static int reentering = 0;
931 extern int select_result(WPARAM, LPARAM);
935 return; /* don't unpend the pending */
937 pending_netevent = FALSE;
940 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
943 if (ret == 0 && !session_closed) {
944 /* Abnormal exits will already have set session_closed and taken
945 * appropriate action. */
946 if (cfg.close_on_exit == COE_ALWAYS ||
947 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
949 session_closed = TRUE;
950 SetWindowText(hwnd, "PuTTY (inactive)");
951 MessageBox(hwnd, "Connection closed by remote host",
952 "PuTTY", MB_OK | MB_ICONINFORMATION);
958 * Copy the colour palette from the configuration data into defpal.
959 * This is non-trivial because the colour indices are different.
961 static void cfgtopalette(void)
964 static const int ww[] = {
965 6, 7, 8, 9, 10, 11, 12, 13,
966 14, 15, 16, 17, 18, 19, 20, 21,
967 0, 1, 2, 3, 4, 4, 5, 5
970 for (i = 0; i < 24; i++) {
972 defpal[i].rgbtRed = cfg.colours[w][0];
973 defpal[i].rgbtGreen = cfg.colours[w][1];
974 defpal[i].rgbtBlue = cfg.colours[w][2];
979 * Set up the colour palette.
981 static void init_palette(void)
984 HDC hdc = GetDC(hwnd);
986 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
987 logpal = smalloc(sizeof(*logpal)
988 - sizeof(logpal->palPalEntry)
989 + NCOLOURS * sizeof(PALETTEENTRY));
990 logpal->palVersion = 0x300;
991 logpal->palNumEntries = NCOLOURS;
992 for (i = 0; i < NCOLOURS; i++) {
993 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
994 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
995 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
996 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
998 pal = CreatePalette(logpal);
1000 SelectPalette(hdc, pal, FALSE);
1001 RealizePalette(hdc);
1002 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1005 ReleaseDC(hwnd, hdc);
1008 for (i = 0; i < NCOLOURS; i++)
1009 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1010 defpal[i].rgbtGreen,
1011 defpal[i].rgbtBlue);
1013 for (i = 0; i < NCOLOURS; i++)
1014 colours[i] = RGB(defpal[i].rgbtRed,
1015 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1019 * Initialise all the fonts we will need initially. There may be as many as
1020 * three or as few as one. The other (poentially) twentyone fonts are done
1021 * if/when they are needed.
1025 * - check the font width and height, correcting our guesses if
1028 * - verify that the bold font is the same width as the ordinary
1029 * one, and engage shadow bolding if not.
1031 * - verify that the underlined font is the same width as the
1032 * ordinary one (manual underlining by means of line drawing can
1033 * be done in a pinch).
1035 static void init_fonts(int pick_width, int pick_height)
1042 int fw_dontcare, fw_bold;
1044 for (i = 0; i < FONT_MAXNO; i++)
1047 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1048 und_mode = UND_FONT;
1050 if (cfg.fontisbold) {
1051 fw_dontcare = FW_BOLD;
1054 fw_dontcare = FW_DONTCARE;
1061 font_height = pick_height;
1063 font_height = cfg.fontheight;
1064 if (font_height > 0) {
1066 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1069 font_width = pick_width;
1071 #define f(i,c,w,u) \
1072 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1073 c, OUT_DEFAULT_PRECIS, \
1074 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1075 FIXED_PITCH | FF_DONTCARE, cfg.font)
1077 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
1079 lfont.lfHeight = font_height;
1080 lfont.lfWidth = font_width;
1081 lfont.lfEscapement = 0;
1082 lfont.lfOrientation = 0;
1083 lfont.lfWeight = fw_dontcare;
1084 lfont.lfItalic = FALSE;
1085 lfont.lfUnderline = FALSE;
1086 lfont.lfStrikeOut = FALSE;
1087 lfont.lfCharSet = cfg.fontcharset;
1088 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1089 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1090 lfont.lfQuality = DEFAULT_QUALITY;
1091 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1092 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
1094 SelectObject(hdc, fonts[FONT_NORMAL]);
1095 GetTextMetrics(hdc, &tm);
1097 if (pick_width == 0 || pick_height == 0) {
1098 font_height = tm.tmHeight;
1099 font_width = tm.tmAveCharWidth;
1101 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1103 #ifdef RDB_DEBUG_PATCH
1104 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1105 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1110 DWORD cset = tm.tmCharSet;
1111 memset(&info, 0xFF, sizeof(info));
1113 /* !!! Yes the next line is right */
1114 if (cset == OEM_CHARSET)
1115 font_codepage = GetOEMCP();
1117 if (TranslateCharsetInfo
1118 ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
1123 GetCPInfo(font_codepage, &cpinfo);
1124 dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1127 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
1130 * Some fonts, e.g. 9-pt Courier, draw their underlines
1131 * outside their character cell. We successfully prevent
1132 * screen corruption by clipping the text output, but then
1133 * we lose the underline completely. Here we try to work
1134 * out whether this is such a font, and if it is, we set a
1135 * flag that causes underlines to be drawn by hand.
1137 * Having tried other more sophisticated approaches (such
1138 * as examining the TEXTMETRIC structure or requesting the
1139 * height of a string), I think we'll do this the brute
1140 * force way: we create a small bitmap, draw an underlined
1141 * space on it, and test to see whether any pixels are
1142 * foreground-coloured. (Since we expect the underline to
1143 * go all the way across the character cell, we only search
1144 * down a single column of the bitmap, half way across.)
1148 HBITMAP und_bm, und_oldbm;
1152 und_dc = CreateCompatibleDC(hdc);
1153 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1154 und_oldbm = SelectObject(und_dc, und_bm);
1155 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1156 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1157 SetTextColor(und_dc, RGB(255, 255, 255));
1158 SetBkColor(und_dc, RGB(0, 0, 0));
1159 SetBkMode(und_dc, OPAQUE);
1160 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1162 for (i = 0; i < font_height; i++) {
1163 c = GetPixel(und_dc, font_width / 2, i);
1164 if (c != RGB(0, 0, 0))
1167 SelectObject(und_dc, und_oldbm);
1168 DeleteObject(und_bm);
1171 und_mode = UND_LINE;
1172 DeleteObject(fonts[FONT_UNDERLINE]);
1173 fonts[FONT_UNDERLINE] = 0;
1177 if (bold_mode == BOLD_FONT) {
1178 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1182 descent = tm.tmAscent + 1;
1183 if (descent >= font_height)
1184 descent = font_height - 1;
1186 for (i = 0; i < 3; i++) {
1188 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1189 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1196 ReleaseDC(hwnd, hdc);
1198 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1199 und_mode = UND_LINE;
1200 DeleteObject(fonts[FONT_UNDERLINE]);
1201 fonts[FONT_UNDERLINE] = 0;
1204 if (bold_mode == BOLD_FONT &&
1205 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1206 bold_mode = BOLD_SHADOW;
1207 DeleteObject(fonts[FONT_BOLD]);
1208 fonts[FONT_BOLD] = 0;
1210 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1215 static void another_font(int fontno)
1218 int fw_dontcare, fw_bold;
1222 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1225 basefont = (fontno & ~(FONT_BOLDUND));
1226 if (basefont != fontno && !fontflag[basefont])
1227 another_font(basefont);
1229 if (cfg.fontisbold) {
1230 fw_dontcare = FW_BOLD;
1233 fw_dontcare = FW_DONTCARE;
1237 c = cfg.fontcharset;
1243 if (fontno & FONT_WIDE)
1245 if (fontno & FONT_NARROW)
1247 if (fontno & FONT_OEM)
1249 if (fontno & FONT_BOLD)
1251 if (fontno & FONT_UNDERLINE)
1255 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1256 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1257 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1258 FIXED_PITCH | FF_DONTCARE, s);
1260 fontflag[fontno] = 1;
1263 static void deinit_fonts(void)
1266 for (i = 0; i < FONT_MAXNO; i++) {
1268 DeleteObject(fonts[i]);
1274 void request_resize(void *frontend, int w, int h)
1278 /* If the window is maximized supress resizing attempts */
1279 if (IsZoomed(hwnd)) {
1280 if (cfg.resize_action == RESIZE_TERM)
1284 if (cfg.resize_action == RESIZE_DISABLED) return;
1285 if (h == term->rows && w == term->cols) return;
1287 /* Sanity checks ... */
1289 static int first_time = 1;
1292 switch (first_time) {
1294 /* Get the size of the screen */
1295 if (get_fullscreen_rect(&ss))
1296 /* first_time = 0 */ ;
1302 /* Make sure the values are sane */
1303 width = (ss.right - ss.left - extra_width) / 4;
1304 height = (ss.bottom - ss.top - extra_height) / 6;
1306 if (w > width || h > height)
1315 term_size(term, h, w, cfg.savelines);
1317 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1318 width = extra_width + font_width * w;
1319 height = extra_height + font_height * h;
1321 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1322 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1323 SWP_NOMOVE | SWP_NOZORDER);
1327 InvalidateRect(hwnd, NULL, TRUE);
1330 static void reset_window(int reinit) {
1332 * This function decides how to resize or redraw when the
1333 * user changes something.
1335 * This function doesn't like to change the terminal size but if the
1336 * font size is locked that may be it's only soluion.
1338 int win_width, win_height;
1341 #ifdef RDB_DEBUG_PATCH
1342 debug((27, "reset_window()"));
1345 /* Current window sizes ... */
1346 GetWindowRect(hwnd, &wr);
1347 GetClientRect(hwnd, &cr);
1349 win_width = cr.right - cr.left;
1350 win_height = cr.bottom - cr.top;
1352 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1354 /* Are we being forced to reload the fonts ? */
1356 #ifdef RDB_DEBUG_PATCH
1357 debug((27, "reset_window() -- Forced deinit"));
1363 /* Oh, looks like we're minimised */
1364 if (win_width == 0 || win_height == 0)
1367 /* Is the window out of position ? */
1369 (offset_width != (win_width-font_width*term->cols)/2 ||
1370 offset_height != (win_height-font_height*term->rows)/2) ){
1371 offset_width = (win_width-font_width*term->cols)/2;
1372 offset_height = (win_height-font_height*term->rows)/2;
1373 InvalidateRect(hwnd, NULL, TRUE);
1374 #ifdef RDB_DEBUG_PATCH
1375 debug((27, "reset_window() -> Reposition terminal"));
1379 if (IsZoomed(hwnd)) {
1380 /* We're fullscreen, this means we must not change the size of
1381 * the window so it's the font size or the terminal itself.
1384 extra_width = wr.right - wr.left - cr.right + cr.left;
1385 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1387 if (cfg.resize_action != RESIZE_TERM) {
1388 if ( font_width != win_width/term->cols ||
1389 font_height != win_height/term->rows) {
1391 init_fonts(win_width/term->cols, win_height/term->rows);
1392 offset_width = (win_width-font_width*term->cols)/2;
1393 offset_height = (win_height-font_height*term->rows)/2;
1394 InvalidateRect(hwnd, NULL, TRUE);
1395 #ifdef RDB_DEBUG_PATCH
1396 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1397 font_width, font_height));
1401 if ( font_width != win_width/term->cols ||
1402 font_height != win_height/term->rows) {
1403 /* Our only choice at this point is to change the
1404 * size of the terminal; Oh well.
1406 term_size(term, win_height/font_height, win_width/font_width,
1408 offset_width = (win_width-font_width*term->cols)/2;
1409 offset_height = (win_height-font_height*term->rows)/2;
1410 InvalidateRect(hwnd, NULL, TRUE);
1411 #ifdef RDB_DEBUG_PATCH
1412 debug((27, "reset_window() -> Zoomed term_size"));
1419 /* Hmm, a force re-init means we should ignore the current window
1420 * so we resize to the default font size.
1423 #ifdef RDB_DEBUG_PATCH
1424 debug((27, "reset_window() -> Forced re-init"));
1427 offset_width = offset_height = cfg.window_border;
1428 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1429 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1431 if (win_width != font_width*term->cols + offset_width*2 ||
1432 win_height != font_height*term->rows + offset_height*2) {
1434 /* If this is too large windows will resize it to the maximum
1435 * allowed window size, we will then be back in here and resize
1436 * the font or terminal to fit.
1438 SetWindowPos(hwnd, NULL, 0, 0,
1439 font_width*term->cols + extra_width,
1440 font_height*term->rows + extra_height,
1441 SWP_NOMOVE | SWP_NOZORDER);
1444 InvalidateRect(hwnd, NULL, TRUE);
1448 /* Okay the user doesn't want us to change the font so we try the
1449 * window. But that may be too big for the screen which forces us
1450 * to change the terminal.
1452 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1453 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1455 offset_width = offset_height = cfg.window_border;
1456 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1457 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1459 if (win_width != font_width*term->cols + offset_width*2 ||
1460 win_height != font_height*term->rows + offset_height*2) {
1465 get_fullscreen_rect(&ss);
1467 width = (ss.right - ss.left - extra_width) / font_width;
1468 height = (ss.bottom - ss.top - extra_height) / font_height;
1471 if ( term->rows > height || term->cols > width ) {
1472 if (cfg.resize_action == RESIZE_EITHER) {
1473 /* Make the font the biggest we can */
1474 if (term->cols > width)
1475 font_width = (ss.right - ss.left - extra_width)
1477 if (term->rows > height)
1478 font_height = (ss.bottom - ss.top - extra_height)
1482 init_fonts(font_width, font_height);
1484 width = (ss.right - ss.left - extra_width) / font_width;
1485 height = (ss.bottom - ss.top - extra_height) / font_height;
1487 if ( height > term->rows ) height = term->rows;
1488 if ( width > term->cols ) width = term->cols;
1489 term_size(term, height, width, cfg.savelines);
1490 #ifdef RDB_DEBUG_PATCH
1491 debug((27, "reset_window() -> term resize to (%d,%d)",
1497 SetWindowPos(hwnd, NULL, 0, 0,
1498 font_width*term->cols + extra_width,
1499 font_height*term->rows + extra_height,
1500 SWP_NOMOVE | SWP_NOZORDER);
1502 InvalidateRect(hwnd, NULL, TRUE);
1503 #ifdef RDB_DEBUG_PATCH
1504 debug((27, "reset_window() -> window resize to (%d,%d)",
1505 font_width*term->cols + extra_width,
1506 font_height*term->rows + extra_height));
1512 /* We're allowed to or must change the font but do we want to ? */
1514 if (font_width != (win_width-cfg.window_border*2)/term->cols ||
1515 font_height != (win_height-cfg.window_border*2)/term->rows) {
1518 init_fonts((win_width-cfg.window_border*2)/term->cols,
1519 (win_height-cfg.window_border*2)/term->rows);
1520 offset_width = (win_width-font_width*term->cols)/2;
1521 offset_height = (win_height-font_height*term->rows)/2;
1523 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1524 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1526 InvalidateRect(hwnd, NULL, TRUE);
1527 #ifdef RDB_DEBUG_PATCH
1528 debug((25, "reset_window() -> font resize to (%d,%d)",
1529 font_width, font_height));
1534 static void set_input_locale(HKL kl)
1538 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1539 lbuf, sizeof(lbuf));
1541 kbd_codepage = atoi(lbuf);
1544 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1546 int thistime = GetMessageTime();
1548 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1549 lastbtn = MBT_NOTHING;
1550 term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt);
1554 if (lastbtn == b && thistime - lasttime < dbltime) {
1555 lastact = (lastact == MA_CLICK ? MA_2CLK :
1556 lastact == MA_2CLK ? MA_3CLK :
1557 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1562 if (lastact != MA_NOTHING)
1563 term_mouse(term, b, lastact, x, y, shift, ctrl, alt);
1564 lasttime = thistime;
1568 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1569 * into a cooked one (SELECT, EXTEND, PASTE).
1571 Mouse_Button translate_button(void *frontend, Mouse_Button button)
1573 if (button == MBT_LEFT)
1575 if (button == MBT_MIDDLE)
1576 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1577 if (button == MBT_RIGHT)
1578 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1579 return 0; /* shouldn't happen */
1582 static void show_mouseptr(int show)
1584 static int cursor_visible = 1;
1585 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1587 if (cursor_visible && !show)
1589 else if (!cursor_visible && show)
1591 cursor_visible = show;
1594 static int is_alt_pressed(void)
1597 int r = GetKeyboardState(keystate);
1600 if (keystate[VK_MENU] & 0x80)
1602 if (keystate[VK_RMENU] & 0x80)
1607 static int is_shift_pressed(void)
1610 int r = GetKeyboardState(keystate);
1613 if (keystate[VK_SHIFT] & 0x80)
1618 static int resizing;
1620 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1621 WPARAM wParam, LPARAM lParam)
1624 static int ignore_clip = FALSE;
1625 static int need_backend_resize = FALSE;
1626 static int fullscr_on_max = FALSE;
1630 if (pending_netevent)
1631 enact_pending_netevent();
1632 if (GetCapture() != hwnd ||
1633 (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
1639 if (cfg.ping_interval > 0) {
1642 if (now - last_movement > cfg.ping_interval) {
1643 back->special(backhandle, TS_PING);
1644 last_movement = now;
1647 net_pending_errors();
1653 if (!cfg.warn_on_close || session_closed ||
1655 "Are you sure you want to close this session?",
1656 "PuTTY Exit Confirmation",
1657 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1658 DestroyWindow(hwnd);
1665 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1677 PROCESS_INFORMATION pi;
1678 HANDLE filemap = NULL;
1680 if (wParam == IDM_DUPSESS) {
1682 * Allocate a file-mapping memory chunk for the
1685 SECURITY_ATTRIBUTES sa;
1688 sa.nLength = sizeof(sa);
1689 sa.lpSecurityDescriptor = NULL;
1690 sa.bInheritHandle = TRUE;
1691 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1694 0, sizeof(Config), NULL);
1696 p = (Config *) MapViewOfFile(filemap,
1698 0, 0, sizeof(Config));
1700 *p = cfg; /* structure copy */
1704 sprintf(c, "putty &%p", filemap);
1706 } else if (wParam == IDM_SAVEDSESS) {
1707 if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
1709 sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
1710 cl = smalloc(16 + strlen(session));
1711 /* 8, but play safe */
1714 /* not a very important failure mode */
1716 sprintf(cl, "putty @%s", session);
1724 GetModuleFileName(NULL, b, sizeof(b) - 1);
1726 si.lpReserved = NULL;
1727 si.lpDesktop = NULL;
1731 si.lpReserved2 = NULL;
1732 CreateProcess(b, cl, NULL, NULL, TRUE,
1733 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1736 CloseHandle(filemap);
1746 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1749 if (!do_reconfig(hwnd))
1753 /* Disable full-screen if resizing forbidden */
1754 HMENU m = GetSystemMenu (hwnd, FALSE);
1755 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1756 (cfg.resize_action == RESIZE_DISABLED)
1757 ? MF_GRAYED : MF_ENABLED);
1758 /* Gracefully unzoom if necessary */
1759 if (IsZoomed(hwnd) &&
1760 (cfg.resize_action == RESIZE_DISABLED)) {
1761 ShowWindow(hwnd, SW_RESTORE);
1765 /* Pass new config data to the logging module */
1766 log_reconfig(logctx, &cfg);
1770 * Flush the line discipline's edit buffer in the
1771 * case where local editing has just been disabled.
1773 ldisc_send(ldisc, NULL, 0, 0);
1781 /* Pass new config data to the terminal */
1782 term_reconfig(term, &cfg);
1784 /* Pass new config data to the back end */
1785 back->reconfig(backhandle, &cfg);
1787 /* Screen size changed ? */
1788 if (cfg.height != prev_cfg.height ||
1789 cfg.width != prev_cfg.width ||
1790 cfg.savelines != prev_cfg.savelines ||
1791 cfg.resize_action == RESIZE_FONT ||
1792 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1793 cfg.resize_action == RESIZE_DISABLED)
1794 term_size(term, cfg.height, cfg.width, cfg.savelines);
1796 /* Enable or disable the scroll bar, etc */
1798 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1799 LONG nexflag, exflag =
1800 GetWindowLong(hwnd, GWL_EXSTYLE);
1803 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1804 if (cfg.alwaysontop) {
1805 nexflag |= WS_EX_TOPMOST;
1806 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1807 SWP_NOMOVE | SWP_NOSIZE);
1809 nexflag &= ~(WS_EX_TOPMOST);
1810 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1811 SWP_NOMOVE | SWP_NOSIZE);
1814 if (cfg.sunken_edge)
1815 nexflag |= WS_EX_CLIENTEDGE;
1817 nexflag &= ~(WS_EX_CLIENTEDGE);
1820 if (is_full_screen() ?
1821 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1824 nflg &= ~WS_VSCROLL;
1826 if (cfg.resize_action == RESIZE_DISABLED ||
1828 nflg &= ~WS_THICKFRAME;
1830 nflg |= WS_THICKFRAME;
1832 if (cfg.resize_action == RESIZE_DISABLED)
1833 nflg &= ~WS_MAXIMIZEBOX;
1835 nflg |= WS_MAXIMIZEBOX;
1837 if (nflg != flag || nexflag != exflag) {
1839 SetWindowLong(hwnd, GWL_STYLE, nflg);
1840 if (nexflag != exflag)
1841 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1843 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1844 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1845 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1853 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1858 set_title(NULL, cfg.wintitle);
1859 if (IsIconic(hwnd)) {
1861 cfg.win_name_always ? window_name :
1865 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1866 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1867 cfg.fontisbold != prev_cfg.fontisbold ||
1868 cfg.fontheight != prev_cfg.fontheight ||
1869 cfg.fontcharset != prev_cfg.fontcharset ||
1870 cfg.vtmode != prev_cfg.vtmode ||
1871 cfg.bold_colour != prev_cfg.bold_colour ||
1872 cfg.resize_action == RESIZE_DISABLED ||
1873 cfg.resize_action == RESIZE_EITHER ||
1874 (cfg.resize_action != prev_cfg.resize_action))
1877 InvalidateRect(hwnd, NULL, TRUE);
1878 reset_window(init_lvl);
1879 net_pending_errors();
1890 ldisc_send(ldisc, NULL, 0, 0);
1893 back->special(backhandle, TS_AYT);
1894 net_pending_errors();
1897 back->special(backhandle, TS_BRK);
1898 net_pending_errors();
1901 back->special(backhandle, TS_SYNCH);
1902 net_pending_errors();
1905 back->special(backhandle, TS_EC);
1906 net_pending_errors();
1909 back->special(backhandle, TS_EL);
1910 net_pending_errors();
1913 back->special(backhandle, TS_GA);
1914 net_pending_errors();
1917 back->special(backhandle, TS_NOP);
1918 net_pending_errors();
1921 back->special(backhandle, TS_ABORT);
1922 net_pending_errors();
1925 back->special(backhandle, TS_AO);
1926 net_pending_errors();
1929 back->special(backhandle, TS_IP);
1930 net_pending_errors();
1933 back->special(backhandle, TS_SUSP);
1934 net_pending_errors();
1937 back->special(backhandle, TS_EOR);
1938 net_pending_errors();
1941 back->special(backhandle, TS_EOF);
1942 net_pending_errors();
1948 WinHelp(hwnd, help_path,
1949 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1953 * We get this if the System menu has been activated
1960 * We get this if the System menu has been activated
1961 * using the keyboard. This might happen from within
1962 * TranslateKey, in which case it really wants to be
1963 * followed by a `space' character to actually _bring
1964 * the menu up_ rather than just sitting there in
1965 * `ready to appear' state.
1967 show_mouseptr(1); /* make sure pointer is visible */
1969 PostMessage(hwnd, WM_CHAR, ' ', 0);
1971 case IDM_FULLSCREEN:
1975 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1976 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1981 #define X_POS(l) ((int)(short)LOWORD(l))
1982 #define Y_POS(l) ((int)(short)HIWORD(l))
1984 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1985 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1986 case WM_LBUTTONDOWN:
1987 case WM_MBUTTONDOWN:
1988 case WM_RBUTTONDOWN:
1996 case WM_LBUTTONDOWN:
2000 case WM_MBUTTONDOWN:
2001 button = MBT_MIDDLE;
2004 case WM_RBUTTONDOWN:
2013 button = MBT_MIDDLE;
2021 button = press = 0; /* shouldn't happen */
2025 * Special case: in full-screen mode, if the left
2026 * button is clicked in the very top left corner of the
2027 * window, we put up the System menu instead of doing
2030 if (is_full_screen() && press && button == MBT_LEFT &&
2031 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2032 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2037 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2038 wParam & MK_SHIFT, wParam & MK_CONTROL,
2042 term_mouse(term, button, MA_RELEASE,
2043 TO_CHR_X(X_POS(lParam)),
2044 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2045 wParam & MK_CONTROL, is_alt_pressed());
2053 * Add the mouse position and message time to the random
2056 noise_ultralight(lParam);
2058 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2059 GetCapture() == hwnd) {
2061 if (wParam & MK_LBUTTON)
2063 else if (wParam & MK_MBUTTON)
2067 term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
2068 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2069 wParam & MK_CONTROL, is_alt_pressed());
2072 case WM_NCMOUSEMOVE:
2074 noise_ultralight(lParam);
2076 case WM_IGNORE_CLIP:
2077 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2079 case WM_DESTROYCLIPBOARD:
2081 term_deselect(term);
2082 ignore_clip = FALSE;
2088 hdc = BeginPaint(hwnd, &p);
2090 SelectPalette(hdc, pal, TRUE);
2091 RealizePalette(hdc);
2093 term_paint(term, hdc,
2094 (p.rcPaint.left-offset_width)/font_width,
2095 (p.rcPaint.top-offset_height)/font_height,
2096 (p.rcPaint.right-offset_width-1)/font_width,
2097 (p.rcPaint.bottom-offset_height-1)/font_height,
2101 p.rcPaint.left < offset_width ||
2102 p.rcPaint.top < offset_height ||
2103 p.rcPaint.right >= offset_width + font_width*term->cols ||
2104 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2106 HBRUSH fillcolour, oldbrush;
2108 fillcolour = CreateSolidBrush (
2109 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2110 oldbrush = SelectObject(hdc, fillcolour);
2111 edge = CreatePen(PS_SOLID, 0,
2112 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2113 oldpen = SelectObject(hdc, edge);
2116 * Jordan Russell reports that this apparently
2117 * ineffectual IntersectClipRect() call masks a
2118 * Windows NT/2K bug causing strange display
2119 * problems when the PuTTY window is taller than
2120 * the primary monitor. It seems harmless enough...
2122 IntersectClipRect(hdc,
2123 p.rcPaint.left, p.rcPaint.top,
2124 p.rcPaint.right, p.rcPaint.bottom);
2126 ExcludeClipRect(hdc,
2127 offset_width, offset_height,
2128 offset_width+font_width*term->cols,
2129 offset_height+font_height*term->rows);
2131 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2132 p.rcPaint.right, p.rcPaint.bottom);
2134 // SelectClipRgn(hdc, NULL);
2136 SelectObject(hdc, oldbrush);
2137 DeleteObject(fillcolour);
2138 SelectObject(hdc, oldpen);
2141 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2142 SelectObject(hdc, GetStockObject(WHITE_PEN));
2148 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2149 * but the only one that's likely to try to overload us is FD_READ.
2150 * This means buffering just one is fine.
2152 if (pending_netevent)
2153 enact_pending_netevent();
2155 pending_netevent = TRUE;
2156 pend_netevent_wParam = wParam;
2157 pend_netevent_lParam = lParam;
2158 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2159 enact_pending_netevent();
2161 time(&last_movement);
2164 term->has_focus = TRUE;
2165 CreateCaret(hwnd, caretbm, font_width, font_height);
2167 flash_window(0); /* stop */
2174 term->has_focus = FALSE;
2176 caret_x = caret_y = -1; /* ensure caret is replaced next time */
2180 case WM_ENTERSIZEMOVE:
2181 #ifdef RDB_DEBUG_PATCH
2182 debug((27, "WM_ENTERSIZEMOVE"));
2186 need_backend_resize = FALSE;
2188 case WM_EXITSIZEMOVE:
2191 #ifdef RDB_DEBUG_PATCH
2192 debug((27, "WM_EXITSIZEMOVE"));
2194 if (need_backend_resize) {
2195 term_size(term, cfg.height, cfg.width, cfg.savelines);
2196 InvalidateRect(hwnd, NULL, TRUE);
2201 * This does two jobs:
2202 * 1) Keep the sizetip uptodate
2203 * 2) Make sure the window size is _stepped_ in units of the font size.
2205 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2206 int width, height, w, h, ew, eh;
2207 LPRECT r = (LPRECT) lParam;
2209 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2210 (cfg.height != term->rows || cfg.width != term->cols )) {
2212 * Great! It seems that both the terminal size and the
2213 * font size have been changed and the user is now dragging.
2215 * It will now be difficult to get back to the configured
2218 * This would be easier but it seems to be too confusing.
2220 term_size(term, cfg.height, cfg.width, cfg.savelines);
2223 cfg.height=term->rows; cfg.width=term->cols;
2225 InvalidateRect(hwnd, NULL, TRUE);
2226 need_backend_resize = TRUE;
2229 width = r->right - r->left - extra_width;
2230 height = r->bottom - r->top - extra_height;
2231 w = (width + font_width / 2) / font_width;
2234 h = (height + font_height / 2) / font_height;
2237 UpdateSizeTip(hwnd, w, h);
2238 ew = width - w * font_width;
2239 eh = height - h * font_height;
2241 if (wParam == WMSZ_LEFT ||
2242 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2248 if (wParam == WMSZ_TOP ||
2249 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2259 int width, height, w, h, rv = 0;
2260 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2261 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2262 LPRECT r = (LPRECT) lParam;
2264 width = r->right - r->left - ex_width;
2265 height = r->bottom - r->top - ex_height;
2267 w = (width + term->cols/2)/term->cols;
2268 h = (height + term->rows/2)/term->rows;
2269 if ( r->right != r->left + w*term->cols + ex_width)
2272 if (wParam == WMSZ_LEFT ||
2273 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2274 r->left = r->right - w*term->cols - ex_width;
2276 r->right = r->left + w*term->cols + ex_width;
2278 if (r->bottom != r->top + h*term->rows + ex_height)
2281 if (wParam == WMSZ_TOP ||
2282 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2283 r->top = r->bottom - h*term->rows - ex_height;
2285 r->bottom = r->top + h*term->rows + ex_height;
2289 /* break; (never reached) */
2290 case WM_FULLSCR_ON_MAX:
2291 fullscr_on_max = TRUE;
2294 sys_cursor_update();
2297 #ifdef RDB_DEBUG_PATCH
2298 debug((27, "WM_SIZE %s (%d,%d)",
2299 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2300 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2301 (wParam == SIZE_RESTORED && resizing) ? "to":
2302 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2304 LOWORD(lParam), HIWORD(lParam)));
2306 if (wParam == SIZE_MINIMIZED)
2308 cfg.win_name_always ? window_name : icon_name);
2309 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2310 SetWindowText(hwnd, window_name);
2311 if (wParam == SIZE_RESTORED)
2312 clear_full_screen();
2313 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2314 fullscr_on_max = FALSE;
2318 if (cfg.resize_action == RESIZE_DISABLED) {
2319 /* A resize, well it better be a minimize. */
2323 int width, height, w, h;
2325 width = LOWORD(lParam);
2326 height = HIWORD(lParam);
2329 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2331 prev_rows = term->rows;
2332 prev_cols = term->cols;
2333 if (cfg.resize_action == RESIZE_TERM) {
2334 w = width / font_width;
2336 h = height / font_height;
2339 term_size(term, h, w, cfg.savelines);
2342 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2344 if (cfg.resize_action == RESIZE_TERM)
2345 term_size(term, prev_rows, prev_cols, cfg.savelines);
2346 if (cfg.resize_action != RESIZE_FONT)
2351 /* This is an unexpected resize, these will normally happen
2352 * if the window is too large. Probably either the user
2353 * selected a huge font or the screen size has changed.
2355 * This is also called with minimize.
2357 else reset_window(-1);
2361 * Don't call back->size in mid-resize. (To prevent
2362 * massive numbers of resize events getting sent
2363 * down the connection during an NT opaque drag.)
2366 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2367 need_backend_resize = TRUE;
2368 w = (width-cfg.window_border*2) / font_width;
2370 h = (height-cfg.window_border*2) / font_height;
2379 sys_cursor_update();
2382 switch (LOWORD(wParam)) {
2384 term_scroll(term, -1, 0);
2387 term_scroll(term, +1, 0);
2390 term_scroll(term, 0, +1);
2393 term_scroll(term, 0, -1);
2396 term_scroll(term, 0, +term->rows / 2);
2399 term_scroll(term, 0, -term->rows / 2);
2401 case SB_THUMBPOSITION:
2403 term_scroll(term, 1, HIWORD(wParam));
2407 case WM_PALETTECHANGED:
2408 if ((HWND) wParam != hwnd && pal != NULL) {
2409 HDC hdc = get_ctx(NULL);
2411 if (RealizePalette(hdc) > 0)
2417 case WM_QUERYNEWPALETTE:
2419 HDC hdc = get_ctx(NULL);
2421 if (RealizePalette(hdc) > 0)
2433 * Add the scan code and keypress timing to the random
2436 noise_ultralight(lParam);
2439 * We don't do TranslateMessage since it disassociates the
2440 * resulting CHAR message from the KEYDOWN that sparked it,
2441 * which we occasionally don't want. Instead, we process
2442 * KEYDOWN, and call the Win32 translator functions so that
2443 * we get the translations under _our_ control.
2446 unsigned char buf[20];
2449 if (wParam == VK_PROCESSKEY) {
2452 m.message = WM_KEYDOWN;
2454 m.lParam = lParam & 0xdfff;
2455 TranslateMessage(&m);
2457 len = TranslateKey(message, wParam, lParam, buf);
2459 return DefWindowProc(hwnd, message, wParam, lParam);
2463 * Interrupt an ongoing paste. I'm not sure
2464 * this is sensible, but for the moment it's
2465 * preferable to having to faff about buffering
2471 * We need not bother about stdin backlogs
2472 * here, because in GUI PuTTY we can't do
2473 * anything about it anyway; there's no means
2474 * of asking Windows to hold off on KEYDOWN
2475 * messages. We _have_ to buffer everything
2478 term_seen_key_event(term);
2479 ldisc_send(ldisc, buf, len, 1);
2484 net_pending_errors();
2486 case WM_INPUTLANGCHANGE:
2487 /* wParam == Font number */
2488 /* lParam == Locale */
2489 set_input_locale((HKL)lParam);
2490 sys_cursor_update();
2493 if(wParam == IMN_SETOPENSTATUS) {
2494 HIMC hImc = ImmGetContext(hwnd);
2495 ImmSetCompositionFont(hImc, &lfont);
2496 ImmReleaseContext(hwnd, hImc);
2500 case WM_IME_COMPOSITION:
2506 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2507 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2509 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2510 break; /* fall back to DefWindowProc */
2512 hIMC = ImmGetContext(hwnd);
2513 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2517 buff = (char*) smalloc(n);
2518 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2520 * Jaeyoun Chung reports that Korean character
2521 * input doesn't work correctly if we do a single
2522 * luni_send() covering the whole of buff. So
2523 * instead we luni_send the characters one by one.
2525 term_seen_key_event(term);
2526 for (i = 0; i < n; i += 2) {
2527 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2531 ImmReleaseContext(hwnd, hIMC);
2536 if (wParam & 0xFF00) {
2537 unsigned char buf[2];
2540 buf[0] = wParam >> 8;
2541 term_seen_key_event(term);
2542 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
2544 char c = (unsigned char) wParam;
2545 term_seen_key_event(term);
2546 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
2552 * Nevertheless, we are prepared to deal with WM_CHAR
2553 * messages, should they crop up. So if someone wants to
2554 * post the things to us as part of a macro manoeuvre,
2555 * we're ready to cope.
2558 char c = (unsigned char)wParam;
2559 term_seen_key_event(term);
2560 lpage_send(ldisc, CP_ACP, &c, 1, 1);
2564 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2565 SetCursor(LoadCursor(NULL, IDC_ARROW));
2569 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
2570 int shift_pressed=0, control_pressed=0;
2572 if (message == WM_MOUSEWHEEL) {
2573 wheel_accumulator += (short)HIWORD(wParam);
2574 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2575 control_pressed=LOWORD(wParam) & MK_CONTROL;
2578 wheel_accumulator += (int)wParam;
2579 if (GetKeyboardState(keys)!=0) {
2580 shift_pressed=keys[VK_SHIFT]&0x80;
2581 control_pressed=keys[VK_CONTROL]&0x80;
2585 /* process events when the threshold is reached */
2586 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2589 /* reduce amount for next time */
2590 if (wheel_accumulator > 0) {
2592 wheel_accumulator -= WHEEL_DELTA;
2593 } else if (wheel_accumulator < 0) {
2595 wheel_accumulator += WHEEL_DELTA;
2599 if (send_raw_mouse &&
2600 !(cfg.mouse_override && shift_pressed)) {
2601 /* send a mouse-down followed by a mouse up */
2604 TO_CHR_X(X_POS(lParam)),
2605 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2606 control_pressed, is_alt_pressed());
2607 term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2608 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2609 control_pressed, is_alt_pressed());
2611 /* trigger a scroll */
2612 term_scroll(term, 0,
2614 -term->rows / 2 : term->rows / 2);
2621 return DefWindowProc(hwnd, message, wParam, lParam);
2625 * Move the system caret. (We maintain one, even though it's
2626 * invisible, for the benefit of blind people: apparently some
2627 * helper software tracks the system caret, so we should arrange to
2630 void sys_cursor(void *frontend, int x, int y)
2634 if (!term->has_focus) return;
2637 * Avoid gratuitously re-updating the cursor position and IMM
2638 * window if there's no actual change required.
2640 cx = x * font_width + offset_width;
2641 cy = y * font_height + offset_height;
2642 if (cx == caret_x && cy == caret_y)
2647 sys_cursor_update();
2650 static void sys_cursor_update(void)
2655 if (!term->has_focus) return;
2657 if (caret_x < 0 || caret_y < 0)
2660 SetCaretPos(caret_x, caret_y);
2662 /* IMM calls on Win98 and beyond only */
2663 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2665 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2666 osVersion.dwMinorVersion == 0) return; /* 95 */
2668 /* we should have the IMM functions */
2669 hIMC = ImmGetContext(hwnd);
2670 cf.dwStyle = CFS_POINT;
2671 cf.ptCurrentPos.x = caret_x;
2672 cf.ptCurrentPos.y = caret_y;
2673 ImmSetCompositionWindow(hIMC, &cf);
2675 ImmReleaseContext(hwnd, hIMC);
2679 * Draw a line of text in the window, at given character
2680 * coordinates, in given attributes.
2682 * We are allowed to fiddle with the contents of `text'.
2684 void do_text(Context ctx, int x, int y, char *text, int len,
2685 unsigned long attr, int lattr)
2688 int nfg, nbg, nfont;
2691 int force_manual_underline = 0;
2692 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2693 int char_width = fnt_width;
2694 int text_adjust = 0;
2695 static int *IpDx = 0, IpDxLEN = 0;
2697 if (attr & ATTR_WIDE)
2700 if (len > IpDxLEN || IpDx[0] != char_width) {
2702 if (len > IpDxLEN) {
2704 IpDx = smalloc((len + 16) * sizeof(int));
2705 IpDxLEN = (len + 16);
2707 for (i = 0; i < IpDxLEN; i++)
2708 IpDx[i] = char_width;
2711 /* Only want the left half of double width lines */
2712 if (lattr != LATTR_NORM && x*2 >= term->cols)
2720 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
2721 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2722 attr ^= ATTR_CUR_XOR;
2726 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2727 /* Assume a poorman font is borken in other ways too. */
2737 nfont |= FONT_WIDE + FONT_HIGH;
2740 if (attr & ATTR_NARROW)
2741 nfont |= FONT_NARROW;
2743 /* Special hack for the VT100 linedraw glyphs. */
2744 if ((attr & CSET_MASK) == 0x2300) {
2745 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2746 switch ((unsigned char) (text[0])) {
2748 text_adjust = -2 * font_height / 5;
2751 text_adjust = -1 * font_height / 5;
2754 text_adjust = font_height / 5;
2757 text_adjust = 2 * font_height / 5;
2760 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2763 text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
2764 attr |= (unitab_xterm['q'] & CSET_MASK);
2765 if (attr & ATTR_UNDER) {
2766 attr &= ~ATTR_UNDER;
2767 force_manual_underline = 1;
2772 /* Anything left as an original character set is unprintable. */
2773 if (DIRECT_CHAR(attr)) {
2776 memset(text, 0xFD, len);
2780 if ((attr & CSET_MASK) == ATTR_OEMCP)
2783 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2784 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2785 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2787 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2788 nfont |= FONT_UNDERLINE;
2789 another_font(nfont);
2790 if (!fonts[nfont]) {
2791 if (nfont & FONT_UNDERLINE)
2792 force_manual_underline = 1;
2793 /* Don't do the same for manual bold, it could be bad news. */
2795 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2797 another_font(nfont);
2799 nfont = FONT_NORMAL;
2800 if (attr & ATTR_REVERSE) {
2805 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2807 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2811 SelectObject(hdc, fonts[nfont]);
2812 SetTextColor(hdc, fg);
2813 SetBkColor(hdc, bg);
2814 SetBkMode(hdc, OPAQUE);
2817 line_box.right = x + char_width * len;
2818 line_box.bottom = y + font_height;
2820 /* Only want the left half of double width lines */
2821 if (line_box.right > font_width*term->cols+offset_width)
2822 line_box.right = font_width*term->cols+offset_width;
2824 /* We're using a private area for direct to font. (512 chars.) */
2825 if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2826 /* Ho Hum, dbcs fonts are a PITA! */
2827 /* To display on W9x I have to convert to UCS */
2828 static wchar_t *uni_buf = 0;
2829 static int uni_len = 0;
2831 if (len > uni_len) {
2833 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2836 for(nlen = mptr = 0; mptr<len; mptr++) {
2837 uni_buf[nlen] = 0xFFFD;
2838 if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
2839 IpDx[nlen] += char_width;
2840 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2841 text+mptr, 2, uni_buf+nlen, 1);
2846 MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
2847 text+mptr, 1, uni_buf+nlen, 1);
2855 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2856 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2857 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2858 SetBkMode(hdc, TRANSPARENT);
2859 ExtTextOutW(hdc, x - 1,
2860 y - font_height * (lattr ==
2861 LATTR_BOT) + text_adjust,
2862 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2866 } else if (DIRECT_FONT(attr)) {
2868 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2869 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2870 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2871 SetBkMode(hdc, TRANSPARENT);
2873 /* GRR: This draws the character outside it's box and can leave
2874 * 'droppings' even with the clip box! I suppose I could loop it
2875 * one character at a time ... yuk.
2877 * Or ... I could do a test print with "W", and use +1 or -1 for this
2878 * shift depending on if the leftmost column is blank...
2880 ExtTextOut(hdc, x - 1,
2881 y - font_height * (lattr ==
2882 LATTR_BOT) + text_adjust,
2883 ETO_CLIPPED, &line_box, text, len, IpDx);
2886 /* And 'normal' unicode characters */
2887 static WCHAR *wbuf = NULL;
2888 static int wlen = 0;
2893 wbuf = smalloc(wlen * sizeof(WCHAR));
2895 for (i = 0; i < len; i++)
2896 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2899 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2900 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2902 /* And the shadow bold hack. */
2903 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2904 SetBkMode(hdc, TRANSPARENT);
2905 ExtTextOutW(hdc, x - 1,
2906 y - font_height * (lattr ==
2907 LATTR_BOT) + text_adjust,
2908 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2911 if (lattr != LATTR_TOP && (force_manual_underline ||
2912 (und_mode == UND_LINE
2913 && (attr & ATTR_UNDER)))) {
2916 if (lattr == LATTR_BOT)
2917 dec = dec * 2 - font_height;
2919 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2920 MoveToEx(hdc, x, y + dec, NULL);
2921 LineTo(hdc, x + len * char_width, y + dec);
2922 oldpen = SelectObject(hdc, oldpen);
2923 DeleteObject(oldpen);
2927 void do_cursor(Context ctx, int x, int y, char *text, int len,
2928 unsigned long attr, int lattr)
2934 int ctype = cfg.cursor_type;
2936 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
2937 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2938 do_text(ctx, x, y, text, len, attr, lattr);
2942 attr |= TATTR_RIGHTCURS;
2945 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2946 if (attr & ATTR_WIDE)
2953 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
2956 pts[0].x = pts[1].x = pts[4].x = x;
2957 pts[2].x = pts[3].x = x + char_width - 1;
2958 pts[0].y = pts[3].y = pts[4].y = y;
2959 pts[1].y = pts[2].y = y + font_height - 1;
2960 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2961 Polyline(hdc, pts, 5);
2962 oldpen = SelectObject(hdc, oldpen);
2963 DeleteObject(oldpen);
2964 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2965 int startx, starty, dx, dy, length, i;
2968 starty = y + descent;
2971 length = char_width;
2974 if (attr & TATTR_RIGHTCURS)
2975 xadjust = char_width - 1;
2976 startx = x + xadjust;
2980 length = font_height;
2982 if (attr & TATTR_ACTCURS) {
2985 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2986 MoveToEx(hdc, startx, starty, NULL);
2987 LineTo(hdc, startx + dx * length, starty + dy * length);
2988 oldpen = SelectObject(hdc, oldpen);
2989 DeleteObject(oldpen);
2991 for (i = 0; i < length; i++) {
2993 SetPixel(hdc, startx, starty, colours[23]);
3002 /* This function gets the actual width of a character in the normal font.
3004 int char_width(Context ctx, int uc) {
3008 /* If the font max is the same as the font ave width then this
3009 * function is a no-op.
3011 if (!font_dualwidth) return 1;
3013 switch (uc & CSET_MASK) {
3015 uc = unitab_line[uc & 0xFF];
3018 uc = unitab_xterm[uc & 0xFF];
3021 uc = unitab_scoacs[uc & 0xFF];
3024 if (DIRECT_FONT(uc)) {
3025 if (dbcs_screenfont) return 1;
3027 /* Speedup, I know of no font where ascii is the wrong width */
3028 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3031 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3032 SelectObject(hdc, fonts[FONT_NORMAL]);
3033 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3034 another_font(FONT_OEM);
3035 if (!fonts[FONT_OEM]) return 0;
3037 SelectObject(hdc, fonts[FONT_OEM]);
3041 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3042 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3045 /* Speedup, I know of no font where ascii is the wrong width */
3046 if (uc >= ' ' && uc <= '~') return 1;
3048 SelectObject(hdc, fonts[FONT_NORMAL]);
3049 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3050 /* Okay that one worked */ ;
3051 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3052 /* This should work on 9x too, but it's "less accurate" */ ;
3057 ibuf += font_width / 2 -1;
3064 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3065 * codes. Returns number of bytes used or zero to drop the message
3066 * or -1 to forward the message to windows.
3068 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3069 unsigned char *output)
3072 int scan, left_alt = 0, key_down, shift_state;
3074 unsigned char *p = output;
3075 static int alt_sum = 0;
3077 HKL kbd_layout = GetKeyboardLayout(0);
3079 static WORD keys[3];
3080 static int compose_char = 0;
3081 static WPARAM compose_key = 0;
3083 r = GetKeyboardState(keystate);
3085 memset(keystate, 0, sizeof(keystate));
3088 #define SHOW_TOASCII_RESULT
3089 { /* Tell us all about key events */
3090 static BYTE oldstate[256];
3091 static int first = 1;
3095 memcpy(oldstate, keystate, sizeof(oldstate));
3098 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3100 } else if ((HIWORD(lParam) & KF_UP)
3101 && scan == (HIWORD(lParam) & 0xFF)) {
3105 if (wParam >= VK_F1 && wParam <= VK_F20)
3106 debug(("K_F%d", wParam + 1 - VK_F1));
3119 debug(("VK_%02x", wParam));
3121 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3123 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3125 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3126 if (ch >= ' ' && ch <= '~')
3127 debug((", '%c'", ch));
3129 debug((", $%02x", ch));
3132 debug((", KB0=%02x", keys[0]));
3134 debug((", KB1=%02x", keys[1]));
3136 debug((", KB2=%02x", keys[2]));
3138 if ((keystate[VK_SHIFT] & 0x80) != 0)
3140 if ((keystate[VK_CONTROL] & 0x80) != 0)
3142 if ((HIWORD(lParam) & KF_EXTENDED))
3144 if ((HIWORD(lParam) & KF_UP))
3148 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3149 else if ((HIWORD(lParam) & KF_UP))
3150 oldstate[wParam & 0xFF] ^= 0x80;
3152 oldstate[wParam & 0xFF] ^= 0x81;
3154 for (ch = 0; ch < 256; ch++)
3155 if (oldstate[ch] != keystate[ch])
3156 debug((", M%02x=%02x", ch, keystate[ch]));
3158 memcpy(oldstate, keystate, sizeof(oldstate));
3162 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3163 keystate[VK_RMENU] = keystate[VK_MENU];
3167 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3168 if ((cfg.funky_type == 3 ||
3169 (cfg.funky_type <= 1 && term->app_keypad_keys &&
3171 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3173 wParam = VK_EXECUTE;
3175 /* UnToggle NUMLock */
3176 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3177 keystate[VK_NUMLOCK] ^= 1;
3180 /* And write back the 'adjusted' state */
3181 SetKeyboardState(keystate);
3184 /* Disable Auto repeat if required */
3185 if (term->repeat_off &&
3186 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3189 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3192 key_down = ((HIWORD(lParam) & KF_UP) == 0);
3194 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3195 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3196 if (cfg.ctrlaltkeys)
3197 keystate[VK_MENU] = 0;
3199 keystate[VK_RMENU] = 0x80;
3204 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3205 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3206 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3208 /* Note if AltGr was pressed and if it was used as a compose key */
3209 if (!compose_state) {
3210 compose_key = 0x100;
3211 if (cfg.compose_key) {
3212 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3213 compose_key = wParam;
3215 if (wParam == VK_APPS)
3216 compose_key = wParam;
3219 if (wParam == compose_key) {
3220 if (compose_state == 0
3221 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3223 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3227 } else if (compose_state == 1 && wParam != VK_CONTROL)
3230 if (compose_state > 1 && left_alt)
3233 /* Sanitize the number pad if not using a PC NumPad */
3234 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
3235 && cfg.funky_type != 2)
3236 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3237 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3241 nParam = VK_NUMPAD0;
3244 nParam = VK_NUMPAD1;
3247 nParam = VK_NUMPAD2;
3250 nParam = VK_NUMPAD3;
3253 nParam = VK_NUMPAD4;
3256 nParam = VK_NUMPAD5;
3259 nParam = VK_NUMPAD6;
3262 nParam = VK_NUMPAD7;
3265 nParam = VK_NUMPAD8;
3268 nParam = VK_NUMPAD9;
3271 nParam = VK_DECIMAL;
3275 if (keystate[VK_NUMLOCK] & 1)
3282 /* If a key is pressed and AltGr is not active */
3283 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3284 /* Okay, prepare for most alts then ... */
3288 /* Lets see if it's a pattern we know all about ... */
3289 if (wParam == VK_PRIOR && shift_state == 1) {
3290 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3293 if (wParam == VK_PRIOR && shift_state == 2) {
3294 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3297 if (wParam == VK_NEXT && shift_state == 1) {
3298 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3301 if (wParam == VK_NEXT && shift_state == 2) {
3302 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3305 if (wParam == VK_INSERT && shift_state == 1) {
3306 term_do_paste(term);
3309 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3312 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3313 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3316 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3317 (cfg.resize_action != RESIZE_DISABLED)) {
3318 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3322 /* Control-Numlock for app-keypad mode switch */
3323 if (wParam == VK_PAUSE && shift_state == 2) {
3324 term->app_keypad_keys ^= 1;
3328 /* Nethack keypad */
3329 if (cfg.nethack_keypad && !left_alt) {
3332 *p++ = shift_state ? 'B' : 'b';
3335 *p++ = shift_state ? 'J' : 'j';
3338 *p++ = shift_state ? 'N' : 'n';
3341 *p++ = shift_state ? 'H' : 'h';
3344 *p++ = shift_state ? '.' : '.';
3347 *p++ = shift_state ? 'L' : 'l';
3350 *p++ = shift_state ? 'Y' : 'y';
3353 *p++ = shift_state ? 'K' : 'k';
3356 *p++ = shift_state ? 'U' : 'u';
3361 /* Application Keypad */
3365 if (cfg.funky_type == 3 ||
3366 (cfg.funky_type <= 1 &&
3367 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3381 if (term->app_keypad_keys && !cfg.no_applic_k)
3418 if (cfg.funky_type == 2) {
3423 } else if (shift_state)
3430 if (cfg.funky_type == 2)
3434 if (cfg.funky_type == 2)
3438 if (cfg.funky_type == 2)
3443 if (HIWORD(lParam) & KF_EXTENDED)
3448 if (term->vt52_mode) {
3449 if (xkey >= 'P' && xkey <= 'S')
3450 p += sprintf((char *) p, "\x1B%c", xkey);
3452 p += sprintf((char *) p, "\x1B?%c", xkey);
3454 p += sprintf((char *) p, "\x1BO%c", xkey);
3459 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3460 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3464 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3465 /* We do the opposite of what is configured */
3466 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3470 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3476 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3480 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3484 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3489 if (wParam == VK_PAUSE) { /* Break/Pause */
3494 /* Control-2 to Control-8 are special */
3495 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3496 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3499 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
3503 if (shift_state == 2 && wParam == 0xDF) {
3507 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
3514 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3515 * for integer decimal nn.)
3517 * We also deal with the weird ones here. Linux VCs replace F1
3518 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3519 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3525 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3528 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3531 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3534 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3537 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3540 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3543 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3546 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3549 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3552 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3585 if ((shift_state&2) == 0) switch (wParam) {
3605 /* Reorder edit keys to physical order */
3606 if (cfg.funky_type == 3 && code <= 6)
3607 code = "\0\2\1\4\5\3\6"[code];
3609 if (term->vt52_mode && code > 0 && code <= 6) {
3610 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3614 if (cfg.funky_type == 5 && /* SCO function keys */
3615 code >= 11 && code <= 34) {
3616 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3619 case VK_F1: index = 0; break;
3620 case VK_F2: index = 1; break;
3621 case VK_F3: index = 2; break;
3622 case VK_F4: index = 3; break;
3623 case VK_F5: index = 4; break;
3624 case VK_F6: index = 5; break;
3625 case VK_F7: index = 6; break;
3626 case VK_F8: index = 7; break;
3627 case VK_F9: index = 8; break;
3628 case VK_F10: index = 9; break;
3629 case VK_F11: index = 10; break;
3630 case VK_F12: index = 11; break;
3632 if (keystate[VK_SHIFT] & 0x80) index += 12;
3633 if (keystate[VK_CONTROL] & 0x80) index += 24;
3634 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3637 if (cfg.funky_type == 5 && /* SCO small keypad */
3638 code >= 1 && code <= 6) {
3639 char codes[] = "HL.FIG";
3643 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3647 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3653 if (term->vt52_mode)
3654 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3657 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3660 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3661 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3664 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3665 if (term->vt52_mode)
3666 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3668 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3671 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3672 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3676 p += sprintf((char *) p, "\x1B[%d~", code);
3681 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3682 * some reason seems to send VK_CLEAR to Windows...).
3704 if (term->vt52_mode)
3705 p += sprintf((char *) p, "\x1B%c", xkey);
3707 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3710 * RDB: VT100 & VT102 manuals both state the
3711 * app cursor keys only work if the app keypad
3714 * SGT: That may well be true, but xterm
3715 * disagrees and so does at least one
3716 * application, so I've #if'ed this out and the
3717 * behaviour is back to PuTTY's original: app
3718 * cursor and app keypad are independently
3719 * switchable modes. If anyone complains about
3720 * _this_ I'll have to put in a configurable
3723 if (!term->app_keypad_keys)
3726 /* Useful mapping of Ctrl-arrows */
3727 if (shift_state == 2)
3731 p += sprintf((char *) p, "\x1BO%c", xkey);
3733 p += sprintf((char *) p, "\x1B[%c", xkey);
3740 * Finally, deal with Return ourselves. (Win95 seems to
3741 * foul it up when Alt is pressed, for some reason.)
3743 if (wParam == VK_RETURN) { /* Return */
3749 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3750 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3755 /* Okay we've done everything interesting; let windows deal with
3756 * the boring stuff */
3760 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3761 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3763 keystate[VK_CAPITAL] = 0;
3766 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3767 #ifdef SHOW_TOASCII_RESULT
3768 if (r == 1 && !key_down) {
3770 if (in_utf(term) || dbcs_screenfont)
3771 debug((", (U+%04x)", alt_sum));
3773 debug((", LCH(%d)", alt_sum));
3775 debug((", ACH(%d)", keys[0]));
3780 for (r1 = 0; r1 < r; r1++) {
3781 debug(("%s%d", r1 ? "," : "", keys[r1]));
3790 * Interrupt an ongoing paste. I'm not sure this is
3791 * sensible, but for the moment it's preferable to
3792 * having to faff about buffering things.
3797 for (i = 0; i < r; i++) {
3798 unsigned char ch = (unsigned char) keys[i];
3800 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3805 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3809 if ((nc = check_compose(compose_char, ch)) == -1) {
3810 MessageBeep(MB_ICONHAND);
3814 term_seen_key_event(term);
3815 luni_send(ldisc, &keybuf, 1, 1);
3823 if (in_utf(term) || dbcs_screenfont) {
3825 term_seen_key_event(term);
3826 luni_send(ldisc, &keybuf, 1, 1);
3828 ch = (char) alt_sum;
3830 * We need not bother about stdin
3831 * backlogs here, because in GUI PuTTY
3832 * we can't do anything about it
3833 * anyway; there's no means of asking
3834 * Windows to hold off on KEYDOWN
3835 * messages. We _have_ to buffer
3836 * everything we're sent.
3838 term_seen_key_event(term);
3839 ldisc_send(ldisc, &ch, 1, 1);
3843 term_seen_key_event(term);
3844 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
3846 if(capsOn && ch < 0x80) {
3849 cbuf[1] = xlat_uskbd2cyrllic(ch);
3850 term_seen_key_event(term);
3851 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
3856 term_seen_key_event(term);
3857 lpage_send(ldisc, kbd_codepage,
3858 cbuf+!left_alt, 1+!!left_alt, 1);
3864 /* This is so the ALT-Numpad and dead keys work correctly. */
3869 /* If we're definitly not building up an ALT-54321 then clear it */
3872 /* If we will be using alt_sum fix the 256s */
3873 else if (keys[0] && (in_utf(term) || dbcs_screenfont))
3878 * ALT alone may or may not want to bring up the System menu.
3879 * If it's not meant to, we return 0 on presses or releases of
3880 * ALT, to show that we've swallowed the keystroke. Otherwise
3881 * we return -1, which means Windows will give the keystroke
3882 * its default handling (i.e. bring up the System menu).
3884 if (wParam == VK_MENU && !cfg.alt_only)
3890 void request_paste(void *frontend)
3893 * In Windows, pasting is synchronous: we can read the
3894 * clipboard with no difficulty, so request_paste() can just go
3897 term_do_paste(term);
3900 void set_title(void *frontend, char *title)
3903 window_name = smalloc(1 + strlen(title));
3904 strcpy(window_name, title);
3905 if (cfg.win_name_always || !IsIconic(hwnd))
3906 SetWindowText(hwnd, title);
3909 void set_icon(void *frontend, char *title)
3912 icon_name = smalloc(1 + strlen(title));
3913 strcpy(icon_name, title);
3914 if (!cfg.win_name_always && IsIconic(hwnd))
3915 SetWindowText(hwnd, title);
3918 void set_sbar(void *frontend, int total, int start, int page)
3922 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3925 si.cbSize = sizeof(si);
3926 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3928 si.nMax = total - 1;
3932 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3935 Context get_ctx(void *frontend)
3941 SelectPalette(hdc, pal, FALSE);
3947 void free_ctx(Context ctx)
3949 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3950 ReleaseDC(hwnd, ctx);
3953 static void real_palette_set(int n, int r, int g, int b)
3956 logpal->palPalEntry[n].peRed = r;
3957 logpal->palPalEntry[n].peGreen = g;
3958 logpal->palPalEntry[n].peBlue = b;
3959 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3960 colours[n] = PALETTERGB(r, g, b);
3961 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3963 colours[n] = RGB(r, g, b);
3966 void palette_set(void *frontend, int n, int r, int g, int b)
3968 static const int first[21] = {
3969 0, 2, 4, 6, 8, 10, 12, 14,
3970 1, 3, 5, 7, 9, 11, 13, 15,
3973 real_palette_set(first[n], r, g, b);
3975 real_palette_set(first[n] + 1, r, g, b);
3977 HDC hdc = get_ctx(frontend);
3978 UnrealizeObject(pal);
3979 RealizePalette(hdc);
3984 void palette_reset(void *frontend)
3988 for (i = 0; i < NCOLOURS; i++) {
3990 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
3991 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
3992 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
3993 logpal->palPalEntry[i].peFlags = 0;
3994 colours[i] = PALETTERGB(defpal[i].rgbtRed,
3995 defpal[i].rgbtGreen,
3996 defpal[i].rgbtBlue);
3998 colours[i] = RGB(defpal[i].rgbtRed,
3999 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
4004 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
4005 hdc = get_ctx(frontend);
4006 RealizePalette(hdc);
4011 void write_aclip(void *frontend, char *data, int len, int must_deselect)
4016 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
4019 lock = GlobalLock(clipdata);
4022 memcpy(lock, data, len);
4023 ((unsigned char *) lock)[len] = 0;
4024 GlobalUnlock(clipdata);
4027 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4029 if (OpenClipboard(hwnd)) {
4031 SetClipboardData(CF_TEXT, clipdata);
4034 GlobalFree(clipdata);
4037 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4041 * Note: unlike write_aclip() this will not append a nul.
4043 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4045 HGLOBAL clipdata, clipdata2, clipdata3;
4047 void *lock, *lock2, *lock3;
4049 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4051 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4052 len * sizeof(wchar_t));
4053 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4055 if (!clipdata || !clipdata2) {
4057 GlobalFree(clipdata);
4059 GlobalFree(clipdata2);
4062 if (!(lock = GlobalLock(clipdata)))
4064 if (!(lock2 = GlobalLock(clipdata2)))
4067 memcpy(lock, data, len * sizeof(wchar_t));
4068 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4070 if (cfg.rtf_paste) {
4071 wchar_t unitab[256];
4073 unsigned char *tdata = (unsigned char *)lock2;
4074 wchar_t *udata = (wchar_t *)lock;
4075 int rtflen = 0, uindex = 0, tindex = 0;
4077 int multilen, blen, alen, totallen, i;
4078 char before[16], after[4];
4080 get_unitab(CP_ACP, unitab, 0);
4082 rtfsize = 100 + strlen(cfg.font);
4083 rtf = smalloc(rtfsize);
4084 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4085 GetACP(), cfg.font);
4086 rtflen = strlen(rtf);
4089 * We want to construct a piece of RTF that specifies the
4090 * same Unicode text. To do this we will read back in
4091 * parallel from the Unicode data in `udata' and the
4092 * non-Unicode data in `tdata'. For each character in
4093 * `tdata' which becomes the right thing in `udata' when
4094 * looked up in `unitab', we just copy straight over from
4095 * tdata. For each one that doesn't, we must WCToMB it
4096 * individually and produce a \u escape sequence.
4098 * It would probably be more robust to just bite the bullet
4099 * and WCToMB each individual Unicode character one by one,
4100 * then MBToWC each one back to see if it was an accurate
4101 * translation; but that strikes me as a horrifying number
4102 * of Windows API calls so I want to see if this faster way
4103 * will work. If it screws up badly we can always revert to
4104 * the simple and slow way.
4106 while (tindex < len2 && uindex < len &&
4107 tdata[tindex] && udata[uindex]) {
4108 if (tindex + 1 < len2 &&
4109 tdata[tindex] == '\r' &&
4110 tdata[tindex+1] == '\n') {
4114 if (unitab[tdata[tindex]] == udata[uindex]) {
4120 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4121 NULL, 0, NULL, NULL);
4122 if (multilen != 1) {
4123 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4125 alen = 1; strcpy(after, "}");
4127 blen = sprintf(before, "\\u%d", udata[uindex]);
4128 alen = 0; after[0] = '\0';
4131 assert(tindex + multilen <= len2);
4132 totallen = blen + alen;
4133 for (i = 0; i < multilen; i++) {
4134 if (tdata[tindex+i] == '\\' ||
4135 tdata[tindex+i] == '{' ||
4136 tdata[tindex+i] == '}')
4138 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4139 totallen += 6; /* \par\r\n */
4140 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4146 if (rtfsize < rtflen + totallen + 3) {
4147 rtfsize = rtflen + totallen + 512;
4148 rtf = srealloc(rtf, rtfsize);
4151 strcpy(rtf + rtflen, before); rtflen += blen;
4152 for (i = 0; i < multilen; i++) {
4153 if (tdata[tindex+i] == '\\' ||
4154 tdata[tindex+i] == '{' ||
4155 tdata[tindex+i] == '}') {
4156 rtf[rtflen++] = '\\';
4157 rtf[rtflen++] = tdata[tindex+i];
4158 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4159 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4160 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4161 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4163 rtf[rtflen++] = tdata[tindex+i];
4166 strcpy(rtf + rtflen, after); rtflen += alen;
4172 strcpy(rtf + rtflen, "}");
4175 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4176 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4178 GlobalUnlock(clipdata3);
4184 GlobalUnlock(clipdata);
4185 GlobalUnlock(clipdata2);
4188 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4190 if (OpenClipboard(hwnd)) {
4192 SetClipboardData(CF_UNICODETEXT, clipdata);
4193 SetClipboardData(CF_TEXT, clipdata2);
4195 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4198 GlobalFree(clipdata);
4199 GlobalFree(clipdata2);
4203 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4206 void get_clip(void *frontend, wchar_t ** p, int *len)
4208 static HGLOBAL clipdata = NULL;
4209 static wchar_t *converted = 0;
4218 GlobalUnlock(clipdata);
4221 } else if (OpenClipboard(NULL)) {
4222 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4224 *p = GlobalLock(clipdata);
4226 for (p2 = *p; *p2; p2++);
4230 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4234 s = GlobalLock(clipdata);
4235 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4236 *p = converted = smalloc(i * sizeof(wchar_t));
4237 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4250 * Move `lines' lines from position `from' to position `to' in the
4253 void optimised_move(void *frontend, int to, int from, int lines)
4258 min = (to < from ? to : from);
4259 max = to + from - min;
4261 r.left = offset_width;
4262 r.right = offset_width + term->cols * font_width;
4263 r.top = offset_height + min * font_height;
4264 r.bottom = offset_height + (max + lines) * font_height;
4265 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4270 * Print a message box and perform a fatal exit.
4272 void fatalbox(char *fmt, ...)
4278 vsprintf(stuff, fmt, ap);
4280 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4285 * Print a modal (Really Bad) message box and perform a fatal exit.
4287 void modalfatalbox(char *fmt, ...)
4293 vsprintf(stuff, fmt, ap);
4295 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4296 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4301 * Manage window caption / taskbar flashing, if enabled.
4302 * 0 = stop, 1 = maintain, 2 = start
4304 static void flash_window(int mode)
4306 static long last_flash = 0;
4307 static int flashing = 0;
4308 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4311 FlashWindow(hwnd, FALSE);
4315 } else if (mode == 2) {
4318 last_flash = GetTickCount();
4320 FlashWindow(hwnd, TRUE);
4323 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4326 long now = GetTickCount();
4327 long fdiff = now - last_flash;
4328 if (fdiff < 0 || fdiff > 450) {
4330 FlashWindow(hwnd, TRUE); /* toggle */
4339 void beep(void *frontend, int mode)
4341 if (mode == BELL_DEFAULT) {
4343 * For MessageBeep style bells, we want to be careful of
4344 * timing, because they don't have the nice property of
4345 * PlaySound bells that each one cancels the previous
4346 * active one. So we limit the rate to one per 50ms or so.
4348 static long lastbeep = 0;
4351 beepdiff = GetTickCount() - lastbeep;
4352 if (beepdiff >= 0 && beepdiff < 50)
4356 * The above MessageBeep call takes time, so we record the
4357 * time _after_ it finishes rather than before it starts.
4359 lastbeep = GetTickCount();
4360 } else if (mode == BELL_WAVEFILE) {
4361 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4362 char buf[sizeof(cfg.bell_wavefile) + 80];
4363 sprintf(buf, "Unable to play sound file\n%s\n"
4364 "Using default sound instead", cfg.bell_wavefile);
4365 MessageBox(hwnd, buf, "PuTTY Sound Error",
4366 MB_OK | MB_ICONEXCLAMATION);
4367 cfg.beep = BELL_DEFAULT;
4370 /* Otherwise, either visual bell or disabled; do nothing here */
4371 if (!term->has_focus) {
4372 flash_window(2); /* start */
4377 * Minimise or restore the window in response to a server-side
4380 void set_iconic(void *frontend, int iconic)
4382 if (IsIconic(hwnd)) {
4384 ShowWindow(hwnd, SW_RESTORE);
4387 ShowWindow(hwnd, SW_MINIMIZE);
4392 * Move the window in response to a server-side request.
4394 void move_window(void *frontend, int x, int y)
4396 if (cfg.resize_action == RESIZE_DISABLED ||
4397 cfg.resize_action == RESIZE_FONT ||
4401 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4405 * Move the window to the top or bottom of the z-order in response
4406 * to a server-side request.
4408 void set_zorder(void *frontend, int top)
4410 if (cfg.alwaysontop)
4411 return; /* ignore */
4412 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4413 SWP_NOMOVE | SWP_NOSIZE);
4417 * Refresh the window in response to a server-side request.
4419 void refresh_window(void *frontend)
4421 InvalidateRect(hwnd, NULL, TRUE);
4425 * Maximise or restore the window in response to a server-side
4428 void set_zoomed(void *frontend, int zoomed)
4430 if (IsZoomed(hwnd)) {
4432 ShowWindow(hwnd, SW_RESTORE);
4435 ShowWindow(hwnd, SW_MAXIMIZE);
4440 * Report whether the window is iconic, for terminal reports.
4442 int is_iconic(void *frontend)
4444 return IsIconic(hwnd);
4448 * Report the window's position, for terminal reports.
4450 void get_window_pos(void *frontend, int *x, int *y)
4453 GetWindowRect(hwnd, &r);
4459 * Report the window's pixel size, for terminal reports.
4461 void get_window_pixels(void *frontend, int *x, int *y)
4464 GetWindowRect(hwnd, &r);
4465 *x = r.right - r.left;
4466 *y = r.bottom - r.top;
4470 * Return the window or icon title.
4472 char *get_window_title(void *frontend, int icon)
4474 return icon ? icon_name : window_name;
4478 * See if we're in full-screen mode.
4480 int is_full_screen()
4482 if (!IsZoomed(hwnd))
4484 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4489 /* Get the rect/size of a full screen window using the nearest available
4490 * monitor in multimon systems; default to something sensible if only
4491 * one monitor is present. */
4492 static int get_fullscreen_rect(RECT * ss)
4494 #ifdef MONITOR_DEFAULTTONEAREST
4497 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4498 mi.cbSize = sizeof(mi);
4499 GetMonitorInfo(mon, &mi);
4501 /* structure copy */
4505 /* could also use code like this:
4506 ss->left = ss->top = 0;
4507 ss->right = GetSystemMetrics(SM_CXSCREEN);
4508 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4510 return GetClientRect(GetDesktopWindow(), ss);
4516 * Go full-screen. This should only be called when we are already
4519 void make_full_screen()
4524 assert(IsZoomed(hwnd));
4526 if (is_full_screen())
4529 /* Remove the window furniture. */
4530 style = GetWindowLong(hwnd, GWL_STYLE);
4531 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4532 if (cfg.scrollbar_in_fullscreen)
4533 style |= WS_VSCROLL;
4535 style &= ~WS_VSCROLL;
4536 SetWindowLong(hwnd, GWL_STYLE, style);
4538 /* Resize ourselves to exactly cover the nearest monitor. */
4539 get_fullscreen_rect(&ss);
4540 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4545 /* Tick the menu item in the System menu. */
4546 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4551 * Clear the full-screen attributes.
4553 void clear_full_screen()
4555 DWORD oldstyle, style;
4557 /* Reinstate the window furniture. */
4558 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4559 style |= WS_CAPTION | WS_BORDER;
4560 if (cfg.resize_action == RESIZE_DISABLED)
4561 style &= ~WS_THICKFRAME;
4563 style |= WS_THICKFRAME;
4565 style |= WS_VSCROLL;
4567 style &= ~WS_VSCROLL;
4568 if (style != oldstyle) {
4569 SetWindowLong(hwnd, GWL_STYLE, style);
4570 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4571 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4575 /* Untick the menu item in the System menu. */
4576 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4581 * Toggle full-screen mode.
4583 void flip_full_screen()
4585 if (is_full_screen()) {
4586 ShowWindow(hwnd, SW_RESTORE);
4587 } else if (IsZoomed(hwnd)) {
4590 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4591 ShowWindow(hwnd, SW_MAXIMIZE);
4595 void frontend_keypress(void *handle)
4598 * Keypress termination in non-Close-On-Exit mode is not
4599 * currently supported in PuTTY proper, because the window
4600 * always has a perfectly good Close button anyway. So we do