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 Mouse_Button translate_button(Mouse_Button button);
82 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
83 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
84 unsigned char *output);
85 static void cfgtopalette(void);
86 static void init_palette(void);
87 static void init_fonts(int, int);
88 static void another_font(int);
89 static void deinit_fonts(void);
90 static void set_input_locale(HKL);
92 static int is_full_screen(void);
93 static void make_full_screen(void);
94 static void clear_full_screen(void);
95 static void flip_full_screen(void);
97 /* Window layout information */
98 static void reset_window(int);
99 static int extra_width, extra_height;
100 static int font_width, font_height, font_dualwidth;
101 static int offset_width, offset_height;
102 static int was_zoomed = 0;
103 static int prev_rows, prev_cols;
105 static int pending_netevent = 0;
106 static WPARAM pend_netevent_wParam = 0;
107 static LPARAM pend_netevent_lParam = 0;
108 static void enact_pending_netevent(void);
109 static void flash_window(int mode);
110 static void sys_cursor_update(void);
111 static int is_shift_pressed(void);
112 static int get_fullscreen_rect(RECT * ss);
114 static time_t last_movement = 0;
116 static int caret_x = -1, caret_y = -1;
118 static int kbd_codepage;
121 static Backend *back;
122 static void *backhandle;
124 static struct unicode_data ucsdata;
125 static int session_closed;
127 Config cfg; /* exported to windlg.c */
129 extern struct sesslist sesslist; /* imported from windlg.c */
131 #define FONT_NORMAL 0
133 #define FONT_UNDERLINE 2
134 #define FONT_BOLDUND 3
135 #define FONT_WIDE 0x04
136 #define FONT_HIGH 0x08
137 #define FONT_NARROW 0x10
139 #define FONT_OEM 0x20
140 #define FONT_OEMBOLD 0x21
141 #define FONT_OEMUND 0x22
142 #define FONT_OEMBOLDUND 0x23
144 #define FONT_MAXNO 0x2F
146 static HFONT fonts[FONT_MAXNO];
147 static LOGFONT lfont;
148 static int fontflag[FONT_MAXNO];
150 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
158 static COLORREF colours[NCOLOURS];
160 static LPLOGPALETTE logpal;
161 static RGBTRIPLE defpal[NCOLOURS];
165 static HBITMAP caretbm;
167 static int dbltime, lasttime, lastact;
168 static Mouse_Button lastbtn;
170 /* this allows xterm-style mouse handling. */
171 static int send_raw_mouse = 0;
172 static int wheel_accumulator = 0;
174 static char *window_name, *icon_name;
176 static int compose_state = 0;
178 static int wsa_started = 0;
180 static OSVERSIONINFO osVersion;
182 static UINT wm_mousewheel = WM_MOUSEWHEEL;
184 /* Dummy routine, only required in plink. */
185 void ldisc_update(void *frontend, int echo, int edit)
189 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
191 static char appname[] = "PuTTY";
196 int guess_width, guess_height;
199 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
201 winsock_ver = MAKEWORD(1, 1);
202 if (WSAStartup(winsock_ver, &wsadata)) {
203 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
204 MB_OK | MB_ICONEXCLAMATION);
207 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
208 MessageBox(NULL, "WinSock version is incompatible with 1.1",
209 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
214 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
217 InitCommonControls();
219 /* Ensure a Maximize setting in Explorer doesn't maximise the
224 ZeroMemory(&osVersion, sizeof(osVersion));
225 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
226 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
227 MessageBox(NULL, "Windows refuses to report a version",
228 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
234 * If we're running a version of Windows that doesn't support
235 * WM_MOUSEWHEEL, find out what message number we should be
238 if (osVersion.dwMajorVersion < 4 ||
239 (osVersion.dwMajorVersion == 4 &&
240 osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
241 wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
244 * See if we can find our Help file.
247 char b[2048], *p, *q, *r;
249 GetModuleFileName(NULL, b, sizeof(b) - 1);
251 p = strrchr(b, '\\');
252 if (p && p >= r) r = p+1;
254 if (q && q >= r) r = q+1;
255 strcpy(r, "putty.hlp");
256 if ( (fp = fopen(b, "r")) != NULL) {
257 help_path = dupstr(b);
261 strcpy(r, "putty.cnt");
262 if ( (fp = fopen(b, "r")) != NULL) {
263 help_has_contents = TRUE;
266 help_has_contents = FALSE;
270 * Process the command line.
276 default_protocol = be_default_protocol;
277 /* Find the appropriate default port. */
280 default_port = 0; /* illegal */
281 for (i = 0; backends[i].backend != NULL; i++)
282 if (backends[i].protocol == default_protocol) {
283 default_port = backends[i].backend->default_port;
287 cfg.logtype = LGTYP_NONE;
289 do_defaults(NULL, &cfg);
294 * Process a couple of command-line options which are more
295 * easily dealt with before the line is broken up into
296 * words. These are the soon-to-be-defunct @sessionname and
297 * the internal-use-only &sharedmemoryhandle, neither of
298 * which are combined with anything else.
300 while (*p && isspace(*p))
304 while (i > 1 && isspace(p[i - 1]))
307 do_defaults(p + 1, &cfg);
308 if (!*cfg.host && !do_config()) {
312 } else if (*p == '&') {
314 * An initial & means we've been given a command line
315 * containing the hex value of a HANDLE for a file
316 * mapping object, which we must then extract as a
321 if (sscanf(p + 1, "%p", &filemap) == 1 &&
322 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
323 0, 0, sizeof(Config))) != NULL) {
326 CloseHandle(filemap);
327 } else if (!do_config()) {
333 * Otherwise, break up the command line and deal with
339 split_into_argv(cmdline, &argc, &argv, NULL);
341 for (i = 0; i < argc; i++) {
345 ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
348 cmdline_error("option \"%s\" requires an argument", p);
349 } else if (ret == 2) {
350 i++; /* skip next argument */
351 } else if (ret == 1) {
352 continue; /* nothing further needs doing */
353 } else if (!strcmp(p, "-cleanup")) {
355 * `putty -cleanup'. Remove all registry
356 * entries associated with PuTTY, and also find
357 * and delete the random seed file.
360 "This procedure will remove ALL Registry\n"
361 "entries associated with PuTTY, and will\n"
362 "also remove the PuTTY random seed file.\n"
364 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
365 "SESSIONS. Are you really sure you want\n"
368 MB_YESNO | MB_ICONWARNING) == IDYES) {
372 } else if (*p != '-') {
376 * If we already have a host name, treat
377 * this argument as a port number. NB we
378 * have to treat this as a saved -P
379 * argument, so that it will be deferred
380 * until it's a good moment to run it.
382 int ret = cmdline_process_param("-P", p, 1, &cfg);
384 } else if (!strncmp(q, "telnet:", 7)) {
386 * If the hostname starts with "telnet:",
387 * set the protocol to Telnet and process
388 * the string as a Telnet URL.
393 if (q[0] == '/' && q[1] == '/')
395 cfg.protocol = PROT_TELNET;
397 while (*p && *p != ':' && *p != '/')
406 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
407 cfg.host[sizeof(cfg.host) - 1] = '\0';
411 * Otherwise, treat this argument as a host
414 while (*p && !isspace(*p))
418 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
419 cfg.host[sizeof(cfg.host) - 1] = '\0';
423 cmdline_error("unknown option \"%s\"", p);
428 cmdline_run_saved(&cfg);
430 if (!*cfg.host && !do_config()) {
436 * Trim leading whitespace off the hostname if it's there.
439 int space = strspn(cfg.host, " \t");
440 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
443 /* See if host is of the form user@host */
444 if (cfg.host[0] != '\0') {
445 char *atsign = strchr(cfg.host, '@');
446 /* Make sure we're not overflowing the user field */
448 if (atsign - cfg.host < sizeof cfg.username) {
449 strncpy(cfg.username, cfg.host, atsign - cfg.host);
450 cfg.username[atsign - cfg.host] = '\0';
452 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
457 * Trim a colon suffix off the hostname if it's there.
459 cfg.host[strcspn(cfg.host, ":")] = '\0';
462 * Remove any remaining whitespace from the hostname.
466 while (cfg.host[p2] != '\0') {
467 if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
468 cfg.host[p1] = cfg.host[p2];
478 * Select protocol. This is farmed out into a table in a
479 * separate file to enable an ssh-free variant.
484 for (i = 0; backends[i].backend != NULL; i++)
485 if (backends[i].protocol == cfg.protocol) {
486 back = backends[i].backend;
490 MessageBox(NULL, "Unsupported protocol number found",
491 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
497 /* Check for invalid Port number (i.e. zero) */
499 MessageBox(NULL, "Invalid Port Number",
500 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
507 wndclass.lpfnWndProc = WndProc;
508 wndclass.cbClsExtra = 0;
509 wndclass.cbWndExtra = 0;
510 wndclass.hInstance = inst;
511 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
512 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
513 wndclass.hbrBackground = NULL;
514 wndclass.lpszMenuName = NULL;
515 wndclass.lpszClassName = appname;
517 RegisterClass(&wndclass);
522 memset(&ucsdata, 0, sizeof(ucsdata));
524 term = term_init(&cfg, &ucsdata, NULL);
525 logctx = log_init(NULL, &cfg);
526 term_provide_logctx(term, logctx);
531 * Guess some defaults for the window size. This all gets
532 * updated later, so we don't really care too much. However, we
533 * do want the font width/height guesses to correspond to a
534 * large font rather than a small one...
541 term_size(term, cfg.height, cfg.width, cfg.savelines);
542 guess_width = extra_width + font_width * term->cols;
543 guess_height = extra_height + font_height * term->rows;
546 get_fullscreen_rect(&r);
547 if (guess_width > r.right - r.left)
548 guess_width = r.right - r.left;
549 if (guess_height > r.bottom - r.top)
550 guess_height = r.bottom - r.top;
554 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
557 winmode &= ~(WS_VSCROLL);
558 if (cfg.resize_action == RESIZE_DISABLED)
559 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
561 exwinmode |= WS_EX_TOPMOST;
563 exwinmode |= WS_EX_CLIENTEDGE;
564 hwnd = CreateWindowEx(exwinmode, appname, appname,
565 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
566 guess_width, guess_height,
567 NULL, NULL, inst, NULL);
571 * Initialise the fonts, simultaneously correcting the guesses
572 * for font_{width,height}.
577 * Correct the guesses for extra_{width,height}.
581 GetWindowRect(hwnd, &wr);
582 GetClientRect(hwnd, &cr);
583 offset_width = offset_height = cfg.window_border;
584 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
585 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
589 * Resize the window, now we know what size we _really_ want it
592 guess_width = extra_width + font_width * term->cols;
593 guess_height = extra_height + font_height * term->rows;
594 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
595 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
598 * Set up a caret bitmap, with no content.
602 int size = (font_width + 15) / 16 * 2 * font_height;
603 bits = snewn(size, char);
604 memset(bits, 0, size);
605 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
608 CreateCaret(hwnd, caretbm, font_width, font_height);
611 * Initialise the scroll bar.
616 si.cbSize = sizeof(si);
617 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
619 si.nMax = term->rows - 1;
620 si.nPage = term->rows;
622 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
626 * Start up the telnet connection.
630 char msg[1024], *title;
633 error = back->init((void *)term, &backhandle, &cfg,
634 cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
635 back->provide_logctx(backhandle, logctx);
637 sprintf(msg, "Unable to open connection to\n"
638 "%.800s\n" "%s", cfg.host, error);
639 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
642 window_name = icon_name = NULL;
644 title = cfg.wintitle;
646 sprintf(msg, "%s - PuTTY", realhost);
650 set_title(NULL, title);
651 set_icon(NULL, title);
655 * Connect the terminal to the backend for resize purposes.
657 term_provide_resize_fn(term, back->size, backhandle);
660 * Set up a line discipline.
662 ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
664 session_closed = FALSE;
667 * Prepare the mouse handler.
669 lastact = MA_NOTHING;
670 lastbtn = MBT_NOTHING;
671 dbltime = GetDoubleClickTime();
674 * Set up the session-control options on the system menu.
677 HMENU m = GetSystemMenu(hwnd, FALSE);
681 AppendMenu(m, MF_SEPARATOR, 0, 0);
682 if (cfg.protocol == PROT_TELNET) {
684 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
685 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
686 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
687 AppendMenu(p, MF_SEPARATOR, 0, 0);
688 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
689 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
690 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
691 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
692 AppendMenu(p, MF_SEPARATOR, 0, 0);
693 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
694 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
695 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
696 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
697 AppendMenu(p, MF_SEPARATOR, 0, 0);
698 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
699 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
700 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
702 AppendMenu(m, MF_SEPARATOR, 0, 0);
704 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
705 AppendMenu(m, MF_SEPARATOR, 0, 0);
706 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
707 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
709 get_sesslist(&sesslist, TRUE);
711 i < ((sesslist.nsessions < 256) ? sesslist.nsessions : 256);
713 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
714 sesslist.sessions[i]);
715 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
716 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
717 AppendMenu(m, MF_SEPARATOR, 0, 0);
718 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
719 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
720 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
721 AppendMenu(m, MF_SEPARATOR, 0, 0);
722 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
723 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
724 AppendMenu(m, MF_SEPARATOR, 0, 0);
726 AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
727 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
731 * Set up the initial input locale.
733 set_input_locale(GetKeyboardLayout(0));
736 * Open the initial log file if there is one.
741 * Finally show the window!
743 ShowWindow(hwnd, show);
744 SetForegroundWindow(hwnd);
747 * Set the palette up.
753 term->has_focus = (GetForegroundWindow() == hwnd);
756 if (GetMessage(&msg, NULL, 0, 0) == 1) {
757 int timer_id = 0, long_timer = 0;
759 while (msg.message != WM_QUIT) {
760 /* Sometimes DispatchMessage calls routines that use their own
761 * GetMessage loop, setup this timer so we get some control back.
763 * Also call term_update() from the timer so that if the host
764 * is sending data flat out we still do redraws.
766 if (timer_id && long_timer) {
767 KillTimer(hwnd, timer_id);
768 long_timer = timer_id = 0;
771 timer_id = SetTimer(hwnd, 1, 20, NULL);
772 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
773 DispatchMessage(&msg);
775 /* Make sure we blink everything that needs it. */
778 /* Send the paste buffer if there's anything to send */
781 /* If there's nothing new in the queue then we can do everything
782 * we've delayed, reading the socket, writing, and repainting
785 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
788 if (pending_netevent) {
789 enact_pending_netevent();
791 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
795 /* Okay there is now nothing to do so we make sure the screen is
796 * completely up to date then tell windows to call us in a little
800 KillTimer(hwnd, timer_id);
804 if (GetCapture() != hwnd ||
806 !(cfg.mouse_override && is_shift_pressed())))
811 flash_window(1); /* maintain */
813 /* The messages seem unreliable; especially if we're being tricky */
814 term->has_focus = (GetForegroundWindow() == hwnd);
817 /* Hmm, term_update didn't want to do an update too soon ... */
818 timer_id = SetTimer(hwnd, 1, 50, NULL);
819 else if (!term->has_focus)
820 timer_id = SetTimer(hwnd, 1, 500, NULL);
822 timer_id = SetTimer(hwnd, 1, 100, NULL);
825 /* There's no point rescanning everything in the message queue
826 * so we do an apparently unnecessary wait here
829 if (GetMessage(&msg, NULL, 0, 0) != 1)
834 cleanup_exit(msg.wParam); /* this doesn't return... */
835 return msg.wParam; /* ... but optimiser doesn't know */
841 void cleanup_exit(int code)
854 if (cfg.protocol == PROT_SSH) {
865 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
867 char *do_select(SOCKET skt, int startup)
872 events = (FD_CONNECT | FD_READ | FD_WRITE |
873 FD_OOB | FD_CLOSE | FD_ACCEPT);
878 return "do_select(): internal error (hwnd==NULL)";
879 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
880 switch (WSAGetLastError()) {
882 return "Network is down";
884 return "WSAAsyncSelect(): unknown error";
891 * set or clear the "raw mouse message" mode
893 void set_raw_mouse_mode(void *frontend, int activate)
895 activate = activate && !cfg.no_mouse_rep;
896 send_raw_mouse = activate;
897 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
901 * Print a message box and close the connection.
903 void connection_fatal(void *frontend, char *fmt, ...)
909 vsprintf(stuff, fmt, ap);
911 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
912 if (cfg.close_on_exit == FORCE_ON)
915 session_closed = TRUE;
916 set_icon(NULL, "PuTTY (inactive)");
917 set_title(NULL, "PuTTY (inactive)");
922 * Report an error at the command-line parsing stage.
924 void cmdline_error(char *fmt, ...)
930 vsprintf(stuff, fmt, ap);
932 MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
937 * Actually do the job requested by a WM_NETEVENT
939 static void enact_pending_netevent(void)
941 static int reentering = 0;
942 extern int select_result(WPARAM, LPARAM);
946 return; /* don't unpend the pending */
948 pending_netevent = FALSE;
951 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
954 if (ret == 0 && !session_closed) {
955 /* Abnormal exits will already have set session_closed and taken
956 * appropriate action. */
957 if (cfg.close_on_exit == FORCE_ON ||
958 cfg.close_on_exit == AUTO) PostQuitMessage(0);
960 session_closed = TRUE;
961 set_icon(NULL, "PuTTY (inactive)");
962 set_title(NULL, "PuTTY (inactive)");
963 MessageBox(hwnd, "Connection closed by remote host",
964 "PuTTY", MB_OK | MB_ICONINFORMATION);
970 * Copy the colour palette from the configuration data into defpal.
971 * This is non-trivial because the colour indices are different.
973 static void cfgtopalette(void)
976 static const int ww[] = {
977 6, 7, 8, 9, 10, 11, 12, 13,
978 14, 15, 16, 17, 18, 19, 20, 21,
979 0, 1, 2, 3, 4, 4, 5, 5
982 for (i = 0; i < 24; i++) {
984 defpal[i].rgbtRed = cfg.colours[w][0];
985 defpal[i].rgbtGreen = cfg.colours[w][1];
986 defpal[i].rgbtBlue = cfg.colours[w][2];
991 * Set up the colour palette.
993 static void init_palette(void)
996 HDC hdc = GetDC(hwnd);
998 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
1000 * This is a genuine case where we must use smalloc
1001 * because the snew macros can't cope.
1003 logpal = smalloc(sizeof(*logpal)
1004 - sizeof(logpal->palPalEntry)
1005 + NCOLOURS * sizeof(PALETTEENTRY));
1006 logpal->palVersion = 0x300;
1007 logpal->palNumEntries = NCOLOURS;
1008 for (i = 0; i < NCOLOURS; i++) {
1009 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1010 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1011 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1012 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1014 pal = CreatePalette(logpal);
1016 SelectPalette(hdc, pal, FALSE);
1017 RealizePalette(hdc);
1018 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1021 ReleaseDC(hwnd, hdc);
1024 for (i = 0; i < NCOLOURS; i++)
1025 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1026 defpal[i].rgbtGreen,
1027 defpal[i].rgbtBlue);
1029 for (i = 0; i < NCOLOURS; i++)
1030 colours[i] = RGB(defpal[i].rgbtRed,
1031 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1035 * Initialise all the fonts we will need initially. There may be as many as
1036 * three or as few as one. The other (poentially) twentyone fonts are done
1037 * if/when they are needed.
1041 * - check the font width and height, correcting our guesses if
1044 * - verify that the bold font is the same width as the ordinary
1045 * one, and engage shadow bolding if not.
1047 * - verify that the underlined font is the same width as the
1048 * ordinary one (manual underlining by means of line drawing can
1049 * be done in a pinch).
1051 static void init_fonts(int pick_width, int pick_height)
1058 int fw_dontcare, fw_bold;
1060 for (i = 0; i < FONT_MAXNO; i++)
1063 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1064 und_mode = UND_FONT;
1066 if (cfg.font.isbold) {
1067 fw_dontcare = FW_BOLD;
1070 fw_dontcare = FW_DONTCARE;
1077 font_height = pick_height;
1079 font_height = cfg.font.height;
1080 if (font_height > 0) {
1082 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1085 font_width = pick_width;
1087 #define f(i,c,w,u) \
1088 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1089 c, OUT_DEFAULT_PRECIS, \
1090 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1091 FIXED_PITCH | FF_DONTCARE, cfg.font.name)
1093 f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
1095 lfont.lfHeight = font_height;
1096 lfont.lfWidth = font_width;
1097 lfont.lfEscapement = 0;
1098 lfont.lfOrientation = 0;
1099 lfont.lfWeight = fw_dontcare;
1100 lfont.lfItalic = FALSE;
1101 lfont.lfUnderline = FALSE;
1102 lfont.lfStrikeOut = FALSE;
1103 lfont.lfCharSet = cfg.font.charset;
1104 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1105 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1106 lfont.lfQuality = DEFAULT_QUALITY;
1107 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1108 strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
1110 SelectObject(hdc, fonts[FONT_NORMAL]);
1111 GetTextMetrics(hdc, &tm);
1113 if (pick_width == 0 || pick_height == 0) {
1114 font_height = tm.tmHeight;
1115 font_width = tm.tmAveCharWidth;
1117 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1119 #ifdef RDB_DEBUG_PATCH
1120 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1121 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1126 DWORD cset = tm.tmCharSet;
1127 memset(&info, 0xFF, sizeof(info));
1129 /* !!! Yes the next line is right */
1130 if (cset == OEM_CHARSET)
1131 ucsdata.font_codepage = GetOEMCP();
1133 if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
1134 ucsdata.font_codepage = info.ciACP;
1136 ucsdata.font_codepage = -1;
1138 GetCPInfo(ucsdata.font_codepage, &cpinfo);
1139 ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1142 f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
1145 * Some fonts, e.g. 9-pt Courier, draw their underlines
1146 * outside their character cell. We successfully prevent
1147 * screen corruption by clipping the text output, but then
1148 * we lose the underline completely. Here we try to work
1149 * out whether this is such a font, and if it is, we set a
1150 * flag that causes underlines to be drawn by hand.
1152 * Having tried other more sophisticated approaches (such
1153 * as examining the TEXTMETRIC structure or requesting the
1154 * height of a string), I think we'll do this the brute
1155 * force way: we create a small bitmap, draw an underlined
1156 * space on it, and test to see whether any pixels are
1157 * foreground-coloured. (Since we expect the underline to
1158 * go all the way across the character cell, we only search
1159 * down a single column of the bitmap, half way across.)
1163 HBITMAP und_bm, und_oldbm;
1167 und_dc = CreateCompatibleDC(hdc);
1168 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1169 und_oldbm = SelectObject(und_dc, und_bm);
1170 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1171 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1172 SetTextColor(und_dc, RGB(255, 255, 255));
1173 SetBkColor(und_dc, RGB(0, 0, 0));
1174 SetBkMode(und_dc, OPAQUE);
1175 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1177 for (i = 0; i < font_height; i++) {
1178 c = GetPixel(und_dc, font_width / 2, i);
1179 if (c != RGB(0, 0, 0))
1182 SelectObject(und_dc, und_oldbm);
1183 DeleteObject(und_bm);
1186 und_mode = UND_LINE;
1187 DeleteObject(fonts[FONT_UNDERLINE]);
1188 fonts[FONT_UNDERLINE] = 0;
1192 if (bold_mode == BOLD_FONT) {
1193 f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
1197 descent = tm.tmAscent + 1;
1198 if (descent >= font_height)
1199 descent = font_height - 1;
1201 for (i = 0; i < 3; i++) {
1203 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1204 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1211 ReleaseDC(hwnd, hdc);
1213 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1214 und_mode = UND_LINE;
1215 DeleteObject(fonts[FONT_UNDERLINE]);
1216 fonts[FONT_UNDERLINE] = 0;
1219 if (bold_mode == BOLD_FONT &&
1220 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1221 bold_mode = BOLD_SHADOW;
1222 DeleteObject(fonts[FONT_BOLD]);
1223 fonts[FONT_BOLD] = 0;
1225 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1227 init_ucs(&cfg, &ucsdata);
1230 static void another_font(int fontno)
1233 int fw_dontcare, fw_bold;
1237 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1240 basefont = (fontno & ~(FONT_BOLDUND));
1241 if (basefont != fontno && !fontflag[basefont])
1242 another_font(basefont);
1244 if (cfg.font.isbold) {
1245 fw_dontcare = FW_BOLD;
1248 fw_dontcare = FW_DONTCARE;
1252 c = cfg.font.charset;
1258 if (fontno & FONT_WIDE)
1260 if (fontno & FONT_NARROW)
1262 if (fontno & FONT_OEM)
1264 if (fontno & FONT_BOLD)
1266 if (fontno & FONT_UNDERLINE)
1270 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1271 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1272 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1273 FIXED_PITCH | FF_DONTCARE, s);
1275 fontflag[fontno] = 1;
1278 static void deinit_fonts(void)
1281 for (i = 0; i < FONT_MAXNO; i++) {
1283 DeleteObject(fonts[i]);
1289 void request_resize(void *frontend, int w, int h)
1293 /* If the window is maximized supress resizing attempts */
1294 if (IsZoomed(hwnd)) {
1295 if (cfg.resize_action == RESIZE_TERM)
1299 if (cfg.resize_action == RESIZE_DISABLED) return;
1300 if (h == term->rows && w == term->cols) return;
1302 /* Sanity checks ... */
1304 static int first_time = 1;
1307 switch (first_time) {
1309 /* Get the size of the screen */
1310 if (get_fullscreen_rect(&ss))
1311 /* first_time = 0 */ ;
1317 /* Make sure the values are sane */
1318 width = (ss.right - ss.left - extra_width) / 4;
1319 height = (ss.bottom - ss.top - extra_height) / 6;
1321 if (w > width || h > height)
1330 term_size(term, h, w, cfg.savelines);
1332 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1333 width = extra_width + font_width * w;
1334 height = extra_height + font_height * h;
1336 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1337 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1338 SWP_NOMOVE | SWP_NOZORDER);
1342 InvalidateRect(hwnd, NULL, TRUE);
1345 static void reset_window(int reinit) {
1347 * This function decides how to resize or redraw when the
1348 * user changes something.
1350 * This function doesn't like to change the terminal size but if the
1351 * font size is locked that may be it's only soluion.
1353 int win_width, win_height;
1356 #ifdef RDB_DEBUG_PATCH
1357 debug((27, "reset_window()"));
1360 /* Current window sizes ... */
1361 GetWindowRect(hwnd, &wr);
1362 GetClientRect(hwnd, &cr);
1364 win_width = cr.right - cr.left;
1365 win_height = cr.bottom - cr.top;
1367 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1369 /* Are we being forced to reload the fonts ? */
1371 #ifdef RDB_DEBUG_PATCH
1372 debug((27, "reset_window() -- Forced deinit"));
1378 /* Oh, looks like we're minimised */
1379 if (win_width == 0 || win_height == 0)
1382 /* Is the window out of position ? */
1384 (offset_width != (win_width-font_width*term->cols)/2 ||
1385 offset_height != (win_height-font_height*term->rows)/2) ){
1386 offset_width = (win_width-font_width*term->cols)/2;
1387 offset_height = (win_height-font_height*term->rows)/2;
1388 InvalidateRect(hwnd, NULL, TRUE);
1389 #ifdef RDB_DEBUG_PATCH
1390 debug((27, "reset_window() -> Reposition terminal"));
1394 if (IsZoomed(hwnd)) {
1395 /* We're fullscreen, this means we must not change the size of
1396 * the window so it's the font size or the terminal itself.
1399 extra_width = wr.right - wr.left - cr.right + cr.left;
1400 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1402 if (cfg.resize_action != RESIZE_TERM) {
1403 if ( font_width != win_width/term->cols ||
1404 font_height != win_height/term->rows) {
1406 init_fonts(win_width/term->cols, win_height/term->rows);
1407 offset_width = (win_width-font_width*term->cols)/2;
1408 offset_height = (win_height-font_height*term->rows)/2;
1409 InvalidateRect(hwnd, NULL, TRUE);
1410 #ifdef RDB_DEBUG_PATCH
1411 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1412 font_width, font_height));
1416 if ( font_width != win_width/term->cols ||
1417 font_height != win_height/term->rows) {
1418 /* Our only choice at this point is to change the
1419 * size of the terminal; Oh well.
1421 term_size(term, win_height/font_height, win_width/font_width,
1423 offset_width = (win_width-font_width*term->cols)/2;
1424 offset_height = (win_height-font_height*term->rows)/2;
1425 InvalidateRect(hwnd, NULL, TRUE);
1426 #ifdef RDB_DEBUG_PATCH
1427 debug((27, "reset_window() -> Zoomed term_size"));
1434 /* Hmm, a force re-init means we should ignore the current window
1435 * so we resize to the default font size.
1438 #ifdef RDB_DEBUG_PATCH
1439 debug((27, "reset_window() -> Forced re-init"));
1442 offset_width = offset_height = cfg.window_border;
1443 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1444 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1446 if (win_width != font_width*term->cols + offset_width*2 ||
1447 win_height != font_height*term->rows + offset_height*2) {
1449 /* If this is too large windows will resize it to the maximum
1450 * allowed window size, we will then be back in here and resize
1451 * the font or terminal to fit.
1453 SetWindowPos(hwnd, NULL, 0, 0,
1454 font_width*term->cols + extra_width,
1455 font_height*term->rows + extra_height,
1456 SWP_NOMOVE | SWP_NOZORDER);
1459 InvalidateRect(hwnd, NULL, TRUE);
1463 /* Okay the user doesn't want us to change the font so we try the
1464 * window. But that may be too big for the screen which forces us
1465 * to change the terminal.
1467 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1468 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1470 offset_width = offset_height = cfg.window_border;
1471 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1472 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1474 if (win_width != font_width*term->cols + offset_width*2 ||
1475 win_height != font_height*term->rows + offset_height*2) {
1480 get_fullscreen_rect(&ss);
1482 width = (ss.right - ss.left - extra_width) / font_width;
1483 height = (ss.bottom - ss.top - extra_height) / font_height;
1486 if ( term->rows > height || term->cols > width ) {
1487 if (cfg.resize_action == RESIZE_EITHER) {
1488 /* Make the font the biggest we can */
1489 if (term->cols > width)
1490 font_width = (ss.right - ss.left - extra_width)
1492 if (term->rows > height)
1493 font_height = (ss.bottom - ss.top - extra_height)
1497 init_fonts(font_width, font_height);
1499 width = (ss.right - ss.left - extra_width) / font_width;
1500 height = (ss.bottom - ss.top - extra_height) / font_height;
1502 if ( height > term->rows ) height = term->rows;
1503 if ( width > term->cols ) width = term->cols;
1504 term_size(term, height, width, cfg.savelines);
1505 #ifdef RDB_DEBUG_PATCH
1506 debug((27, "reset_window() -> term resize to (%d,%d)",
1512 SetWindowPos(hwnd, NULL, 0, 0,
1513 font_width*term->cols + extra_width,
1514 font_height*term->rows + extra_height,
1515 SWP_NOMOVE | SWP_NOZORDER);
1517 InvalidateRect(hwnd, NULL, TRUE);
1518 #ifdef RDB_DEBUG_PATCH
1519 debug((27, "reset_window() -> window resize to (%d,%d)",
1520 font_width*term->cols + extra_width,
1521 font_height*term->rows + extra_height));
1527 /* We're allowed to or must change the font but do we want to ? */
1529 if (font_width != (win_width-cfg.window_border*2)/term->cols ||
1530 font_height != (win_height-cfg.window_border*2)/term->rows) {
1533 init_fonts((win_width-cfg.window_border*2)/term->cols,
1534 (win_height-cfg.window_border*2)/term->rows);
1535 offset_width = (win_width-font_width*term->cols)/2;
1536 offset_height = (win_height-font_height*term->rows)/2;
1538 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1539 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1541 InvalidateRect(hwnd, NULL, TRUE);
1542 #ifdef RDB_DEBUG_PATCH
1543 debug((25, "reset_window() -> font resize to (%d,%d)",
1544 font_width, font_height));
1549 static void set_input_locale(HKL kl)
1553 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1554 lbuf, sizeof(lbuf));
1556 kbd_codepage = atoi(lbuf);
1559 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1561 int thistime = GetMessageTime();
1563 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1564 lastbtn = MBT_NOTHING;
1565 term_mouse(term, b, translate_button(b), MA_CLICK,
1566 x, y, shift, ctrl, alt);
1570 if (lastbtn == b && thistime - lasttime < dbltime) {
1571 lastact = (lastact == MA_CLICK ? MA_2CLK :
1572 lastact == MA_2CLK ? MA_3CLK :
1573 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1578 if (lastact != MA_NOTHING)
1579 term_mouse(term, b, translate_button(b), lastact,
1580 x, y, shift, ctrl, alt);
1581 lasttime = thistime;
1585 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1586 * into a cooked one (SELECT, EXTEND, PASTE).
1588 static Mouse_Button translate_button(Mouse_Button button)
1590 if (button == MBT_LEFT)
1592 if (button == MBT_MIDDLE)
1593 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1594 if (button == MBT_RIGHT)
1595 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1596 return 0; /* shouldn't happen */
1599 static void show_mouseptr(int show)
1601 static int cursor_visible = 1;
1602 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1604 if (cursor_visible && !show)
1606 else if (!cursor_visible && show)
1608 cursor_visible = show;
1611 static int is_alt_pressed(void)
1614 int r = GetKeyboardState(keystate);
1617 if (keystate[VK_MENU] & 0x80)
1619 if (keystate[VK_RMENU] & 0x80)
1624 static int is_shift_pressed(void)
1627 int r = GetKeyboardState(keystate);
1630 if (keystate[VK_SHIFT] & 0x80)
1635 static int resizing;
1637 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1638 WPARAM wParam, LPARAM lParam)
1641 static int ignore_clip = FALSE;
1642 static int need_backend_resize = FALSE;
1643 static int fullscr_on_max = FALSE;
1647 if (pending_netevent)
1648 enact_pending_netevent();
1649 if (GetCapture() != hwnd ||
1650 (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
1656 if (cfg.ping_interval > 0) {
1659 if (now - last_movement > cfg.ping_interval) {
1660 back->special(backhandle, TS_PING);
1661 last_movement = now;
1664 net_pending_errors();
1670 if (!cfg.warn_on_close || session_closed ||
1672 "Are you sure you want to close this session?",
1673 "PuTTY Exit Confirmation",
1674 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1675 DestroyWindow(hwnd);
1682 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1694 PROCESS_INFORMATION pi;
1695 HANDLE filemap = NULL;
1697 if (wParam == IDM_DUPSESS) {
1699 * Allocate a file-mapping memory chunk for the
1702 SECURITY_ATTRIBUTES sa;
1705 sa.nLength = sizeof(sa);
1706 sa.lpSecurityDescriptor = NULL;
1707 sa.bInheritHandle = TRUE;
1708 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1711 0, sizeof(Config), NULL);
1713 p = (Config *) MapViewOfFile(filemap,
1715 0, 0, sizeof(Config));
1717 *p = cfg; /* structure copy */
1721 sprintf(c, "putty &%p", filemap);
1723 } else if (wParam == IDM_SAVEDSESS) {
1724 if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
1726 sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
1727 cl = snewn(16 + strlen(session), char);
1728 /* 8, but play safe */
1731 /* not a very important failure mode */
1733 sprintf(cl, "putty @%s", session);
1741 GetModuleFileName(NULL, b, sizeof(b) - 1);
1743 si.lpReserved = NULL;
1744 si.lpDesktop = NULL;
1748 si.lpReserved2 = NULL;
1749 CreateProcess(b, cl, NULL, NULL, TRUE,
1750 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1753 CloseHandle(filemap);
1763 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1766 if (!do_reconfig(hwnd))
1770 /* Disable full-screen if resizing forbidden */
1771 HMENU m = GetSystemMenu (hwnd, FALSE);
1772 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1773 (cfg.resize_action == RESIZE_DISABLED)
1774 ? MF_GRAYED : MF_ENABLED);
1775 /* Gracefully unzoom if necessary */
1776 if (IsZoomed(hwnd) &&
1777 (cfg.resize_action == RESIZE_DISABLED)) {
1778 ShowWindow(hwnd, SW_RESTORE);
1782 /* Pass new config data to the logging module */
1783 log_reconfig(logctx, &cfg);
1787 * Flush the line discipline's edit buffer in the
1788 * case where local editing has just been disabled.
1790 ldisc_send(ldisc, NULL, 0, 0);
1798 /* Pass new config data to the terminal */
1799 term_reconfig(term, &cfg);
1801 /* Pass new config data to the back end */
1802 back->reconfig(backhandle, &cfg);
1804 /* Screen size changed ? */
1805 if (cfg.height != prev_cfg.height ||
1806 cfg.width != prev_cfg.width ||
1807 cfg.savelines != prev_cfg.savelines ||
1808 cfg.resize_action == RESIZE_FONT ||
1809 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1810 cfg.resize_action == RESIZE_DISABLED)
1811 term_size(term, cfg.height, cfg.width, cfg.savelines);
1813 /* Enable or disable the scroll bar, etc */
1815 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1816 LONG nexflag, exflag =
1817 GetWindowLong(hwnd, GWL_EXSTYLE);
1820 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1821 if (cfg.alwaysontop) {
1822 nexflag |= WS_EX_TOPMOST;
1823 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1824 SWP_NOMOVE | SWP_NOSIZE);
1826 nexflag &= ~(WS_EX_TOPMOST);
1827 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1828 SWP_NOMOVE | SWP_NOSIZE);
1831 if (cfg.sunken_edge)
1832 nexflag |= WS_EX_CLIENTEDGE;
1834 nexflag &= ~(WS_EX_CLIENTEDGE);
1837 if (is_full_screen() ?
1838 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1841 nflg &= ~WS_VSCROLL;
1843 if (cfg.resize_action == RESIZE_DISABLED ||
1845 nflg &= ~WS_THICKFRAME;
1847 nflg |= WS_THICKFRAME;
1849 if (cfg.resize_action == RESIZE_DISABLED)
1850 nflg &= ~WS_MAXIMIZEBOX;
1852 nflg |= WS_MAXIMIZEBOX;
1854 if (nflg != flag || nexflag != exflag) {
1856 SetWindowLong(hwnd, GWL_STYLE, nflg);
1857 if (nexflag != exflag)
1858 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1860 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1861 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1862 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1870 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1875 set_title(NULL, cfg.wintitle);
1876 if (IsIconic(hwnd)) {
1878 cfg.win_name_always ? window_name :
1882 if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
1883 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1884 cfg.font.isbold != prev_cfg.font.isbold ||
1885 cfg.font.height != prev_cfg.font.height ||
1886 cfg.font.charset != prev_cfg.font.charset ||
1887 cfg.vtmode != prev_cfg.vtmode ||
1888 cfg.bold_colour != prev_cfg.bold_colour ||
1889 cfg.resize_action == RESIZE_DISABLED ||
1890 cfg.resize_action == RESIZE_EITHER ||
1891 (cfg.resize_action != prev_cfg.resize_action))
1894 InvalidateRect(hwnd, NULL, TRUE);
1895 reset_window(init_lvl);
1896 net_pending_errors();
1907 ldisc_send(ldisc, NULL, 0, 0);
1910 back->special(backhandle, TS_AYT);
1911 net_pending_errors();
1914 back->special(backhandle, TS_BRK);
1915 net_pending_errors();
1918 back->special(backhandle, TS_SYNCH);
1919 net_pending_errors();
1922 back->special(backhandle, TS_EC);
1923 net_pending_errors();
1926 back->special(backhandle, TS_EL);
1927 net_pending_errors();
1930 back->special(backhandle, TS_GA);
1931 net_pending_errors();
1934 back->special(backhandle, TS_NOP);
1935 net_pending_errors();
1938 back->special(backhandle, TS_ABORT);
1939 net_pending_errors();
1942 back->special(backhandle, TS_AO);
1943 net_pending_errors();
1946 back->special(backhandle, TS_IP);
1947 net_pending_errors();
1950 back->special(backhandle, TS_SUSP);
1951 net_pending_errors();
1954 back->special(backhandle, TS_EOR);
1955 net_pending_errors();
1958 back->special(backhandle, TS_EOF);
1959 net_pending_errors();
1965 WinHelp(hwnd, help_path,
1966 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1970 * We get this if the System menu has been activated
1977 * We get this if the System menu has been activated
1978 * using the keyboard. This might happen from within
1979 * TranslateKey, in which case it really wants to be
1980 * followed by a `space' character to actually _bring
1981 * the menu up_ rather than just sitting there in
1982 * `ready to appear' state.
1984 show_mouseptr(1); /* make sure pointer is visible */
1986 PostMessage(hwnd, WM_CHAR, ' ', 0);
1988 case IDM_FULLSCREEN:
1992 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1993 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1998 #define X_POS(l) ((int)(short)LOWORD(l))
1999 #define Y_POS(l) ((int)(short)HIWORD(l))
2001 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2002 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
2003 case WM_LBUTTONDOWN:
2004 case WM_MBUTTONDOWN:
2005 case WM_RBUTTONDOWN:
2013 case WM_LBUTTONDOWN:
2017 case WM_MBUTTONDOWN:
2018 button = MBT_MIDDLE;
2021 case WM_RBUTTONDOWN:
2030 button = MBT_MIDDLE;
2038 button = press = 0; /* shouldn't happen */
2042 * Special case: in full-screen mode, if the left
2043 * button is clicked in the very top left corner of the
2044 * window, we put up the System menu instead of doing
2047 if (is_full_screen() && press && button == MBT_LEFT &&
2048 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2049 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2054 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2055 wParam & MK_SHIFT, wParam & MK_CONTROL,
2059 term_mouse(term, button, translate_button(button), MA_RELEASE,
2060 TO_CHR_X(X_POS(lParam)),
2061 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2062 wParam & MK_CONTROL, is_alt_pressed());
2070 * Add the mouse position and message time to the random
2073 noise_ultralight(lParam);
2075 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2076 GetCapture() == hwnd) {
2078 if (wParam & MK_LBUTTON)
2080 else if (wParam & MK_MBUTTON)
2084 term_mouse(term, b, translate_button(b), MA_DRAG,
2085 TO_CHR_X(X_POS(lParam)),
2086 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2087 wParam & MK_CONTROL, is_alt_pressed());
2090 case WM_NCMOUSEMOVE:
2092 noise_ultralight(lParam);
2094 case WM_IGNORE_CLIP:
2095 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2097 case WM_DESTROYCLIPBOARD:
2099 term_deselect(term);
2100 ignore_clip = FALSE;
2106 hdc = BeginPaint(hwnd, &p);
2108 SelectPalette(hdc, pal, TRUE);
2109 RealizePalette(hdc);
2111 term_paint(term, hdc,
2112 (p.rcPaint.left-offset_width)/font_width,
2113 (p.rcPaint.top-offset_height)/font_height,
2114 (p.rcPaint.right-offset_width-1)/font_width,
2115 (p.rcPaint.bottom-offset_height-1)/font_height,
2119 p.rcPaint.left < offset_width ||
2120 p.rcPaint.top < offset_height ||
2121 p.rcPaint.right >= offset_width + font_width*term->cols ||
2122 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2124 HBRUSH fillcolour, oldbrush;
2126 fillcolour = CreateSolidBrush (
2127 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2128 oldbrush = SelectObject(hdc, fillcolour);
2129 edge = CreatePen(PS_SOLID, 0,
2130 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2131 oldpen = SelectObject(hdc, edge);
2134 * Jordan Russell reports that this apparently
2135 * ineffectual IntersectClipRect() call masks a
2136 * Windows NT/2K bug causing strange display
2137 * problems when the PuTTY window is taller than
2138 * the primary monitor. It seems harmless enough...
2140 IntersectClipRect(hdc,
2141 p.rcPaint.left, p.rcPaint.top,
2142 p.rcPaint.right, p.rcPaint.bottom);
2144 ExcludeClipRect(hdc,
2145 offset_width, offset_height,
2146 offset_width+font_width*term->cols,
2147 offset_height+font_height*term->rows);
2149 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2150 p.rcPaint.right, p.rcPaint.bottom);
2152 // SelectClipRgn(hdc, NULL);
2154 SelectObject(hdc, oldbrush);
2155 DeleteObject(fillcolour);
2156 SelectObject(hdc, oldpen);
2159 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2160 SelectObject(hdc, GetStockObject(WHITE_PEN));
2166 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2167 * but the only one that's likely to try to overload us is FD_READ.
2168 * This means buffering just one is fine.
2170 if (pending_netevent)
2171 enact_pending_netevent();
2173 pending_netevent = TRUE;
2174 pend_netevent_wParam = wParam;
2175 pend_netevent_lParam = lParam;
2176 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2177 enact_pending_netevent();
2179 time(&last_movement);
2182 term->has_focus = TRUE;
2183 CreateCaret(hwnd, caretbm, font_width, font_height);
2185 flash_window(0); /* stop */
2192 term->has_focus = FALSE;
2194 caret_x = caret_y = -1; /* ensure caret is replaced next time */
2198 case WM_ENTERSIZEMOVE:
2199 #ifdef RDB_DEBUG_PATCH
2200 debug((27, "WM_ENTERSIZEMOVE"));
2204 need_backend_resize = FALSE;
2206 case WM_EXITSIZEMOVE:
2209 #ifdef RDB_DEBUG_PATCH
2210 debug((27, "WM_EXITSIZEMOVE"));
2212 if (need_backend_resize) {
2213 term_size(term, cfg.height, cfg.width, cfg.savelines);
2214 InvalidateRect(hwnd, NULL, TRUE);
2219 * This does two jobs:
2220 * 1) Keep the sizetip uptodate
2221 * 2) Make sure the window size is _stepped_ in units of the font size.
2223 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2224 int width, height, w, h, ew, eh;
2225 LPRECT r = (LPRECT) lParam;
2227 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2228 (cfg.height != term->rows || cfg.width != term->cols )) {
2230 * Great! It seems that both the terminal size and the
2231 * font size have been changed and the user is now dragging.
2233 * It will now be difficult to get back to the configured
2236 * This would be easier but it seems to be too confusing.
2238 term_size(term, cfg.height, cfg.width, cfg.savelines);
2241 cfg.height=term->rows; cfg.width=term->cols;
2243 InvalidateRect(hwnd, NULL, TRUE);
2244 need_backend_resize = TRUE;
2247 width = r->right - r->left - extra_width;
2248 height = r->bottom - r->top - extra_height;
2249 w = (width + font_width / 2) / font_width;
2252 h = (height + font_height / 2) / font_height;
2255 UpdateSizeTip(hwnd, w, h);
2256 ew = width - w * font_width;
2257 eh = height - h * font_height;
2259 if (wParam == WMSZ_LEFT ||
2260 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2266 if (wParam == WMSZ_TOP ||
2267 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2277 int width, height, w, h, rv = 0;
2278 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2279 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2280 LPRECT r = (LPRECT) lParam;
2282 width = r->right - r->left - ex_width;
2283 height = r->bottom - r->top - ex_height;
2285 w = (width + term->cols/2)/term->cols;
2286 h = (height + term->rows/2)/term->rows;
2287 if ( r->right != r->left + w*term->cols + ex_width)
2290 if (wParam == WMSZ_LEFT ||
2291 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2292 r->left = r->right - w*term->cols - ex_width;
2294 r->right = r->left + w*term->cols + ex_width;
2296 if (r->bottom != r->top + h*term->rows + ex_height)
2299 if (wParam == WMSZ_TOP ||
2300 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2301 r->top = r->bottom - h*term->rows - ex_height;
2303 r->bottom = r->top + h*term->rows + ex_height;
2307 /* break; (never reached) */
2308 case WM_FULLSCR_ON_MAX:
2309 fullscr_on_max = TRUE;
2312 sys_cursor_update();
2315 #ifdef RDB_DEBUG_PATCH
2316 debug((27, "WM_SIZE %s (%d,%d)",
2317 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2318 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2319 (wParam == SIZE_RESTORED && resizing) ? "to":
2320 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2322 LOWORD(lParam), HIWORD(lParam)));
2324 if (wParam == SIZE_MINIMIZED)
2326 cfg.win_name_always ? window_name : icon_name);
2327 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2328 SetWindowText(hwnd, window_name);
2329 if (wParam == SIZE_RESTORED)
2330 clear_full_screen();
2331 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2332 fullscr_on_max = FALSE;
2336 if (cfg.resize_action == RESIZE_DISABLED) {
2337 /* A resize, well it better be a minimize. */
2341 int width, height, w, h;
2343 width = LOWORD(lParam);
2344 height = HIWORD(lParam);
2347 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2349 prev_rows = term->rows;
2350 prev_cols = term->cols;
2351 if (cfg.resize_action == RESIZE_TERM) {
2352 w = width / font_width;
2354 h = height / font_height;
2357 term_size(term, h, w, cfg.savelines);
2360 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2362 if (cfg.resize_action == RESIZE_TERM)
2363 term_size(term, prev_rows, prev_cols, cfg.savelines);
2364 if (cfg.resize_action != RESIZE_FONT)
2369 /* This is an unexpected resize, these will normally happen
2370 * if the window is too large. Probably either the user
2371 * selected a huge font or the screen size has changed.
2373 * This is also called with minimize.
2375 else reset_window(-1);
2379 * Don't call back->size in mid-resize. (To prevent
2380 * massive numbers of resize events getting sent
2381 * down the connection during an NT opaque drag.)
2384 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2385 need_backend_resize = TRUE;
2386 w = (width-cfg.window_border*2) / font_width;
2388 h = (height-cfg.window_border*2) / font_height;
2397 sys_cursor_update();
2400 switch (LOWORD(wParam)) {
2402 term_scroll(term, -1, 0);
2405 term_scroll(term, +1, 0);
2408 term_scroll(term, 0, +1);
2411 term_scroll(term, 0, -1);
2414 term_scroll(term, 0, +term->rows / 2);
2417 term_scroll(term, 0, -term->rows / 2);
2419 case SB_THUMBPOSITION:
2421 term_scroll(term, 1, HIWORD(wParam));
2425 case WM_PALETTECHANGED:
2426 if ((HWND) wParam != hwnd && pal != NULL) {
2427 HDC hdc = get_ctx(NULL);
2429 if (RealizePalette(hdc) > 0)
2435 case WM_QUERYNEWPALETTE:
2437 HDC hdc = get_ctx(NULL);
2439 if (RealizePalette(hdc) > 0)
2451 * Add the scan code and keypress timing to the random
2454 noise_ultralight(lParam);
2457 * We don't do TranslateMessage since it disassociates the
2458 * resulting CHAR message from the KEYDOWN that sparked it,
2459 * which we occasionally don't want. Instead, we process
2460 * KEYDOWN, and call the Win32 translator functions so that
2461 * we get the translations under _our_ control.
2464 unsigned char buf[20];
2467 if (wParam == VK_PROCESSKEY) {
2470 m.message = WM_KEYDOWN;
2472 m.lParam = lParam & 0xdfff;
2473 TranslateMessage(&m);
2475 len = TranslateKey(message, wParam, lParam, buf);
2477 return DefWindowProc(hwnd, message, wParam, lParam);
2481 * Interrupt an ongoing paste. I'm not sure
2482 * this is sensible, but for the moment it's
2483 * preferable to having to faff about buffering
2489 * We need not bother about stdin backlogs
2490 * here, because in GUI PuTTY we can't do
2491 * anything about it anyway; there's no means
2492 * of asking Windows to hold off on KEYDOWN
2493 * messages. We _have_ to buffer everything
2496 term_seen_key_event(term);
2497 ldisc_send(ldisc, buf, len, 1);
2502 net_pending_errors();
2504 case WM_INPUTLANGCHANGE:
2505 /* wParam == Font number */
2506 /* lParam == Locale */
2507 set_input_locale((HKL)lParam);
2508 sys_cursor_update();
2511 if(wParam == IMN_SETOPENSTATUS) {
2512 HIMC hImc = ImmGetContext(hwnd);
2513 ImmSetCompositionFont(hImc, &lfont);
2514 ImmReleaseContext(hwnd, hImc);
2518 case WM_IME_COMPOSITION:
2524 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2525 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2527 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2528 break; /* fall back to DefWindowProc */
2530 hIMC = ImmGetContext(hwnd);
2531 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2535 buff = snewn(n, char);
2536 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2538 * Jaeyoun Chung reports that Korean character
2539 * input doesn't work correctly if we do a single
2540 * luni_send() covering the whole of buff. So
2541 * instead we luni_send the characters one by one.
2543 term_seen_key_event(term);
2544 for (i = 0; i < n; i += 2) {
2545 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2549 ImmReleaseContext(hwnd, hIMC);
2554 if (wParam & 0xFF00) {
2555 unsigned char buf[2];
2558 buf[0] = wParam >> 8;
2559 term_seen_key_event(term);
2560 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
2562 char c = (unsigned char) wParam;
2563 term_seen_key_event(term);
2564 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
2570 * Nevertheless, we are prepared to deal with WM_CHAR
2571 * messages, should they crop up. So if someone wants to
2572 * post the things to us as part of a macro manoeuvre,
2573 * we're ready to cope.
2576 char c = (unsigned char)wParam;
2577 term_seen_key_event(term);
2578 lpage_send(ldisc, CP_ACP, &c, 1, 1);
2582 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2583 SetCursor(LoadCursor(NULL, IDC_ARROW));
2587 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
2588 int shift_pressed=0, control_pressed=0;
2590 if (message == WM_MOUSEWHEEL) {
2591 wheel_accumulator += (short)HIWORD(wParam);
2592 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2593 control_pressed=LOWORD(wParam) & MK_CONTROL;
2596 wheel_accumulator += (int)wParam;
2597 if (GetKeyboardState(keys)!=0) {
2598 shift_pressed=keys[VK_SHIFT]&0x80;
2599 control_pressed=keys[VK_CONTROL]&0x80;
2603 /* process events when the threshold is reached */
2604 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2607 /* reduce amount for next time */
2608 if (wheel_accumulator > 0) {
2610 wheel_accumulator -= WHEEL_DELTA;
2611 } else if (wheel_accumulator < 0) {
2613 wheel_accumulator += WHEEL_DELTA;
2617 if (send_raw_mouse &&
2618 !(cfg.mouse_override && shift_pressed)) {
2619 /* send a mouse-down followed by a mouse up */
2620 term_mouse(term, b, translate_button(b),
2622 TO_CHR_X(X_POS(lParam)),
2623 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2624 control_pressed, is_alt_pressed());
2625 term_mouse(term, b, translate_button(b),
2626 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2627 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2628 control_pressed, is_alt_pressed());
2630 /* trigger a scroll */
2631 term_scroll(term, 0,
2633 -term->rows / 2 : term->rows / 2);
2640 return DefWindowProc(hwnd, message, wParam, lParam);
2644 * Move the system caret. (We maintain one, even though it's
2645 * invisible, for the benefit of blind people: apparently some
2646 * helper software tracks the system caret, so we should arrange to
2649 void sys_cursor(void *frontend, int x, int y)
2653 if (!term->has_focus) return;
2656 * Avoid gratuitously re-updating the cursor position and IMM
2657 * window if there's no actual change required.
2659 cx = x * font_width + offset_width;
2660 cy = y * font_height + offset_height;
2661 if (cx == caret_x && cy == caret_y)
2666 sys_cursor_update();
2669 static void sys_cursor_update(void)
2674 if (!term->has_focus) return;
2676 if (caret_x < 0 || caret_y < 0)
2679 SetCaretPos(caret_x, caret_y);
2681 /* IMM calls on Win98 and beyond only */
2682 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2684 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2685 osVersion.dwMinorVersion == 0) return; /* 95 */
2687 /* we should have the IMM functions */
2688 hIMC = ImmGetContext(hwnd);
2689 cf.dwStyle = CFS_POINT;
2690 cf.ptCurrentPos.x = caret_x;
2691 cf.ptCurrentPos.y = caret_y;
2692 ImmSetCompositionWindow(hIMC, &cf);
2694 ImmReleaseContext(hwnd, hIMC);
2698 * Draw a line of text in the window, at given character
2699 * coordinates, in given attributes.
2701 * We are allowed to fiddle with the contents of `text'.
2703 void do_text(Context ctx, int x, int y, char *text, int len,
2704 unsigned long attr, int lattr)
2707 int nfg, nbg, nfont;
2710 int force_manual_underline = 0;
2711 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2712 int char_width = fnt_width;
2713 int text_adjust = 0;
2714 static int *IpDx = 0, IpDxLEN = 0;
2716 if (attr & ATTR_WIDE)
2719 if (len > IpDxLEN || IpDx[0] != char_width) {
2721 if (len > IpDxLEN) {
2723 IpDx = snewn(len + 16, int);
2724 IpDxLEN = (len + 16);
2726 for (i = 0; i < IpDxLEN; i++)
2727 IpDx[i] = char_width;
2730 /* Only want the left half of double width lines */
2731 if (lattr != LATTR_NORM && x*2 >= term->cols)
2739 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
2740 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2741 attr ^= ATTR_CUR_XOR;
2745 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2746 /* Assume a poorman font is borken in other ways too. */
2756 nfont |= FONT_WIDE + FONT_HIGH;
2759 if (attr & ATTR_NARROW)
2760 nfont |= FONT_NARROW;
2762 /* Special hack for the VT100 linedraw glyphs. */
2763 if ((attr & CSET_MASK) == 0x2300) {
2764 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2765 switch ((unsigned char) (text[0])) {
2767 text_adjust = -2 * font_height / 5;
2770 text_adjust = -1 * font_height / 5;
2773 text_adjust = font_height / 5;
2776 text_adjust = 2 * font_height / 5;
2779 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2782 text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
2783 attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
2784 if (attr & ATTR_UNDER) {
2785 attr &= ~ATTR_UNDER;
2786 force_manual_underline = 1;
2791 /* Anything left as an original character set is unprintable. */
2792 if (DIRECT_CHAR(attr)) {
2795 memset(text, 0xFD, len);
2799 if ((attr & CSET_MASK) == ATTR_OEMCP)
2802 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2803 nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
2804 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2805 nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
2806 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2808 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2809 nfont |= FONT_UNDERLINE;
2810 another_font(nfont);
2811 if (!fonts[nfont]) {
2812 if (nfont & FONT_UNDERLINE)
2813 force_manual_underline = 1;
2814 /* Don't do the same for manual bold, it could be bad news. */
2816 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2818 another_font(nfont);
2820 nfont = FONT_NORMAL;
2821 if (attr & ATTR_REVERSE) {
2826 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2828 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2832 SelectObject(hdc, fonts[nfont]);
2833 SetTextColor(hdc, fg);
2834 SetBkColor(hdc, bg);
2835 SetBkMode(hdc, OPAQUE);
2838 line_box.right = x + char_width * len;
2839 line_box.bottom = y + font_height;
2841 /* Only want the left half of double width lines */
2842 if (line_box.right > font_width*term->cols+offset_width)
2843 line_box.right = font_width*term->cols+offset_width;
2845 /* We're using a private area for direct to font. (512 chars.) */
2846 if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2847 /* Ho Hum, dbcs fonts are a PITA! */
2848 /* To display on W9x I have to convert to UCS */
2849 static wchar_t *uni_buf = 0;
2850 static int uni_len = 0;
2852 if (len > uni_len) {
2855 uni_buf = snewn(uni_len, wchar_t);
2858 for(nlen = mptr = 0; mptr<len; mptr++) {
2859 uni_buf[nlen] = 0xFFFD;
2860 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
2861 IpDx[nlen] += char_width;
2862 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2863 text+mptr, 2, uni_buf+nlen, 1);
2868 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2869 text+mptr, 1, uni_buf+nlen, 1);
2877 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2878 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2879 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2880 SetBkMode(hdc, TRANSPARENT);
2881 ExtTextOutW(hdc, x - 1,
2882 y - font_height * (lattr ==
2883 LATTR_BOT) + text_adjust,
2884 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2888 } else if (DIRECT_FONT(attr)) {
2890 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2891 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2892 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2893 SetBkMode(hdc, TRANSPARENT);
2895 /* GRR: This draws the character outside it's box and can leave
2896 * 'droppings' even with the clip box! I suppose I could loop it
2897 * one character at a time ... yuk.
2899 * Or ... I could do a test print with "W", and use +1 or -1 for this
2900 * shift depending on if the leftmost column is blank...
2902 ExtTextOut(hdc, x - 1,
2903 y - font_height * (lattr ==
2904 LATTR_BOT) + text_adjust,
2905 ETO_CLIPPED, &line_box, text, len, IpDx);
2908 /* And 'normal' unicode characters */
2909 static WCHAR *wbuf = NULL;
2910 static int wlen = 0;
2915 wbuf = snewn(wlen, WCHAR);
2917 for (i = 0; i < len; i++)
2918 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2921 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2922 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2924 /* And the shadow bold hack. */
2925 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2926 SetBkMode(hdc, TRANSPARENT);
2927 ExtTextOutW(hdc, x - 1,
2928 y - font_height * (lattr ==
2929 LATTR_BOT) + text_adjust,
2930 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2933 if (lattr != LATTR_TOP && (force_manual_underline ||
2934 (und_mode == UND_LINE
2935 && (attr & ATTR_UNDER)))) {
2938 if (lattr == LATTR_BOT)
2939 dec = dec * 2 - font_height;
2941 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2942 MoveToEx(hdc, x, y + dec, NULL);
2943 LineTo(hdc, x + len * char_width, y + dec);
2944 oldpen = SelectObject(hdc, oldpen);
2945 DeleteObject(oldpen);
2949 void do_cursor(Context ctx, int x, int y, char *text, int len,
2950 unsigned long attr, int lattr)
2956 int ctype = cfg.cursor_type;
2958 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
2959 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2960 do_text(ctx, x, y, text, len, attr, lattr);
2964 attr |= TATTR_RIGHTCURS;
2967 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2968 if (attr & ATTR_WIDE)
2975 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
2978 pts[0].x = pts[1].x = pts[4].x = x;
2979 pts[2].x = pts[3].x = x + char_width - 1;
2980 pts[0].y = pts[3].y = pts[4].y = y;
2981 pts[1].y = pts[2].y = y + font_height - 1;
2982 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2983 Polyline(hdc, pts, 5);
2984 oldpen = SelectObject(hdc, oldpen);
2985 DeleteObject(oldpen);
2986 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2987 int startx, starty, dx, dy, length, i;
2990 starty = y + descent;
2993 length = char_width;
2996 if (attr & TATTR_RIGHTCURS)
2997 xadjust = char_width - 1;
2998 startx = x + xadjust;
3002 length = font_height;
3004 if (attr & TATTR_ACTCURS) {
3007 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3008 MoveToEx(hdc, startx, starty, NULL);
3009 LineTo(hdc, startx + dx * length, starty + dy * length);
3010 oldpen = SelectObject(hdc, oldpen);
3011 DeleteObject(oldpen);
3013 for (i = 0; i < length; i++) {
3015 SetPixel(hdc, startx, starty, colours[23]);
3024 /* This function gets the actual width of a character in the normal font.
3026 int char_width(Context ctx, int uc) {
3030 /* If the font max is the same as the font ave width then this
3031 * function is a no-op.
3033 if (!font_dualwidth) return 1;
3035 switch (uc & CSET_MASK) {
3037 uc = ucsdata.unitab_line[uc & 0xFF];
3040 uc = ucsdata.unitab_xterm[uc & 0xFF];
3043 uc = ucsdata.unitab_scoacs[uc & 0xFF];
3046 if (DIRECT_FONT(uc)) {
3047 if (ucsdata.dbcs_screenfont) return 1;
3049 /* Speedup, I know of no font where ascii is the wrong width */
3050 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3053 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3054 SelectObject(hdc, fonts[FONT_NORMAL]);
3055 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3056 another_font(FONT_OEM);
3057 if (!fonts[FONT_OEM]) return 0;
3059 SelectObject(hdc, fonts[FONT_OEM]);
3063 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3064 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3067 /* Speedup, I know of no font where ascii is the wrong width */
3068 if (uc >= ' ' && uc <= '~') return 1;
3070 SelectObject(hdc, fonts[FONT_NORMAL]);
3071 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3072 /* Okay that one worked */ ;
3073 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3074 /* This should work on 9x too, but it's "less accurate" */ ;
3079 ibuf += font_width / 2 -1;
3086 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3087 * codes. Returns number of bytes used or zero to drop the message
3088 * or -1 to forward the message to windows.
3090 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3091 unsigned char *output)
3094 int scan, left_alt = 0, key_down, shift_state;
3096 unsigned char *p = output;
3097 static int alt_sum = 0;
3099 HKL kbd_layout = GetKeyboardLayout(0);
3101 static WORD keys[3];
3102 static int compose_char = 0;
3103 static WPARAM compose_key = 0;
3105 r = GetKeyboardState(keystate);
3107 memset(keystate, 0, sizeof(keystate));
3110 #define SHOW_TOASCII_RESULT
3111 { /* Tell us all about key events */
3112 static BYTE oldstate[256];
3113 static int first = 1;
3117 memcpy(oldstate, keystate, sizeof(oldstate));
3120 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3122 } else if ((HIWORD(lParam) & KF_UP)
3123 && scan == (HIWORD(lParam) & 0xFF)) {
3127 if (wParam >= VK_F1 && wParam <= VK_F20)
3128 debug(("K_F%d", wParam + 1 - VK_F1));
3141 debug(("VK_%02x", wParam));
3143 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3145 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3147 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3148 if (ch >= ' ' && ch <= '~')
3149 debug((", '%c'", ch));
3151 debug((", $%02x", ch));
3154 debug((", KB0=%02x", keys[0]));
3156 debug((", KB1=%02x", keys[1]));
3158 debug((", KB2=%02x", keys[2]));
3160 if ((keystate[VK_SHIFT] & 0x80) != 0)
3162 if ((keystate[VK_CONTROL] & 0x80) != 0)
3164 if ((HIWORD(lParam) & KF_EXTENDED))
3166 if ((HIWORD(lParam) & KF_UP))
3170 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3171 else if ((HIWORD(lParam) & KF_UP))
3172 oldstate[wParam & 0xFF] ^= 0x80;
3174 oldstate[wParam & 0xFF] ^= 0x81;
3176 for (ch = 0; ch < 256; ch++)
3177 if (oldstate[ch] != keystate[ch])
3178 debug((", M%02x=%02x", ch, keystate[ch]));
3180 memcpy(oldstate, keystate, sizeof(oldstate));
3184 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3185 keystate[VK_RMENU] = keystate[VK_MENU];
3189 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3190 if ((cfg.funky_type == 3 ||
3191 (cfg.funky_type <= 1 && term->app_keypad_keys &&
3193 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3195 wParam = VK_EXECUTE;
3197 /* UnToggle NUMLock */
3198 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3199 keystate[VK_NUMLOCK] ^= 1;
3202 /* And write back the 'adjusted' state */
3203 SetKeyboardState(keystate);
3206 /* Disable Auto repeat if required */
3207 if (term->repeat_off &&
3208 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3211 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3214 key_down = ((HIWORD(lParam) & KF_UP) == 0);
3216 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3217 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3218 if (cfg.ctrlaltkeys)
3219 keystate[VK_MENU] = 0;
3221 keystate[VK_RMENU] = 0x80;
3226 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3227 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3228 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3230 /* Note if AltGr was pressed and if it was used as a compose key */
3231 if (!compose_state) {
3232 compose_key = 0x100;
3233 if (cfg.compose_key) {
3234 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3235 compose_key = wParam;
3237 if (wParam == VK_APPS)
3238 compose_key = wParam;
3241 if (wParam == compose_key) {
3242 if (compose_state == 0
3243 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3245 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3249 } else if (compose_state == 1 && wParam != VK_CONTROL)
3252 if (compose_state > 1 && left_alt)
3255 /* Sanitize the number pad if not using a PC NumPad */
3256 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
3257 && cfg.funky_type != 2)
3258 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3259 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3263 nParam = VK_NUMPAD0;
3266 nParam = VK_NUMPAD1;
3269 nParam = VK_NUMPAD2;
3272 nParam = VK_NUMPAD3;
3275 nParam = VK_NUMPAD4;
3278 nParam = VK_NUMPAD5;
3281 nParam = VK_NUMPAD6;
3284 nParam = VK_NUMPAD7;
3287 nParam = VK_NUMPAD8;
3290 nParam = VK_NUMPAD9;
3293 nParam = VK_DECIMAL;
3297 if (keystate[VK_NUMLOCK] & 1)
3304 /* If a key is pressed and AltGr is not active */
3305 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3306 /* Okay, prepare for most alts then ... */
3310 /* Lets see if it's a pattern we know all about ... */
3311 if (wParam == VK_PRIOR && shift_state == 1) {
3312 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3315 if (wParam == VK_PRIOR && shift_state == 2) {
3316 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3319 if (wParam == VK_NEXT && shift_state == 1) {
3320 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3323 if (wParam == VK_NEXT && shift_state == 2) {
3324 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3327 if (wParam == VK_INSERT && shift_state == 1) {
3328 term_do_paste(term);
3331 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3334 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3335 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3338 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3339 (cfg.resize_action != RESIZE_DISABLED)) {
3340 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3344 /* Control-Numlock for app-keypad mode switch */
3345 if (wParam == VK_PAUSE && shift_state == 2) {
3346 term->app_keypad_keys ^= 1;
3350 /* Nethack keypad */
3351 if (cfg.nethack_keypad && !left_alt) {
3354 *p++ = shift_state ? 'B' : 'b';
3357 *p++ = shift_state ? 'J' : 'j';
3360 *p++ = shift_state ? 'N' : 'n';
3363 *p++ = shift_state ? 'H' : 'h';
3366 *p++ = shift_state ? '.' : '.';
3369 *p++ = shift_state ? 'L' : 'l';
3372 *p++ = shift_state ? 'Y' : 'y';
3375 *p++ = shift_state ? 'K' : 'k';
3378 *p++ = shift_state ? 'U' : 'u';
3383 /* Application Keypad */
3387 if (cfg.funky_type == 3 ||
3388 (cfg.funky_type <= 1 &&
3389 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3403 if (term->app_keypad_keys && !cfg.no_applic_k)
3440 if (cfg.funky_type == 2) {
3445 } else if (shift_state)
3452 if (cfg.funky_type == 2)
3456 if (cfg.funky_type == 2)
3460 if (cfg.funky_type == 2)
3465 if (HIWORD(lParam) & KF_EXTENDED)
3470 if (term->vt52_mode) {
3471 if (xkey >= 'P' && xkey <= 'S')
3472 p += sprintf((char *) p, "\x1B%c", xkey);
3474 p += sprintf((char *) p, "\x1B?%c", xkey);
3476 p += sprintf((char *) p, "\x1BO%c", xkey);
3481 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3482 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3486 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3487 /* We do the opposite of what is configured */
3488 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3492 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3498 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3502 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3506 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3511 if (wParam == VK_PAUSE) { /* Break/Pause */
3516 /* Control-2 to Control-8 are special */
3517 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3518 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3521 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
3525 if (shift_state == 2 && wParam == 0xDF) {
3529 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
3536 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3537 * for integer decimal nn.)
3539 * We also deal with the weird ones here. Linux VCs replace F1
3540 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3541 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3547 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3550 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3553 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3556 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3559 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3562 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3565 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3568 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3571 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3574 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3607 if ((shift_state&2) == 0) switch (wParam) {
3627 /* Reorder edit keys to physical order */
3628 if (cfg.funky_type == 3 && code <= 6)
3629 code = "\0\2\1\4\5\3\6"[code];
3631 if (term->vt52_mode && code > 0 && code <= 6) {
3632 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3636 if (cfg.funky_type == 5 && /* SCO function keys */
3637 code >= 11 && code <= 34) {
3638 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3641 case VK_F1: index = 0; break;
3642 case VK_F2: index = 1; break;
3643 case VK_F3: index = 2; break;
3644 case VK_F4: index = 3; break;
3645 case VK_F5: index = 4; break;
3646 case VK_F6: index = 5; break;
3647 case VK_F7: index = 6; break;
3648 case VK_F8: index = 7; break;
3649 case VK_F9: index = 8; break;
3650 case VK_F10: index = 9; break;
3651 case VK_F11: index = 10; break;
3652 case VK_F12: index = 11; break;
3654 if (keystate[VK_SHIFT] & 0x80) index += 12;
3655 if (keystate[VK_CONTROL] & 0x80) index += 24;
3656 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3659 if (cfg.funky_type == 5 && /* SCO small keypad */
3660 code >= 1 && code <= 6) {
3661 char codes[] = "HL.FIG";
3665 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3669 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3675 if (term->vt52_mode)
3676 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3679 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3682 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3683 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3686 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3687 if (term->vt52_mode)
3688 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3690 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3693 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3694 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3698 p += sprintf((char *) p, "\x1B[%d~", code);
3703 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3704 * some reason seems to send VK_CLEAR to Windows...).
3726 if (term->vt52_mode)
3727 p += sprintf((char *) p, "\x1B%c", xkey);
3729 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3732 * RDB: VT100 & VT102 manuals both state the
3733 * app cursor keys only work if the app keypad
3736 * SGT: That may well be true, but xterm
3737 * disagrees and so does at least one
3738 * application, so I've #if'ed this out and the
3739 * behaviour is back to PuTTY's original: app
3740 * cursor and app keypad are independently
3741 * switchable modes. If anyone complains about
3742 * _this_ I'll have to put in a configurable
3745 if (!term->app_keypad_keys)
3748 /* Useful mapping of Ctrl-arrows */
3749 if (shift_state == 2)
3753 p += sprintf((char *) p, "\x1BO%c", xkey);
3755 p += sprintf((char *) p, "\x1B[%c", xkey);
3762 * Finally, deal with Return ourselves. (Win95 seems to
3763 * foul it up when Alt is pressed, for some reason.)
3765 if (wParam == VK_RETURN) { /* Return */
3771 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3772 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3777 /* Okay we've done everything interesting; let windows deal with
3778 * the boring stuff */
3782 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3783 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3785 keystate[VK_CAPITAL] = 0;
3788 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3789 #ifdef SHOW_TOASCII_RESULT
3790 if (r == 1 && !key_down) {
3792 if (in_utf(term) || ucsdata.dbcs_screenfont)
3793 debug((", (U+%04x)", alt_sum));
3795 debug((", LCH(%d)", alt_sum));
3797 debug((", ACH(%d)", keys[0]));
3802 for (r1 = 0; r1 < r; r1++) {
3803 debug(("%s%d", r1 ? "," : "", keys[r1]));
3812 * Interrupt an ongoing paste. I'm not sure this is
3813 * sensible, but for the moment it's preferable to
3814 * having to faff about buffering things.
3819 for (i = 0; i < r; i++) {
3820 unsigned char ch = (unsigned char) keys[i];
3822 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3827 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3831 if ((nc = check_compose(compose_char, ch)) == -1) {
3832 MessageBeep(MB_ICONHAND);
3836 term_seen_key_event(term);
3837 luni_send(ldisc, &keybuf, 1, 1);
3845 if (in_utf(term) || ucsdata.dbcs_screenfont) {
3847 term_seen_key_event(term);
3848 luni_send(ldisc, &keybuf, 1, 1);
3850 ch = (char) alt_sum;
3852 * We need not bother about stdin
3853 * backlogs here, because in GUI PuTTY
3854 * we can't do anything about it
3855 * anyway; there's no means of asking
3856 * Windows to hold off on KEYDOWN
3857 * messages. We _have_ to buffer
3858 * everything we're sent.
3860 term_seen_key_event(term);
3861 ldisc_send(ldisc, &ch, 1, 1);
3865 term_seen_key_event(term);
3866 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
3868 if(capsOn && ch < 0x80) {
3871 cbuf[1] = xlat_uskbd2cyrllic(ch);
3872 term_seen_key_event(term);
3873 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
3878 term_seen_key_event(term);
3879 lpage_send(ldisc, kbd_codepage,
3880 cbuf+!left_alt, 1+!!left_alt, 1);
3886 /* This is so the ALT-Numpad and dead keys work correctly. */
3891 /* If we're definitly not building up an ALT-54321 then clear it */
3894 /* If we will be using alt_sum fix the 256s */
3895 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
3900 * ALT alone may or may not want to bring up the System menu.
3901 * If it's not meant to, we return 0 on presses or releases of
3902 * ALT, to show that we've swallowed the keystroke. Otherwise
3903 * we return -1, which means Windows will give the keystroke
3904 * its default handling (i.e. bring up the System menu).
3906 if (wParam == VK_MENU && !cfg.alt_only)
3912 void request_paste(void *frontend)
3915 * In Windows, pasting is synchronous: we can read the
3916 * clipboard with no difficulty, so request_paste() can just go
3919 term_do_paste(term);
3922 void set_title(void *frontend, char *title)
3925 window_name = snewn(1 + strlen(title), char);
3926 strcpy(window_name, title);
3927 if (cfg.win_name_always || !IsIconic(hwnd))
3928 SetWindowText(hwnd, title);
3931 void set_icon(void *frontend, char *title)
3934 icon_name = snewn(1 + strlen(title), char);
3935 strcpy(icon_name, title);
3936 if (!cfg.win_name_always && IsIconic(hwnd))
3937 SetWindowText(hwnd, title);
3940 void set_sbar(void *frontend, int total, int start, int page)
3944 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3947 si.cbSize = sizeof(si);
3948 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3950 si.nMax = total - 1;
3954 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3957 Context get_ctx(void *frontend)
3963 SelectPalette(hdc, pal, FALSE);
3969 void free_ctx(Context ctx)
3971 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3972 ReleaseDC(hwnd, ctx);
3975 static void real_palette_set(int n, int r, int g, int b)
3978 logpal->palPalEntry[n].peRed = r;
3979 logpal->palPalEntry[n].peGreen = g;
3980 logpal->palPalEntry[n].peBlue = b;
3981 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3982 colours[n] = PALETTERGB(r, g, b);
3983 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3985 colours[n] = RGB(r, g, b);
3988 void palette_set(void *frontend, int n, int r, int g, int b)
3990 static const int first[21] = {
3991 0, 2, 4, 6, 8, 10, 12, 14,
3992 1, 3, 5, 7, 9, 11, 13, 15,
3995 real_palette_set(first[n], r, g, b);
3997 real_palette_set(first[n] + 1, r, g, b);
3999 HDC hdc = get_ctx(frontend);
4000 UnrealizeObject(pal);
4001 RealizePalette(hdc);
4006 void palette_reset(void *frontend)
4010 for (i = 0; i < NCOLOURS; i++) {
4012 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4013 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4014 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4015 logpal->palPalEntry[i].peFlags = 0;
4016 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4017 defpal[i].rgbtGreen,
4018 defpal[i].rgbtBlue);
4020 colours[i] = RGB(defpal[i].rgbtRed,
4021 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
4026 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
4027 hdc = get_ctx(frontend);
4028 RealizePalette(hdc);
4033 void write_aclip(void *frontend, char *data, int len, int must_deselect)
4038 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
4041 lock = GlobalLock(clipdata);
4044 memcpy(lock, data, len);
4045 ((unsigned char *) lock)[len] = 0;
4046 GlobalUnlock(clipdata);
4049 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4051 if (OpenClipboard(hwnd)) {
4053 SetClipboardData(CF_TEXT, clipdata);
4056 GlobalFree(clipdata);
4059 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4063 * Note: unlike write_aclip() this will not append a nul.
4065 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4067 HGLOBAL clipdata, clipdata2, clipdata3;
4069 void *lock, *lock2, *lock3;
4071 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4073 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4074 len * sizeof(wchar_t));
4075 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4077 if (!clipdata || !clipdata2) {
4079 GlobalFree(clipdata);
4081 GlobalFree(clipdata2);
4084 if (!(lock = GlobalLock(clipdata)))
4086 if (!(lock2 = GlobalLock(clipdata2)))
4089 memcpy(lock, data, len * sizeof(wchar_t));
4090 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4092 if (cfg.rtf_paste) {
4093 wchar_t unitab[256];
4095 unsigned char *tdata = (unsigned char *)lock2;
4096 wchar_t *udata = (wchar_t *)lock;
4097 int rtflen = 0, uindex = 0, tindex = 0;
4099 int multilen, blen, alen, totallen, i;
4100 char before[16], after[4];
4102 get_unitab(CP_ACP, unitab, 0);
4104 rtfsize = 100 + strlen(cfg.font.name);
4105 rtf = snewn(rtfsize, char);
4106 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4107 GetACP(), cfg.font.name);
4108 rtflen = strlen(rtf);
4111 * We want to construct a piece of RTF that specifies the
4112 * same Unicode text. To do this we will read back in
4113 * parallel from the Unicode data in `udata' and the
4114 * non-Unicode data in `tdata'. For each character in
4115 * `tdata' which becomes the right thing in `udata' when
4116 * looked up in `unitab', we just copy straight over from
4117 * tdata. For each one that doesn't, we must WCToMB it
4118 * individually and produce a \u escape sequence.
4120 * It would probably be more robust to just bite the bullet
4121 * and WCToMB each individual Unicode character one by one,
4122 * then MBToWC each one back to see if it was an accurate
4123 * translation; but that strikes me as a horrifying number
4124 * of Windows API calls so I want to see if this faster way
4125 * will work. If it screws up badly we can always revert to
4126 * the simple and slow way.
4128 while (tindex < len2 && uindex < len &&
4129 tdata[tindex] && udata[uindex]) {
4130 if (tindex + 1 < len2 &&
4131 tdata[tindex] == '\r' &&
4132 tdata[tindex+1] == '\n') {
4136 if (unitab[tdata[tindex]] == udata[uindex]) {
4142 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4143 NULL, 0, NULL, NULL);
4144 if (multilen != 1) {
4145 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4147 alen = 1; strcpy(after, "}");
4149 blen = sprintf(before, "\\u%d", udata[uindex]);
4150 alen = 0; after[0] = '\0';
4153 assert(tindex + multilen <= len2);
4154 totallen = blen + alen;
4155 for (i = 0; i < multilen; i++) {
4156 if (tdata[tindex+i] == '\\' ||
4157 tdata[tindex+i] == '{' ||
4158 tdata[tindex+i] == '}')
4160 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4161 totallen += 6; /* \par\r\n */
4162 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4168 if (rtfsize < rtflen + totallen + 3) {
4169 rtfsize = rtflen + totallen + 512;
4170 rtf = sresize(rtf, rtfsize, char);
4173 strcpy(rtf + rtflen, before); rtflen += blen;
4174 for (i = 0; i < multilen; i++) {
4175 if (tdata[tindex+i] == '\\' ||
4176 tdata[tindex+i] == '{' ||
4177 tdata[tindex+i] == '}') {
4178 rtf[rtflen++] = '\\';
4179 rtf[rtflen++] = tdata[tindex+i];
4180 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4181 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4182 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4183 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4185 rtf[rtflen++] = tdata[tindex+i];
4188 strcpy(rtf + rtflen, after); rtflen += alen;
4194 strcpy(rtf + rtflen, "}");
4197 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4198 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4200 GlobalUnlock(clipdata3);
4206 GlobalUnlock(clipdata);
4207 GlobalUnlock(clipdata2);
4210 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4212 if (OpenClipboard(hwnd)) {
4214 SetClipboardData(CF_UNICODETEXT, clipdata);
4215 SetClipboardData(CF_TEXT, clipdata2);
4217 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4220 GlobalFree(clipdata);
4221 GlobalFree(clipdata2);
4225 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4228 void get_clip(void *frontend, wchar_t ** p, int *len)
4230 static HGLOBAL clipdata = NULL;
4231 static wchar_t *converted = 0;
4240 GlobalUnlock(clipdata);
4243 } else if (OpenClipboard(NULL)) {
4244 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4246 *p = GlobalLock(clipdata);
4248 for (p2 = *p; *p2; p2++);
4252 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4256 s = GlobalLock(clipdata);
4257 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4258 *p = converted = snewn(i, wchar_t);
4259 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4272 * Move `lines' lines from position `from' to position `to' in the
4275 void optimised_move(void *frontend, int to, int from, int lines)
4280 min = (to < from ? to : from);
4281 max = to + from - min;
4283 r.left = offset_width;
4284 r.right = offset_width + term->cols * font_width;
4285 r.top = offset_height + min * font_height;
4286 r.bottom = offset_height + (max + lines) * font_height;
4287 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4292 * Print a message box and perform a fatal exit.
4294 void fatalbox(char *fmt, ...)
4300 vsprintf(stuff, fmt, ap);
4302 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4307 * Print a modal (Really Bad) message box and perform a fatal exit.
4309 void modalfatalbox(char *fmt, ...)
4315 vsprintf(stuff, fmt, ap);
4317 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4318 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4323 * Manage window caption / taskbar flashing, if enabled.
4324 * 0 = stop, 1 = maintain, 2 = start
4326 static void flash_window(int mode)
4328 static long last_flash = 0;
4329 static int flashing = 0;
4330 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4333 FlashWindow(hwnd, FALSE);
4337 } else if (mode == 2) {
4340 last_flash = GetTickCount();
4342 FlashWindow(hwnd, TRUE);
4345 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4348 long now = GetTickCount();
4349 long fdiff = now - last_flash;
4350 if (fdiff < 0 || fdiff > 450) {
4352 FlashWindow(hwnd, TRUE); /* toggle */
4361 void beep(void *frontend, int mode)
4363 if (mode == BELL_DEFAULT) {
4365 * For MessageBeep style bells, we want to be careful of
4366 * timing, because they don't have the nice property of
4367 * PlaySound bells that each one cancels the previous
4368 * active one. So we limit the rate to one per 50ms or so.
4370 static long lastbeep = 0;
4373 beepdiff = GetTickCount() - lastbeep;
4374 if (beepdiff >= 0 && beepdiff < 50)
4378 * The above MessageBeep call takes time, so we record the
4379 * time _after_ it finishes rather than before it starts.
4381 lastbeep = GetTickCount();
4382 } else if (mode == BELL_WAVEFILE) {
4383 if (!PlaySound(cfg.bell_wavefile.path, NULL,
4384 SND_ASYNC | SND_FILENAME)) {
4385 char buf[sizeof(cfg.bell_wavefile) + 80];
4386 sprintf(buf, "Unable to play sound file\n%s\n"
4387 "Using default sound instead", cfg.bell_wavefile);
4388 MessageBox(hwnd, buf, "PuTTY Sound Error",
4389 MB_OK | MB_ICONEXCLAMATION);
4390 cfg.beep = BELL_DEFAULT;
4393 /* Otherwise, either visual bell or disabled; do nothing here */
4394 if (!term->has_focus) {
4395 flash_window(2); /* start */
4400 * Minimise or restore the window in response to a server-side
4403 void set_iconic(void *frontend, int iconic)
4405 if (IsIconic(hwnd)) {
4407 ShowWindow(hwnd, SW_RESTORE);
4410 ShowWindow(hwnd, SW_MINIMIZE);
4415 * Move the window in response to a server-side request.
4417 void move_window(void *frontend, int x, int y)
4419 if (cfg.resize_action == RESIZE_DISABLED ||
4420 cfg.resize_action == RESIZE_FONT ||
4424 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4428 * Move the window to the top or bottom of the z-order in response
4429 * to a server-side request.
4431 void set_zorder(void *frontend, int top)
4433 if (cfg.alwaysontop)
4434 return; /* ignore */
4435 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4436 SWP_NOMOVE | SWP_NOSIZE);
4440 * Refresh the window in response to a server-side request.
4442 void refresh_window(void *frontend)
4444 InvalidateRect(hwnd, NULL, TRUE);
4448 * Maximise or restore the window in response to a server-side
4451 void set_zoomed(void *frontend, int zoomed)
4453 if (IsZoomed(hwnd)) {
4455 ShowWindow(hwnd, SW_RESTORE);
4458 ShowWindow(hwnd, SW_MAXIMIZE);
4463 * Report whether the window is iconic, for terminal reports.
4465 int is_iconic(void *frontend)
4467 return IsIconic(hwnd);
4471 * Report the window's position, for terminal reports.
4473 void get_window_pos(void *frontend, int *x, int *y)
4476 GetWindowRect(hwnd, &r);
4482 * Report the window's pixel size, for terminal reports.
4484 void get_window_pixels(void *frontend, int *x, int *y)
4487 GetWindowRect(hwnd, &r);
4488 *x = r.right - r.left;
4489 *y = r.bottom - r.top;
4493 * Return the window or icon title.
4495 char *get_window_title(void *frontend, int icon)
4497 return icon ? icon_name : window_name;
4501 * See if we're in full-screen mode.
4503 int is_full_screen()
4505 if (!IsZoomed(hwnd))
4507 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4512 /* Get the rect/size of a full screen window using the nearest available
4513 * monitor in multimon systems; default to something sensible if only
4514 * one monitor is present. */
4515 static int get_fullscreen_rect(RECT * ss)
4517 #ifdef MONITOR_DEFAULTTONEAREST
4520 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4521 mi.cbSize = sizeof(mi);
4522 GetMonitorInfo(mon, &mi);
4524 /* structure copy */
4528 /* could also use code like this:
4529 ss->left = ss->top = 0;
4530 ss->right = GetSystemMetrics(SM_CXSCREEN);
4531 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4533 return GetClientRect(GetDesktopWindow(), ss);
4539 * Go full-screen. This should only be called when we are already
4542 void make_full_screen()
4547 assert(IsZoomed(hwnd));
4549 if (is_full_screen())
4552 /* Remove the window furniture. */
4553 style = GetWindowLong(hwnd, GWL_STYLE);
4554 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4555 if (cfg.scrollbar_in_fullscreen)
4556 style |= WS_VSCROLL;
4558 style &= ~WS_VSCROLL;
4559 SetWindowLong(hwnd, GWL_STYLE, style);
4561 /* Resize ourselves to exactly cover the nearest monitor. */
4562 get_fullscreen_rect(&ss);
4563 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4568 /* Tick the menu item in the System menu. */
4569 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4574 * Clear the full-screen attributes.
4576 void clear_full_screen()
4578 DWORD oldstyle, style;
4580 /* Reinstate the window furniture. */
4581 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4582 style |= WS_CAPTION | WS_BORDER;
4583 if (cfg.resize_action == RESIZE_DISABLED)
4584 style &= ~WS_THICKFRAME;
4586 style |= WS_THICKFRAME;
4588 style |= WS_VSCROLL;
4590 style &= ~WS_VSCROLL;
4591 if (style != oldstyle) {
4592 SetWindowLong(hwnd, GWL_STYLE, style);
4593 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4594 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4598 /* Untick the menu item in the System menu. */
4599 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4604 * Toggle full-screen mode.
4606 void flip_full_screen()
4608 if (is_full_screen()) {
4609 ShowWindow(hwnd, SW_RESTORE);
4610 } else if (IsZoomed(hwnd)) {
4613 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4614 ShowWindow(hwnd, SW_MAXIMIZE);
4618 void frontend_keypress(void *handle)
4621 * Keypress termination in non-Close-On-Exit mode is not
4622 * currently supported in PuTTY proper, because the window
4623 * always has a perfectly good Close button anyway. So we do