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 = smalloc(size);
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 /* Force the cursor blink on */
794 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
798 /* Okay there is now nothing to do so we make sure the screen is
799 * completely up to date then tell windows to call us in a little
803 KillTimer(hwnd, timer_id);
807 if (GetCapture() != hwnd ||
809 !(cfg.mouse_override && is_shift_pressed())))
814 flash_window(1); /* maintain */
816 /* The messages seem unreliable; especially if we're being tricky */
817 term->has_focus = (GetForegroundWindow() == hwnd);
820 /* Hmm, term_update didn't want to do an update too soon ... */
821 timer_id = SetTimer(hwnd, 1, 50, NULL);
822 else if (!term->has_focus)
823 timer_id = SetTimer(hwnd, 1, 500, NULL);
825 timer_id = SetTimer(hwnd, 1, 100, NULL);
828 /* There's no point rescanning everything in the message queue
829 * so we do an apparently unnecessary wait here
832 if (GetMessage(&msg, NULL, 0, 0) != 1)
837 cleanup_exit(msg.wParam); /* this doesn't return... */
838 return msg.wParam; /* ... but optimiser doesn't know */
844 void cleanup_exit(int code)
857 if (cfg.protocol == PROT_SSH) {
868 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
870 char *do_select(SOCKET skt, int startup)
875 events = (FD_CONNECT | FD_READ | FD_WRITE |
876 FD_OOB | FD_CLOSE | FD_ACCEPT);
881 return "do_select(): internal error (hwnd==NULL)";
882 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
883 switch (WSAGetLastError()) {
885 return "Network is down";
887 return "WSAAsyncSelect(): unknown error";
894 * set or clear the "raw mouse message" mode
896 void set_raw_mouse_mode(void *frontend, int activate)
898 activate = activate && !cfg.no_mouse_rep;
899 send_raw_mouse = activate;
900 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
904 * Print a message box and close the connection.
906 void connection_fatal(void *frontend, char *fmt, ...)
912 vsprintf(stuff, fmt, ap);
914 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
915 if (cfg.close_on_exit == FORCE_ON)
918 session_closed = TRUE;
919 set_icon(NULL, "PuTTY (inactive)");
920 set_title(NULL, "PuTTY (inactive)");
925 * Report an error at the command-line parsing stage.
927 void cmdline_error(char *fmt, ...)
933 vsprintf(stuff, fmt, ap);
935 MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
940 * Actually do the job requested by a WM_NETEVENT
942 static void enact_pending_netevent(void)
944 static int reentering = 0;
945 extern int select_result(WPARAM, LPARAM);
949 return; /* don't unpend the pending */
951 pending_netevent = FALSE;
954 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
957 if (ret == 0 && !session_closed) {
958 /* Abnormal exits will already have set session_closed and taken
959 * appropriate action. */
960 if (cfg.close_on_exit == FORCE_ON ||
961 cfg.close_on_exit == AUTO) PostQuitMessage(0);
963 session_closed = TRUE;
964 set_icon(NULL, "PuTTY (inactive)");
965 set_title(NULL, "PuTTY (inactive)");
966 MessageBox(hwnd, "Connection closed by remote host",
967 "PuTTY", MB_OK | MB_ICONINFORMATION);
973 * Copy the colour palette from the configuration data into defpal.
974 * This is non-trivial because the colour indices are different.
976 static void cfgtopalette(void)
979 static const int ww[] = {
980 6, 7, 8, 9, 10, 11, 12, 13,
981 14, 15, 16, 17, 18, 19, 20, 21,
982 0, 1, 2, 3, 4, 4, 5, 5
985 for (i = 0; i < 24; i++) {
987 defpal[i].rgbtRed = cfg.colours[w][0];
988 defpal[i].rgbtGreen = cfg.colours[w][1];
989 defpal[i].rgbtBlue = cfg.colours[w][2];
994 * Set up the colour palette.
996 static void init_palette(void)
999 HDC hdc = GetDC(hwnd);
1001 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
1002 logpal = smalloc(sizeof(*logpal)
1003 - sizeof(logpal->palPalEntry)
1004 + NCOLOURS * sizeof(PALETTEENTRY));
1005 logpal->palVersion = 0x300;
1006 logpal->palNumEntries = NCOLOURS;
1007 for (i = 0; i < NCOLOURS; i++) {
1008 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1009 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1010 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1011 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1013 pal = CreatePalette(logpal);
1015 SelectPalette(hdc, pal, FALSE);
1016 RealizePalette(hdc);
1017 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1020 ReleaseDC(hwnd, hdc);
1023 for (i = 0; i < NCOLOURS; i++)
1024 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1025 defpal[i].rgbtGreen,
1026 defpal[i].rgbtBlue);
1028 for (i = 0; i < NCOLOURS; i++)
1029 colours[i] = RGB(defpal[i].rgbtRed,
1030 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1034 * Initialise all the fonts we will need initially. There may be as many as
1035 * three or as few as one. The other (poentially) twentyone fonts are done
1036 * if/when they are needed.
1040 * - check the font width and height, correcting our guesses if
1043 * - verify that the bold font is the same width as the ordinary
1044 * one, and engage shadow bolding if not.
1046 * - verify that the underlined font is the same width as the
1047 * ordinary one (manual underlining by means of line drawing can
1048 * be done in a pinch).
1050 static void init_fonts(int pick_width, int pick_height)
1057 int fw_dontcare, fw_bold;
1059 for (i = 0; i < FONT_MAXNO; i++)
1062 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1063 und_mode = UND_FONT;
1065 if (cfg.font.isbold) {
1066 fw_dontcare = FW_BOLD;
1069 fw_dontcare = FW_DONTCARE;
1076 font_height = pick_height;
1078 font_height = cfg.font.height;
1079 if (font_height > 0) {
1081 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1084 font_width = pick_width;
1086 #define f(i,c,w,u) \
1087 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1088 c, OUT_DEFAULT_PRECIS, \
1089 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1090 FIXED_PITCH | FF_DONTCARE, cfg.font.name)
1092 f(FONT_NORMAL, cfg.font.charset, fw_dontcare, FALSE);
1094 lfont.lfHeight = font_height;
1095 lfont.lfWidth = font_width;
1096 lfont.lfEscapement = 0;
1097 lfont.lfOrientation = 0;
1098 lfont.lfWeight = fw_dontcare;
1099 lfont.lfItalic = FALSE;
1100 lfont.lfUnderline = FALSE;
1101 lfont.lfStrikeOut = FALSE;
1102 lfont.lfCharSet = cfg.font.charset;
1103 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1104 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1105 lfont.lfQuality = DEFAULT_QUALITY;
1106 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1107 strncpy(lfont.lfFaceName, cfg.font.name, LF_FACESIZE);
1109 SelectObject(hdc, fonts[FONT_NORMAL]);
1110 GetTextMetrics(hdc, &tm);
1112 if (pick_width == 0 || pick_height == 0) {
1113 font_height = tm.tmHeight;
1114 font_width = tm.tmAveCharWidth;
1116 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1118 #ifdef RDB_DEBUG_PATCH
1119 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1120 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1125 DWORD cset = tm.tmCharSet;
1126 memset(&info, 0xFF, sizeof(info));
1128 /* !!! Yes the next line is right */
1129 if (cset == OEM_CHARSET)
1130 ucsdata.font_codepage = GetOEMCP();
1132 if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
1133 ucsdata.font_codepage = info.ciACP;
1135 ucsdata.font_codepage = -1;
1137 GetCPInfo(ucsdata.font_codepage, &cpinfo);
1138 ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1141 f(FONT_UNDERLINE, cfg.font.charset, fw_dontcare, TRUE);
1144 * Some fonts, e.g. 9-pt Courier, draw their underlines
1145 * outside their character cell. We successfully prevent
1146 * screen corruption by clipping the text output, but then
1147 * we lose the underline completely. Here we try to work
1148 * out whether this is such a font, and if it is, we set a
1149 * flag that causes underlines to be drawn by hand.
1151 * Having tried other more sophisticated approaches (such
1152 * as examining the TEXTMETRIC structure or requesting the
1153 * height of a string), I think we'll do this the brute
1154 * force way: we create a small bitmap, draw an underlined
1155 * space on it, and test to see whether any pixels are
1156 * foreground-coloured. (Since we expect the underline to
1157 * go all the way across the character cell, we only search
1158 * down a single column of the bitmap, half way across.)
1162 HBITMAP und_bm, und_oldbm;
1166 und_dc = CreateCompatibleDC(hdc);
1167 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1168 und_oldbm = SelectObject(und_dc, und_bm);
1169 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1170 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1171 SetTextColor(und_dc, RGB(255, 255, 255));
1172 SetBkColor(und_dc, RGB(0, 0, 0));
1173 SetBkMode(und_dc, OPAQUE);
1174 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1176 for (i = 0; i < font_height; i++) {
1177 c = GetPixel(und_dc, font_width / 2, i);
1178 if (c != RGB(0, 0, 0))
1181 SelectObject(und_dc, und_oldbm);
1182 DeleteObject(und_bm);
1185 und_mode = UND_LINE;
1186 DeleteObject(fonts[FONT_UNDERLINE]);
1187 fonts[FONT_UNDERLINE] = 0;
1191 if (bold_mode == BOLD_FONT) {
1192 f(FONT_BOLD, cfg.font.charset, fw_bold, FALSE);
1196 descent = tm.tmAscent + 1;
1197 if (descent >= font_height)
1198 descent = font_height - 1;
1200 for (i = 0; i < 3; i++) {
1202 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1203 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1210 ReleaseDC(hwnd, hdc);
1212 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1213 und_mode = UND_LINE;
1214 DeleteObject(fonts[FONT_UNDERLINE]);
1215 fonts[FONT_UNDERLINE] = 0;
1218 if (bold_mode == BOLD_FONT &&
1219 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1220 bold_mode = BOLD_SHADOW;
1221 DeleteObject(fonts[FONT_BOLD]);
1222 fonts[FONT_BOLD] = 0;
1224 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1226 init_ucs(&cfg, &ucsdata);
1229 static void another_font(int fontno)
1232 int fw_dontcare, fw_bold;
1236 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1239 basefont = (fontno & ~(FONT_BOLDUND));
1240 if (basefont != fontno && !fontflag[basefont])
1241 another_font(basefont);
1243 if (cfg.font.isbold) {
1244 fw_dontcare = FW_BOLD;
1247 fw_dontcare = FW_DONTCARE;
1251 c = cfg.font.charset;
1257 if (fontno & FONT_WIDE)
1259 if (fontno & FONT_NARROW)
1261 if (fontno & FONT_OEM)
1263 if (fontno & FONT_BOLD)
1265 if (fontno & FONT_UNDERLINE)
1269 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1270 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1271 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1272 FIXED_PITCH | FF_DONTCARE, s);
1274 fontflag[fontno] = 1;
1277 static void deinit_fonts(void)
1280 for (i = 0; i < FONT_MAXNO; i++) {
1282 DeleteObject(fonts[i]);
1288 void request_resize(void *frontend, int w, int h)
1292 /* If the window is maximized supress resizing attempts */
1293 if (IsZoomed(hwnd)) {
1294 if (cfg.resize_action == RESIZE_TERM)
1298 if (cfg.resize_action == RESIZE_DISABLED) return;
1299 if (h == term->rows && w == term->cols) return;
1301 /* Sanity checks ... */
1303 static int first_time = 1;
1306 switch (first_time) {
1308 /* Get the size of the screen */
1309 if (get_fullscreen_rect(&ss))
1310 /* first_time = 0 */ ;
1316 /* Make sure the values are sane */
1317 width = (ss.right - ss.left - extra_width) / 4;
1318 height = (ss.bottom - ss.top - extra_height) / 6;
1320 if (w > width || h > height)
1329 term_size(term, h, w, cfg.savelines);
1331 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1332 width = extra_width + font_width * w;
1333 height = extra_height + font_height * h;
1335 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1336 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1337 SWP_NOMOVE | SWP_NOZORDER);
1341 InvalidateRect(hwnd, NULL, TRUE);
1344 static void reset_window(int reinit) {
1346 * This function decides how to resize or redraw when the
1347 * user changes something.
1349 * This function doesn't like to change the terminal size but if the
1350 * font size is locked that may be it's only soluion.
1352 int win_width, win_height;
1355 #ifdef RDB_DEBUG_PATCH
1356 debug((27, "reset_window()"));
1359 /* Current window sizes ... */
1360 GetWindowRect(hwnd, &wr);
1361 GetClientRect(hwnd, &cr);
1363 win_width = cr.right - cr.left;
1364 win_height = cr.bottom - cr.top;
1366 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1368 /* Are we being forced to reload the fonts ? */
1370 #ifdef RDB_DEBUG_PATCH
1371 debug((27, "reset_window() -- Forced deinit"));
1377 /* Oh, looks like we're minimised */
1378 if (win_width == 0 || win_height == 0)
1381 /* Is the window out of position ? */
1383 (offset_width != (win_width-font_width*term->cols)/2 ||
1384 offset_height != (win_height-font_height*term->rows)/2) ){
1385 offset_width = (win_width-font_width*term->cols)/2;
1386 offset_height = (win_height-font_height*term->rows)/2;
1387 InvalidateRect(hwnd, NULL, TRUE);
1388 #ifdef RDB_DEBUG_PATCH
1389 debug((27, "reset_window() -> Reposition terminal"));
1393 if (IsZoomed(hwnd)) {
1394 /* We're fullscreen, this means we must not change the size of
1395 * the window so it's the font size or the terminal itself.
1398 extra_width = wr.right - wr.left - cr.right + cr.left;
1399 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1401 if (cfg.resize_action != RESIZE_TERM) {
1402 if ( font_width != win_width/term->cols ||
1403 font_height != win_height/term->rows) {
1405 init_fonts(win_width/term->cols, win_height/term->rows);
1406 offset_width = (win_width-font_width*term->cols)/2;
1407 offset_height = (win_height-font_height*term->rows)/2;
1408 InvalidateRect(hwnd, NULL, TRUE);
1409 #ifdef RDB_DEBUG_PATCH
1410 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1411 font_width, font_height));
1415 if ( font_width != win_width/term->cols ||
1416 font_height != win_height/term->rows) {
1417 /* Our only choice at this point is to change the
1418 * size of the terminal; Oh well.
1420 term_size(term, win_height/font_height, win_width/font_width,
1422 offset_width = (win_width-font_width*term->cols)/2;
1423 offset_height = (win_height-font_height*term->rows)/2;
1424 InvalidateRect(hwnd, NULL, TRUE);
1425 #ifdef RDB_DEBUG_PATCH
1426 debug((27, "reset_window() -> Zoomed term_size"));
1433 /* Hmm, a force re-init means we should ignore the current window
1434 * so we resize to the default font size.
1437 #ifdef RDB_DEBUG_PATCH
1438 debug((27, "reset_window() -> Forced re-init"));
1441 offset_width = offset_height = cfg.window_border;
1442 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1443 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1445 if (win_width != font_width*term->cols + offset_width*2 ||
1446 win_height != font_height*term->rows + offset_height*2) {
1448 /* If this is too large windows will resize it to the maximum
1449 * allowed window size, we will then be back in here and resize
1450 * the font or terminal to fit.
1452 SetWindowPos(hwnd, NULL, 0, 0,
1453 font_width*term->cols + extra_width,
1454 font_height*term->rows + extra_height,
1455 SWP_NOMOVE | SWP_NOZORDER);
1458 InvalidateRect(hwnd, NULL, TRUE);
1462 /* Okay the user doesn't want us to change the font so we try the
1463 * window. But that may be too big for the screen which forces us
1464 * to change the terminal.
1466 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1467 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1469 offset_width = offset_height = cfg.window_border;
1470 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1471 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1473 if (win_width != font_width*term->cols + offset_width*2 ||
1474 win_height != font_height*term->rows + offset_height*2) {
1479 get_fullscreen_rect(&ss);
1481 width = (ss.right - ss.left - extra_width) / font_width;
1482 height = (ss.bottom - ss.top - extra_height) / font_height;
1485 if ( term->rows > height || term->cols > width ) {
1486 if (cfg.resize_action == RESIZE_EITHER) {
1487 /* Make the font the biggest we can */
1488 if (term->cols > width)
1489 font_width = (ss.right - ss.left - extra_width)
1491 if (term->rows > height)
1492 font_height = (ss.bottom - ss.top - extra_height)
1496 init_fonts(font_width, font_height);
1498 width = (ss.right - ss.left - extra_width) / font_width;
1499 height = (ss.bottom - ss.top - extra_height) / font_height;
1501 if ( height > term->rows ) height = term->rows;
1502 if ( width > term->cols ) width = term->cols;
1503 term_size(term, height, width, cfg.savelines);
1504 #ifdef RDB_DEBUG_PATCH
1505 debug((27, "reset_window() -> term resize to (%d,%d)",
1511 SetWindowPos(hwnd, NULL, 0, 0,
1512 font_width*term->cols + extra_width,
1513 font_height*term->rows + extra_height,
1514 SWP_NOMOVE | SWP_NOZORDER);
1516 InvalidateRect(hwnd, NULL, TRUE);
1517 #ifdef RDB_DEBUG_PATCH
1518 debug((27, "reset_window() -> window resize to (%d,%d)",
1519 font_width*term->cols + extra_width,
1520 font_height*term->rows + extra_height));
1526 /* We're allowed to or must change the font but do we want to ? */
1528 if (font_width != (win_width-cfg.window_border*2)/term->cols ||
1529 font_height != (win_height-cfg.window_border*2)/term->rows) {
1532 init_fonts((win_width-cfg.window_border*2)/term->cols,
1533 (win_height-cfg.window_border*2)/term->rows);
1534 offset_width = (win_width-font_width*term->cols)/2;
1535 offset_height = (win_height-font_height*term->rows)/2;
1537 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1538 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1540 InvalidateRect(hwnd, NULL, TRUE);
1541 #ifdef RDB_DEBUG_PATCH
1542 debug((25, "reset_window() -> font resize to (%d,%d)",
1543 font_width, font_height));
1548 static void set_input_locale(HKL kl)
1552 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1553 lbuf, sizeof(lbuf));
1555 kbd_codepage = atoi(lbuf);
1558 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1560 int thistime = GetMessageTime();
1562 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1563 lastbtn = MBT_NOTHING;
1564 term_mouse(term, b, translate_button(b), MA_CLICK,
1565 x, y, shift, ctrl, alt);
1569 if (lastbtn == b && thistime - lasttime < dbltime) {
1570 lastact = (lastact == MA_CLICK ? MA_2CLK :
1571 lastact == MA_2CLK ? MA_3CLK :
1572 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1577 if (lastact != MA_NOTHING)
1578 term_mouse(term, b, translate_button(b), lastact,
1579 x, y, shift, ctrl, alt);
1580 lasttime = thistime;
1584 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1585 * into a cooked one (SELECT, EXTEND, PASTE).
1587 static Mouse_Button translate_button(Mouse_Button button)
1589 if (button == MBT_LEFT)
1591 if (button == MBT_MIDDLE)
1592 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1593 if (button == MBT_RIGHT)
1594 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1595 return 0; /* shouldn't happen */
1598 static void show_mouseptr(int show)
1600 static int cursor_visible = 1;
1601 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1603 if (cursor_visible && !show)
1605 else if (!cursor_visible && show)
1607 cursor_visible = show;
1610 static int is_alt_pressed(void)
1613 int r = GetKeyboardState(keystate);
1616 if (keystate[VK_MENU] & 0x80)
1618 if (keystate[VK_RMENU] & 0x80)
1623 static int is_shift_pressed(void)
1626 int r = GetKeyboardState(keystate);
1629 if (keystate[VK_SHIFT] & 0x80)
1634 static int resizing;
1636 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1637 WPARAM wParam, LPARAM lParam)
1640 static int ignore_clip = FALSE;
1641 static int need_backend_resize = FALSE;
1642 static int fullscr_on_max = FALSE;
1646 if (pending_netevent)
1647 enact_pending_netevent();
1648 if (GetCapture() != hwnd ||
1649 (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
1655 if (cfg.ping_interval > 0) {
1658 if (now - last_movement > cfg.ping_interval) {
1659 back->special(backhandle, TS_PING);
1660 last_movement = now;
1663 net_pending_errors();
1669 if (!cfg.warn_on_close || session_closed ||
1671 "Are you sure you want to close this session?",
1672 "PuTTY Exit Confirmation",
1673 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1674 DestroyWindow(hwnd);
1681 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1693 PROCESS_INFORMATION pi;
1694 HANDLE filemap = NULL;
1696 if (wParam == IDM_DUPSESS) {
1698 * Allocate a file-mapping memory chunk for the
1701 SECURITY_ATTRIBUTES sa;
1704 sa.nLength = sizeof(sa);
1705 sa.lpSecurityDescriptor = NULL;
1706 sa.bInheritHandle = TRUE;
1707 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1710 0, sizeof(Config), NULL);
1712 p = (Config *) MapViewOfFile(filemap,
1714 0, 0, sizeof(Config));
1716 *p = cfg; /* structure copy */
1720 sprintf(c, "putty &%p", filemap);
1722 } else if (wParam == IDM_SAVEDSESS) {
1723 if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
1725 sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
1726 cl = smalloc(16 + strlen(session));
1727 /* 8, but play safe */
1730 /* not a very important failure mode */
1732 sprintf(cl, "putty @%s", session);
1740 GetModuleFileName(NULL, b, sizeof(b) - 1);
1742 si.lpReserved = NULL;
1743 si.lpDesktop = NULL;
1747 si.lpReserved2 = NULL;
1748 CreateProcess(b, cl, NULL, NULL, TRUE,
1749 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1752 CloseHandle(filemap);
1762 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1765 if (!do_reconfig(hwnd))
1769 /* Disable full-screen if resizing forbidden */
1770 HMENU m = GetSystemMenu (hwnd, FALSE);
1771 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1772 (cfg.resize_action == RESIZE_DISABLED)
1773 ? MF_GRAYED : MF_ENABLED);
1774 /* Gracefully unzoom if necessary */
1775 if (IsZoomed(hwnd) &&
1776 (cfg.resize_action == RESIZE_DISABLED)) {
1777 ShowWindow(hwnd, SW_RESTORE);
1781 /* Pass new config data to the logging module */
1782 log_reconfig(logctx, &cfg);
1786 * Flush the line discipline's edit buffer in the
1787 * case where local editing has just been disabled.
1789 ldisc_send(ldisc, NULL, 0, 0);
1797 /* Pass new config data to the terminal */
1798 term_reconfig(term, &cfg);
1800 /* Pass new config data to the back end */
1801 back->reconfig(backhandle, &cfg);
1803 /* Screen size changed ? */
1804 if (cfg.height != prev_cfg.height ||
1805 cfg.width != prev_cfg.width ||
1806 cfg.savelines != prev_cfg.savelines ||
1807 cfg.resize_action == RESIZE_FONT ||
1808 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1809 cfg.resize_action == RESIZE_DISABLED)
1810 term_size(term, cfg.height, cfg.width, cfg.savelines);
1812 /* Enable or disable the scroll bar, etc */
1814 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1815 LONG nexflag, exflag =
1816 GetWindowLong(hwnd, GWL_EXSTYLE);
1819 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1820 if (cfg.alwaysontop) {
1821 nexflag |= WS_EX_TOPMOST;
1822 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1823 SWP_NOMOVE | SWP_NOSIZE);
1825 nexflag &= ~(WS_EX_TOPMOST);
1826 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1827 SWP_NOMOVE | SWP_NOSIZE);
1830 if (cfg.sunken_edge)
1831 nexflag |= WS_EX_CLIENTEDGE;
1833 nexflag &= ~(WS_EX_CLIENTEDGE);
1836 if (is_full_screen() ?
1837 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1840 nflg &= ~WS_VSCROLL;
1842 if (cfg.resize_action == RESIZE_DISABLED ||
1844 nflg &= ~WS_THICKFRAME;
1846 nflg |= WS_THICKFRAME;
1848 if (cfg.resize_action == RESIZE_DISABLED)
1849 nflg &= ~WS_MAXIMIZEBOX;
1851 nflg |= WS_MAXIMIZEBOX;
1853 if (nflg != flag || nexflag != exflag) {
1855 SetWindowLong(hwnd, GWL_STYLE, nflg);
1856 if (nexflag != exflag)
1857 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1859 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1860 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1861 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1869 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1874 set_title(NULL, cfg.wintitle);
1875 if (IsIconic(hwnd)) {
1877 cfg.win_name_always ? window_name :
1881 if (strcmp(cfg.font.name, prev_cfg.font.name) != 0 ||
1882 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1883 cfg.font.isbold != prev_cfg.font.isbold ||
1884 cfg.font.height != prev_cfg.font.height ||
1885 cfg.font.charset != prev_cfg.font.charset ||
1886 cfg.vtmode != prev_cfg.vtmode ||
1887 cfg.bold_colour != prev_cfg.bold_colour ||
1888 cfg.resize_action == RESIZE_DISABLED ||
1889 cfg.resize_action == RESIZE_EITHER ||
1890 (cfg.resize_action != prev_cfg.resize_action))
1893 InvalidateRect(hwnd, NULL, TRUE);
1894 reset_window(init_lvl);
1895 net_pending_errors();
1906 ldisc_send(ldisc, NULL, 0, 0);
1909 back->special(backhandle, TS_AYT);
1910 net_pending_errors();
1913 back->special(backhandle, TS_BRK);
1914 net_pending_errors();
1917 back->special(backhandle, TS_SYNCH);
1918 net_pending_errors();
1921 back->special(backhandle, TS_EC);
1922 net_pending_errors();
1925 back->special(backhandle, TS_EL);
1926 net_pending_errors();
1929 back->special(backhandle, TS_GA);
1930 net_pending_errors();
1933 back->special(backhandle, TS_NOP);
1934 net_pending_errors();
1937 back->special(backhandle, TS_ABORT);
1938 net_pending_errors();
1941 back->special(backhandle, TS_AO);
1942 net_pending_errors();
1945 back->special(backhandle, TS_IP);
1946 net_pending_errors();
1949 back->special(backhandle, TS_SUSP);
1950 net_pending_errors();
1953 back->special(backhandle, TS_EOR);
1954 net_pending_errors();
1957 back->special(backhandle, TS_EOF);
1958 net_pending_errors();
1964 WinHelp(hwnd, help_path,
1965 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1969 * We get this if the System menu has been activated
1976 * We get this if the System menu has been activated
1977 * using the keyboard. This might happen from within
1978 * TranslateKey, in which case it really wants to be
1979 * followed by a `space' character to actually _bring
1980 * the menu up_ rather than just sitting there in
1981 * `ready to appear' state.
1983 show_mouseptr(1); /* make sure pointer is visible */
1985 PostMessage(hwnd, WM_CHAR, ' ', 0);
1987 case IDM_FULLSCREEN:
1991 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1992 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1997 #define X_POS(l) ((int)(short)LOWORD(l))
1998 #define Y_POS(l) ((int)(short)HIWORD(l))
2000 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
2001 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
2002 case WM_LBUTTONDOWN:
2003 case WM_MBUTTONDOWN:
2004 case WM_RBUTTONDOWN:
2012 case WM_LBUTTONDOWN:
2016 case WM_MBUTTONDOWN:
2017 button = MBT_MIDDLE;
2020 case WM_RBUTTONDOWN:
2029 button = MBT_MIDDLE;
2037 button = press = 0; /* shouldn't happen */
2041 * Special case: in full-screen mode, if the left
2042 * button is clicked in the very top left corner of the
2043 * window, we put up the System menu instead of doing
2046 if (is_full_screen() && press && button == MBT_LEFT &&
2047 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2048 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2053 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2054 wParam & MK_SHIFT, wParam & MK_CONTROL,
2058 term_mouse(term, button, translate_button(button), MA_RELEASE,
2059 TO_CHR_X(X_POS(lParam)),
2060 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2061 wParam & MK_CONTROL, is_alt_pressed());
2069 * Add the mouse position and message time to the random
2072 noise_ultralight(lParam);
2074 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2075 GetCapture() == hwnd) {
2077 if (wParam & MK_LBUTTON)
2079 else if (wParam & MK_MBUTTON)
2083 term_mouse(term, b, translate_button(b), MA_DRAG,
2084 TO_CHR_X(X_POS(lParam)),
2085 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2086 wParam & MK_CONTROL, is_alt_pressed());
2089 case WM_NCMOUSEMOVE:
2091 noise_ultralight(lParam);
2093 case WM_IGNORE_CLIP:
2094 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2096 case WM_DESTROYCLIPBOARD:
2098 term_deselect(term);
2099 ignore_clip = FALSE;
2105 hdc = BeginPaint(hwnd, &p);
2107 SelectPalette(hdc, pal, TRUE);
2108 RealizePalette(hdc);
2110 term_paint(term, hdc,
2111 (p.rcPaint.left-offset_width)/font_width,
2112 (p.rcPaint.top-offset_height)/font_height,
2113 (p.rcPaint.right-offset_width-1)/font_width,
2114 (p.rcPaint.bottom-offset_height-1)/font_height,
2118 p.rcPaint.left < offset_width ||
2119 p.rcPaint.top < offset_height ||
2120 p.rcPaint.right >= offset_width + font_width*term->cols ||
2121 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2123 HBRUSH fillcolour, oldbrush;
2125 fillcolour = CreateSolidBrush (
2126 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2127 oldbrush = SelectObject(hdc, fillcolour);
2128 edge = CreatePen(PS_SOLID, 0,
2129 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2130 oldpen = SelectObject(hdc, edge);
2133 * Jordan Russell reports that this apparently
2134 * ineffectual IntersectClipRect() call masks a
2135 * Windows NT/2K bug causing strange display
2136 * problems when the PuTTY window is taller than
2137 * the primary monitor. It seems harmless enough...
2139 IntersectClipRect(hdc,
2140 p.rcPaint.left, p.rcPaint.top,
2141 p.rcPaint.right, p.rcPaint.bottom);
2143 ExcludeClipRect(hdc,
2144 offset_width, offset_height,
2145 offset_width+font_width*term->cols,
2146 offset_height+font_height*term->rows);
2148 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2149 p.rcPaint.right, p.rcPaint.bottom);
2151 // SelectClipRgn(hdc, NULL);
2153 SelectObject(hdc, oldbrush);
2154 DeleteObject(fillcolour);
2155 SelectObject(hdc, oldpen);
2158 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2159 SelectObject(hdc, GetStockObject(WHITE_PEN));
2165 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2166 * but the only one that's likely to try to overload us is FD_READ.
2167 * This means buffering just one is fine.
2169 if (pending_netevent)
2170 enact_pending_netevent();
2172 pending_netevent = TRUE;
2173 pend_netevent_wParam = wParam;
2174 pend_netevent_lParam = lParam;
2175 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2176 enact_pending_netevent();
2178 time(&last_movement);
2181 term->has_focus = TRUE;
2182 CreateCaret(hwnd, caretbm, font_width, font_height);
2184 flash_window(0); /* stop */
2191 term->has_focus = FALSE;
2193 caret_x = caret_y = -1; /* ensure caret is replaced next time */
2197 case WM_ENTERSIZEMOVE:
2198 #ifdef RDB_DEBUG_PATCH
2199 debug((27, "WM_ENTERSIZEMOVE"));
2203 need_backend_resize = FALSE;
2205 case WM_EXITSIZEMOVE:
2208 #ifdef RDB_DEBUG_PATCH
2209 debug((27, "WM_EXITSIZEMOVE"));
2211 if (need_backend_resize) {
2212 term_size(term, cfg.height, cfg.width, cfg.savelines);
2213 InvalidateRect(hwnd, NULL, TRUE);
2218 * This does two jobs:
2219 * 1) Keep the sizetip uptodate
2220 * 2) Make sure the window size is _stepped_ in units of the font size.
2222 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2223 int width, height, w, h, ew, eh;
2224 LPRECT r = (LPRECT) lParam;
2226 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2227 (cfg.height != term->rows || cfg.width != term->cols )) {
2229 * Great! It seems that both the terminal size and the
2230 * font size have been changed and the user is now dragging.
2232 * It will now be difficult to get back to the configured
2235 * This would be easier but it seems to be too confusing.
2237 term_size(term, cfg.height, cfg.width, cfg.savelines);
2240 cfg.height=term->rows; cfg.width=term->cols;
2242 InvalidateRect(hwnd, NULL, TRUE);
2243 need_backend_resize = TRUE;
2246 width = r->right - r->left - extra_width;
2247 height = r->bottom - r->top - extra_height;
2248 w = (width + font_width / 2) / font_width;
2251 h = (height + font_height / 2) / font_height;
2254 UpdateSizeTip(hwnd, w, h);
2255 ew = width - w * font_width;
2256 eh = height - h * font_height;
2258 if (wParam == WMSZ_LEFT ||
2259 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2265 if (wParam == WMSZ_TOP ||
2266 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2276 int width, height, w, h, rv = 0;
2277 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2278 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2279 LPRECT r = (LPRECT) lParam;
2281 width = r->right - r->left - ex_width;
2282 height = r->bottom - r->top - ex_height;
2284 w = (width + term->cols/2)/term->cols;
2285 h = (height + term->rows/2)/term->rows;
2286 if ( r->right != r->left + w*term->cols + ex_width)
2289 if (wParam == WMSZ_LEFT ||
2290 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2291 r->left = r->right - w*term->cols - ex_width;
2293 r->right = r->left + w*term->cols + ex_width;
2295 if (r->bottom != r->top + h*term->rows + ex_height)
2298 if (wParam == WMSZ_TOP ||
2299 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2300 r->top = r->bottom - h*term->rows - ex_height;
2302 r->bottom = r->top + h*term->rows + ex_height;
2306 /* break; (never reached) */
2307 case WM_FULLSCR_ON_MAX:
2308 fullscr_on_max = TRUE;
2311 sys_cursor_update();
2314 #ifdef RDB_DEBUG_PATCH
2315 debug((27, "WM_SIZE %s (%d,%d)",
2316 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2317 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2318 (wParam == SIZE_RESTORED && resizing) ? "to":
2319 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2321 LOWORD(lParam), HIWORD(lParam)));
2323 if (wParam == SIZE_MINIMIZED)
2325 cfg.win_name_always ? window_name : icon_name);
2326 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2327 SetWindowText(hwnd, window_name);
2328 if (wParam == SIZE_RESTORED)
2329 clear_full_screen();
2330 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2331 fullscr_on_max = FALSE;
2335 if (cfg.resize_action == RESIZE_DISABLED) {
2336 /* A resize, well it better be a minimize. */
2340 int width, height, w, h;
2342 width = LOWORD(lParam);
2343 height = HIWORD(lParam);
2346 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2348 prev_rows = term->rows;
2349 prev_cols = term->cols;
2350 if (cfg.resize_action == RESIZE_TERM) {
2351 w = width / font_width;
2353 h = height / font_height;
2356 term_size(term, h, w, cfg.savelines);
2359 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2361 if (cfg.resize_action == RESIZE_TERM)
2362 term_size(term, prev_rows, prev_cols, cfg.savelines);
2363 if (cfg.resize_action != RESIZE_FONT)
2368 /* This is an unexpected resize, these will normally happen
2369 * if the window is too large. Probably either the user
2370 * selected a huge font or the screen size has changed.
2372 * This is also called with minimize.
2374 else reset_window(-1);
2378 * Don't call back->size in mid-resize. (To prevent
2379 * massive numbers of resize events getting sent
2380 * down the connection during an NT opaque drag.)
2383 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2384 need_backend_resize = TRUE;
2385 w = (width-cfg.window_border*2) / font_width;
2387 h = (height-cfg.window_border*2) / font_height;
2396 sys_cursor_update();
2399 switch (LOWORD(wParam)) {
2401 term_scroll(term, -1, 0);
2404 term_scroll(term, +1, 0);
2407 term_scroll(term, 0, +1);
2410 term_scroll(term, 0, -1);
2413 term_scroll(term, 0, +term->rows / 2);
2416 term_scroll(term, 0, -term->rows / 2);
2418 case SB_THUMBPOSITION:
2420 term_scroll(term, 1, HIWORD(wParam));
2424 case WM_PALETTECHANGED:
2425 if ((HWND) wParam != hwnd && pal != NULL) {
2426 HDC hdc = get_ctx(NULL);
2428 if (RealizePalette(hdc) > 0)
2434 case WM_QUERYNEWPALETTE:
2436 HDC hdc = get_ctx(NULL);
2438 if (RealizePalette(hdc) > 0)
2450 * Add the scan code and keypress timing to the random
2453 noise_ultralight(lParam);
2456 * We don't do TranslateMessage since it disassociates the
2457 * resulting CHAR message from the KEYDOWN that sparked it,
2458 * which we occasionally don't want. Instead, we process
2459 * KEYDOWN, and call the Win32 translator functions so that
2460 * we get the translations under _our_ control.
2463 unsigned char buf[20];
2466 if (wParam == VK_PROCESSKEY) {
2469 m.message = WM_KEYDOWN;
2471 m.lParam = lParam & 0xdfff;
2472 TranslateMessage(&m);
2474 len = TranslateKey(message, wParam, lParam, buf);
2476 return DefWindowProc(hwnd, message, wParam, lParam);
2480 * Interrupt an ongoing paste. I'm not sure
2481 * this is sensible, but for the moment it's
2482 * preferable to having to faff about buffering
2488 * We need not bother about stdin backlogs
2489 * here, because in GUI PuTTY we can't do
2490 * anything about it anyway; there's no means
2491 * of asking Windows to hold off on KEYDOWN
2492 * messages. We _have_ to buffer everything
2495 term_seen_key_event(term);
2496 ldisc_send(ldisc, buf, len, 1);
2501 net_pending_errors();
2503 case WM_INPUTLANGCHANGE:
2504 /* wParam == Font number */
2505 /* lParam == Locale */
2506 set_input_locale((HKL)lParam);
2507 sys_cursor_update();
2510 if(wParam == IMN_SETOPENSTATUS) {
2511 HIMC hImc = ImmGetContext(hwnd);
2512 ImmSetCompositionFont(hImc, &lfont);
2513 ImmReleaseContext(hwnd, hImc);
2517 case WM_IME_COMPOSITION:
2523 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2524 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2526 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2527 break; /* fall back to DefWindowProc */
2529 hIMC = ImmGetContext(hwnd);
2530 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2534 buff = (char*) smalloc(n);
2535 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2537 * Jaeyoun Chung reports that Korean character
2538 * input doesn't work correctly if we do a single
2539 * luni_send() covering the whole of buff. So
2540 * instead we luni_send the characters one by one.
2542 term_seen_key_event(term);
2543 for (i = 0; i < n; i += 2) {
2544 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2548 ImmReleaseContext(hwnd, hIMC);
2553 if (wParam & 0xFF00) {
2554 unsigned char buf[2];
2557 buf[0] = wParam >> 8;
2558 term_seen_key_event(term);
2559 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
2561 char c = (unsigned char) wParam;
2562 term_seen_key_event(term);
2563 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
2569 * Nevertheless, we are prepared to deal with WM_CHAR
2570 * messages, should they crop up. So if someone wants to
2571 * post the things to us as part of a macro manoeuvre,
2572 * we're ready to cope.
2575 char c = (unsigned char)wParam;
2576 term_seen_key_event(term);
2577 lpage_send(ldisc, CP_ACP, &c, 1, 1);
2581 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2582 SetCursor(LoadCursor(NULL, IDC_ARROW));
2586 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
2587 int shift_pressed=0, control_pressed=0;
2589 if (message == WM_MOUSEWHEEL) {
2590 wheel_accumulator += (short)HIWORD(wParam);
2591 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2592 control_pressed=LOWORD(wParam) & MK_CONTROL;
2595 wheel_accumulator += (int)wParam;
2596 if (GetKeyboardState(keys)!=0) {
2597 shift_pressed=keys[VK_SHIFT]&0x80;
2598 control_pressed=keys[VK_CONTROL]&0x80;
2602 /* process events when the threshold is reached */
2603 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2606 /* reduce amount for next time */
2607 if (wheel_accumulator > 0) {
2609 wheel_accumulator -= WHEEL_DELTA;
2610 } else if (wheel_accumulator < 0) {
2612 wheel_accumulator += WHEEL_DELTA;
2616 if (send_raw_mouse &&
2617 !(cfg.mouse_override && shift_pressed)) {
2618 /* send a mouse-down followed by a mouse up */
2619 term_mouse(term, b, translate_button(b),
2621 TO_CHR_X(X_POS(lParam)),
2622 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2623 control_pressed, is_alt_pressed());
2624 term_mouse(term, b, translate_button(b),
2625 MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2626 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2627 control_pressed, is_alt_pressed());
2629 /* trigger a scroll */
2630 term_scroll(term, 0,
2632 -term->rows / 2 : term->rows / 2);
2639 return DefWindowProc(hwnd, message, wParam, lParam);
2643 * Move the system caret. (We maintain one, even though it's
2644 * invisible, for the benefit of blind people: apparently some
2645 * helper software tracks the system caret, so we should arrange to
2648 void sys_cursor(void *frontend, int x, int y)
2652 if (!term->has_focus) return;
2655 * Avoid gratuitously re-updating the cursor position and IMM
2656 * window if there's no actual change required.
2658 cx = x * font_width + offset_width;
2659 cy = y * font_height + offset_height;
2660 if (cx == caret_x && cy == caret_y)
2665 sys_cursor_update();
2668 static void sys_cursor_update(void)
2673 if (!term->has_focus) return;
2675 if (caret_x < 0 || caret_y < 0)
2678 SetCaretPos(caret_x, caret_y);
2680 /* IMM calls on Win98 and beyond only */
2681 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2683 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2684 osVersion.dwMinorVersion == 0) return; /* 95 */
2686 /* we should have the IMM functions */
2687 hIMC = ImmGetContext(hwnd);
2688 cf.dwStyle = CFS_POINT;
2689 cf.ptCurrentPos.x = caret_x;
2690 cf.ptCurrentPos.y = caret_y;
2691 ImmSetCompositionWindow(hIMC, &cf);
2693 ImmReleaseContext(hwnd, hIMC);
2697 * Draw a line of text in the window, at given character
2698 * coordinates, in given attributes.
2700 * We are allowed to fiddle with the contents of `text'.
2702 void do_text(Context ctx, int x, int y, char *text, int len,
2703 unsigned long attr, int lattr)
2706 int nfg, nbg, nfont;
2709 int force_manual_underline = 0;
2710 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2711 int char_width = fnt_width;
2712 int text_adjust = 0;
2713 static int *IpDx = 0, IpDxLEN = 0;
2715 if (attr & ATTR_WIDE)
2718 if (len > IpDxLEN || IpDx[0] != char_width) {
2720 if (len > IpDxLEN) {
2722 IpDx = smalloc((len + 16) * sizeof(int));
2723 IpDxLEN = (len + 16);
2725 for (i = 0; i < IpDxLEN; i++)
2726 IpDx[i] = char_width;
2729 /* Only want the left half of double width lines */
2730 if (lattr != LATTR_NORM && x*2 >= term->cols)
2738 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
2739 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2740 attr ^= ATTR_CUR_XOR;
2744 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2745 /* Assume a poorman font is borken in other ways too. */
2755 nfont |= FONT_WIDE + FONT_HIGH;
2758 if (attr & ATTR_NARROW)
2759 nfont |= FONT_NARROW;
2761 /* Special hack for the VT100 linedraw glyphs. */
2762 if ((attr & CSET_MASK) == 0x2300) {
2763 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2764 switch ((unsigned char) (text[0])) {
2766 text_adjust = -2 * font_height / 5;
2769 text_adjust = -1 * font_height / 5;
2772 text_adjust = font_height / 5;
2775 text_adjust = 2 * font_height / 5;
2778 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2781 text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
2782 attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
2783 if (attr & ATTR_UNDER) {
2784 attr &= ~ATTR_UNDER;
2785 force_manual_underline = 1;
2790 /* Anything left as an original character set is unprintable. */
2791 if (DIRECT_CHAR(attr)) {
2794 memset(text, 0xFD, len);
2798 if ((attr & CSET_MASK) == ATTR_OEMCP)
2801 nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2802 nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0);
2803 nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2804 nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0);
2805 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2807 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2808 nfont |= FONT_UNDERLINE;
2809 another_font(nfont);
2810 if (!fonts[nfont]) {
2811 if (nfont & FONT_UNDERLINE)
2812 force_manual_underline = 1;
2813 /* Don't do the same for manual bold, it could be bad news. */
2815 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2817 another_font(nfont);
2819 nfont = FONT_NORMAL;
2820 if (attr & ATTR_REVERSE) {
2825 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2827 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2831 SelectObject(hdc, fonts[nfont]);
2832 SetTextColor(hdc, fg);
2833 SetBkColor(hdc, bg);
2834 SetBkMode(hdc, OPAQUE);
2837 line_box.right = x + char_width * len;
2838 line_box.bottom = y + font_height;
2840 /* Only want the left half of double width lines */
2841 if (line_box.right > font_width*term->cols+offset_width)
2842 line_box.right = font_width*term->cols+offset_width;
2844 /* We're using a private area for direct to font. (512 chars.) */
2845 if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2846 /* Ho Hum, dbcs fonts are a PITA! */
2847 /* To display on W9x I have to convert to UCS */
2848 static wchar_t *uni_buf = 0;
2849 static int uni_len = 0;
2851 if (len > uni_len) {
2853 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2856 for(nlen = mptr = 0; mptr<len; mptr++) {
2857 uni_buf[nlen] = 0xFFFD;
2858 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
2859 IpDx[nlen] += char_width;
2860 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2861 text+mptr, 2, uni_buf+nlen, 1);
2866 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2867 text+mptr, 1, uni_buf+nlen, 1);
2875 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2876 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2877 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2878 SetBkMode(hdc, TRANSPARENT);
2879 ExtTextOutW(hdc, x - 1,
2880 y - font_height * (lattr ==
2881 LATTR_BOT) + text_adjust,
2882 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2886 } else if (DIRECT_FONT(attr)) {
2888 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2889 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2890 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2891 SetBkMode(hdc, TRANSPARENT);
2893 /* GRR: This draws the character outside it's box and can leave
2894 * 'droppings' even with the clip box! I suppose I could loop it
2895 * one character at a time ... yuk.
2897 * Or ... I could do a test print with "W", and use +1 or -1 for this
2898 * shift depending on if the leftmost column is blank...
2900 ExtTextOut(hdc, x - 1,
2901 y - font_height * (lattr ==
2902 LATTR_BOT) + text_adjust,
2903 ETO_CLIPPED, &line_box, text, len, IpDx);
2906 /* And 'normal' unicode characters */
2907 static WCHAR *wbuf = NULL;
2908 static int wlen = 0;
2913 wbuf = smalloc(wlen * sizeof(WCHAR));
2915 for (i = 0; i < len; i++)
2916 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2919 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2920 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2922 /* And the shadow bold hack. */
2923 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2924 SetBkMode(hdc, TRANSPARENT);
2925 ExtTextOutW(hdc, x - 1,
2926 y - font_height * (lattr ==
2927 LATTR_BOT) + text_adjust,
2928 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2931 if (lattr != LATTR_TOP && (force_manual_underline ||
2932 (und_mode == UND_LINE
2933 && (attr & ATTR_UNDER)))) {
2936 if (lattr == LATTR_BOT)
2937 dec = dec * 2 - font_height;
2939 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2940 MoveToEx(hdc, x, y + dec, NULL);
2941 LineTo(hdc, x + len * char_width, y + dec);
2942 oldpen = SelectObject(hdc, oldpen);
2943 DeleteObject(oldpen);
2947 void do_cursor(Context ctx, int x, int y, char *text, int len,
2948 unsigned long attr, int lattr)
2954 int ctype = cfg.cursor_type;
2956 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
2957 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2958 do_text(ctx, x, y, text, len, attr, lattr);
2962 attr |= TATTR_RIGHTCURS;
2965 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2966 if (attr & ATTR_WIDE)
2973 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
2976 pts[0].x = pts[1].x = pts[4].x = x;
2977 pts[2].x = pts[3].x = x + char_width - 1;
2978 pts[0].y = pts[3].y = pts[4].y = y;
2979 pts[1].y = pts[2].y = y + font_height - 1;
2980 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2981 Polyline(hdc, pts, 5);
2982 oldpen = SelectObject(hdc, oldpen);
2983 DeleteObject(oldpen);
2984 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2985 int startx, starty, dx, dy, length, i;
2988 starty = y + descent;
2991 length = char_width;
2994 if (attr & TATTR_RIGHTCURS)
2995 xadjust = char_width - 1;
2996 startx = x + xadjust;
3000 length = font_height;
3002 if (attr & TATTR_ACTCURS) {
3005 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
3006 MoveToEx(hdc, startx, starty, NULL);
3007 LineTo(hdc, startx + dx * length, starty + dy * length);
3008 oldpen = SelectObject(hdc, oldpen);
3009 DeleteObject(oldpen);
3011 for (i = 0; i < length; i++) {
3013 SetPixel(hdc, startx, starty, colours[23]);
3022 /* This function gets the actual width of a character in the normal font.
3024 int char_width(Context ctx, int uc) {
3028 /* If the font max is the same as the font ave width then this
3029 * function is a no-op.
3031 if (!font_dualwidth) return 1;
3033 switch (uc & CSET_MASK) {
3035 uc = ucsdata.unitab_line[uc & 0xFF];
3038 uc = ucsdata.unitab_xterm[uc & 0xFF];
3041 uc = ucsdata.unitab_scoacs[uc & 0xFF];
3044 if (DIRECT_FONT(uc)) {
3045 if (ucsdata.dbcs_screenfont) return 1;
3047 /* Speedup, I know of no font where ascii is the wrong width */
3048 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3051 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3052 SelectObject(hdc, fonts[FONT_NORMAL]);
3053 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3054 another_font(FONT_OEM);
3055 if (!fonts[FONT_OEM]) return 0;
3057 SelectObject(hdc, fonts[FONT_OEM]);
3061 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3062 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3065 /* Speedup, I know of no font where ascii is the wrong width */
3066 if (uc >= ' ' && uc <= '~') return 1;
3068 SelectObject(hdc, fonts[FONT_NORMAL]);
3069 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3070 /* Okay that one worked */ ;
3071 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3072 /* This should work on 9x too, but it's "less accurate" */ ;
3077 ibuf += font_width / 2 -1;
3084 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3085 * codes. Returns number of bytes used or zero to drop the message
3086 * or -1 to forward the message to windows.
3088 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3089 unsigned char *output)
3092 int scan, left_alt = 0, key_down, shift_state;
3094 unsigned char *p = output;
3095 static int alt_sum = 0;
3097 HKL kbd_layout = GetKeyboardLayout(0);
3099 static WORD keys[3];
3100 static int compose_char = 0;
3101 static WPARAM compose_key = 0;
3103 r = GetKeyboardState(keystate);
3105 memset(keystate, 0, sizeof(keystate));
3108 #define SHOW_TOASCII_RESULT
3109 { /* Tell us all about key events */
3110 static BYTE oldstate[256];
3111 static int first = 1;
3115 memcpy(oldstate, keystate, sizeof(oldstate));
3118 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3120 } else if ((HIWORD(lParam) & KF_UP)
3121 && scan == (HIWORD(lParam) & 0xFF)) {
3125 if (wParam >= VK_F1 && wParam <= VK_F20)
3126 debug(("K_F%d", wParam + 1 - VK_F1));
3139 debug(("VK_%02x", wParam));
3141 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3143 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3145 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3146 if (ch >= ' ' && ch <= '~')
3147 debug((", '%c'", ch));
3149 debug((", $%02x", ch));
3152 debug((", KB0=%02x", keys[0]));
3154 debug((", KB1=%02x", keys[1]));
3156 debug((", KB2=%02x", keys[2]));
3158 if ((keystate[VK_SHIFT] & 0x80) != 0)
3160 if ((keystate[VK_CONTROL] & 0x80) != 0)
3162 if ((HIWORD(lParam) & KF_EXTENDED))
3164 if ((HIWORD(lParam) & KF_UP))
3168 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3169 else if ((HIWORD(lParam) & KF_UP))
3170 oldstate[wParam & 0xFF] ^= 0x80;
3172 oldstate[wParam & 0xFF] ^= 0x81;
3174 for (ch = 0; ch < 256; ch++)
3175 if (oldstate[ch] != keystate[ch])
3176 debug((", M%02x=%02x", ch, keystate[ch]));
3178 memcpy(oldstate, keystate, sizeof(oldstate));
3182 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3183 keystate[VK_RMENU] = keystate[VK_MENU];
3187 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3188 if ((cfg.funky_type == 3 ||
3189 (cfg.funky_type <= 1 && term->app_keypad_keys &&
3191 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3193 wParam = VK_EXECUTE;
3195 /* UnToggle NUMLock */
3196 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3197 keystate[VK_NUMLOCK] ^= 1;
3200 /* And write back the 'adjusted' state */
3201 SetKeyboardState(keystate);
3204 /* Disable Auto repeat if required */
3205 if (term->repeat_off &&
3206 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3209 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3212 key_down = ((HIWORD(lParam) & KF_UP) == 0);
3214 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3215 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3216 if (cfg.ctrlaltkeys)
3217 keystate[VK_MENU] = 0;
3219 keystate[VK_RMENU] = 0x80;
3224 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3225 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3226 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3228 /* Note if AltGr was pressed and if it was used as a compose key */
3229 if (!compose_state) {
3230 compose_key = 0x100;
3231 if (cfg.compose_key) {
3232 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3233 compose_key = wParam;
3235 if (wParam == VK_APPS)
3236 compose_key = wParam;
3239 if (wParam == compose_key) {
3240 if (compose_state == 0
3241 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3243 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3247 } else if (compose_state == 1 && wParam != VK_CONTROL)
3250 if (compose_state > 1 && left_alt)
3253 /* Sanitize the number pad if not using a PC NumPad */
3254 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
3255 && cfg.funky_type != 2)
3256 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3257 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3261 nParam = VK_NUMPAD0;
3264 nParam = VK_NUMPAD1;
3267 nParam = VK_NUMPAD2;
3270 nParam = VK_NUMPAD3;
3273 nParam = VK_NUMPAD4;
3276 nParam = VK_NUMPAD5;
3279 nParam = VK_NUMPAD6;
3282 nParam = VK_NUMPAD7;
3285 nParam = VK_NUMPAD8;
3288 nParam = VK_NUMPAD9;
3291 nParam = VK_DECIMAL;
3295 if (keystate[VK_NUMLOCK] & 1)
3302 /* If a key is pressed and AltGr is not active */
3303 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3304 /* Okay, prepare for most alts then ... */
3308 /* Lets see if it's a pattern we know all about ... */
3309 if (wParam == VK_PRIOR && shift_state == 1) {
3310 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3313 if (wParam == VK_PRIOR && shift_state == 2) {
3314 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3317 if (wParam == VK_NEXT && shift_state == 1) {
3318 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3321 if (wParam == VK_NEXT && shift_state == 2) {
3322 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3325 if (wParam == VK_INSERT && shift_state == 1) {
3326 term_do_paste(term);
3329 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3332 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3333 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3336 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3337 (cfg.resize_action != RESIZE_DISABLED)) {
3338 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3342 /* Control-Numlock for app-keypad mode switch */
3343 if (wParam == VK_PAUSE && shift_state == 2) {
3344 term->app_keypad_keys ^= 1;
3348 /* Nethack keypad */
3349 if (cfg.nethack_keypad && !left_alt) {
3352 *p++ = shift_state ? 'B' : 'b';
3355 *p++ = shift_state ? 'J' : 'j';
3358 *p++ = shift_state ? 'N' : 'n';
3361 *p++ = shift_state ? 'H' : 'h';
3364 *p++ = shift_state ? '.' : '.';
3367 *p++ = shift_state ? 'L' : 'l';
3370 *p++ = shift_state ? 'Y' : 'y';
3373 *p++ = shift_state ? 'K' : 'k';
3376 *p++ = shift_state ? 'U' : 'u';
3381 /* Application Keypad */
3385 if (cfg.funky_type == 3 ||
3386 (cfg.funky_type <= 1 &&
3387 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3401 if (term->app_keypad_keys && !cfg.no_applic_k)
3438 if (cfg.funky_type == 2) {
3443 } else if (shift_state)
3450 if (cfg.funky_type == 2)
3454 if (cfg.funky_type == 2)
3458 if (cfg.funky_type == 2)
3463 if (HIWORD(lParam) & KF_EXTENDED)
3468 if (term->vt52_mode) {
3469 if (xkey >= 'P' && xkey <= 'S')
3470 p += sprintf((char *) p, "\x1B%c", xkey);
3472 p += sprintf((char *) p, "\x1B?%c", xkey);
3474 p += sprintf((char *) p, "\x1BO%c", xkey);
3479 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3480 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3484 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3485 /* We do the opposite of what is configured */
3486 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3490 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3496 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3500 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3504 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3509 if (wParam == VK_PAUSE) { /* Break/Pause */
3514 /* Control-2 to Control-8 are special */
3515 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3516 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3519 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
3523 if (shift_state == 2 && wParam == 0xDF) {
3527 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
3534 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3535 * for integer decimal nn.)
3537 * We also deal with the weird ones here. Linux VCs replace F1
3538 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3539 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3545 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3548 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3551 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3554 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3557 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3560 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3563 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3566 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3569 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3572 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3605 if ((shift_state&2) == 0) switch (wParam) {
3625 /* Reorder edit keys to physical order */
3626 if (cfg.funky_type == 3 && code <= 6)
3627 code = "\0\2\1\4\5\3\6"[code];
3629 if (term->vt52_mode && code > 0 && code <= 6) {
3630 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3634 if (cfg.funky_type == 5 && /* SCO function keys */
3635 code >= 11 && code <= 34) {
3636 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3639 case VK_F1: index = 0; break;
3640 case VK_F2: index = 1; break;
3641 case VK_F3: index = 2; break;
3642 case VK_F4: index = 3; break;
3643 case VK_F5: index = 4; break;
3644 case VK_F6: index = 5; break;
3645 case VK_F7: index = 6; break;
3646 case VK_F8: index = 7; break;
3647 case VK_F9: index = 8; break;
3648 case VK_F10: index = 9; break;
3649 case VK_F11: index = 10; break;
3650 case VK_F12: index = 11; break;
3652 if (keystate[VK_SHIFT] & 0x80) index += 12;
3653 if (keystate[VK_CONTROL] & 0x80) index += 24;
3654 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3657 if (cfg.funky_type == 5 && /* SCO small keypad */
3658 code >= 1 && code <= 6) {
3659 char codes[] = "HL.FIG";
3663 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3667 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3673 if (term->vt52_mode)
3674 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3677 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3680 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3681 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3684 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3685 if (term->vt52_mode)
3686 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3688 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3691 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3692 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3696 p += sprintf((char *) p, "\x1B[%d~", code);
3701 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3702 * some reason seems to send VK_CLEAR to Windows...).
3724 if (term->vt52_mode)
3725 p += sprintf((char *) p, "\x1B%c", xkey);
3727 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3730 * RDB: VT100 & VT102 manuals both state the
3731 * app cursor keys only work if the app keypad
3734 * SGT: That may well be true, but xterm
3735 * disagrees and so does at least one
3736 * application, so I've #if'ed this out and the
3737 * behaviour is back to PuTTY's original: app
3738 * cursor and app keypad are independently
3739 * switchable modes. If anyone complains about
3740 * _this_ I'll have to put in a configurable
3743 if (!term->app_keypad_keys)
3746 /* Useful mapping of Ctrl-arrows */
3747 if (shift_state == 2)
3751 p += sprintf((char *) p, "\x1BO%c", xkey);
3753 p += sprintf((char *) p, "\x1B[%c", xkey);
3760 * Finally, deal with Return ourselves. (Win95 seems to
3761 * foul it up when Alt is pressed, for some reason.)
3763 if (wParam == VK_RETURN) { /* Return */
3769 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3770 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3775 /* Okay we've done everything interesting; let windows deal with
3776 * the boring stuff */
3780 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3781 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3783 keystate[VK_CAPITAL] = 0;
3786 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3787 #ifdef SHOW_TOASCII_RESULT
3788 if (r == 1 && !key_down) {
3790 if (in_utf(term) || ucsdata.dbcs_screenfont)
3791 debug((", (U+%04x)", alt_sum));
3793 debug((", LCH(%d)", alt_sum));
3795 debug((", ACH(%d)", keys[0]));
3800 for (r1 = 0; r1 < r; r1++) {
3801 debug(("%s%d", r1 ? "," : "", keys[r1]));
3810 * Interrupt an ongoing paste. I'm not sure this is
3811 * sensible, but for the moment it's preferable to
3812 * having to faff about buffering things.
3817 for (i = 0; i < r; i++) {
3818 unsigned char ch = (unsigned char) keys[i];
3820 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3825 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3829 if ((nc = check_compose(compose_char, ch)) == -1) {
3830 MessageBeep(MB_ICONHAND);
3834 term_seen_key_event(term);
3835 luni_send(ldisc, &keybuf, 1, 1);
3843 if (in_utf(term) || ucsdata.dbcs_screenfont) {
3845 term_seen_key_event(term);
3846 luni_send(ldisc, &keybuf, 1, 1);
3848 ch = (char) alt_sum;
3850 * We need not bother about stdin
3851 * backlogs here, because in GUI PuTTY
3852 * we can't do anything about it
3853 * anyway; there's no means of asking
3854 * Windows to hold off on KEYDOWN
3855 * messages. We _have_ to buffer
3856 * everything we're sent.
3858 term_seen_key_event(term);
3859 ldisc_send(ldisc, &ch, 1, 1);
3863 term_seen_key_event(term);
3864 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
3866 if(capsOn && ch < 0x80) {
3869 cbuf[1] = xlat_uskbd2cyrllic(ch);
3870 term_seen_key_event(term);
3871 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
3876 term_seen_key_event(term);
3877 lpage_send(ldisc, kbd_codepage,
3878 cbuf+!left_alt, 1+!!left_alt, 1);
3884 /* This is so the ALT-Numpad and dead keys work correctly. */
3889 /* If we're definitly not building up an ALT-54321 then clear it */
3892 /* If we will be using alt_sum fix the 256s */
3893 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
3898 * ALT alone may or may not want to bring up the System menu.
3899 * If it's not meant to, we return 0 on presses or releases of
3900 * ALT, to show that we've swallowed the keystroke. Otherwise
3901 * we return -1, which means Windows will give the keystroke
3902 * its default handling (i.e. bring up the System menu).
3904 if (wParam == VK_MENU && !cfg.alt_only)
3910 void request_paste(void *frontend)
3913 * In Windows, pasting is synchronous: we can read the
3914 * clipboard with no difficulty, so request_paste() can just go
3917 term_do_paste(term);
3920 void set_title(void *frontend, char *title)
3923 window_name = smalloc(1 + strlen(title));
3924 strcpy(window_name, title);
3925 if (cfg.win_name_always || !IsIconic(hwnd))
3926 SetWindowText(hwnd, title);
3929 void set_icon(void *frontend, char *title)
3932 icon_name = smalloc(1 + strlen(title));
3933 strcpy(icon_name, title);
3934 if (!cfg.win_name_always && IsIconic(hwnd))
3935 SetWindowText(hwnd, title);
3938 void set_sbar(void *frontend, int total, int start, int page)
3942 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3945 si.cbSize = sizeof(si);
3946 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3948 si.nMax = total - 1;
3952 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3955 Context get_ctx(void *frontend)
3961 SelectPalette(hdc, pal, FALSE);
3967 void free_ctx(Context ctx)
3969 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3970 ReleaseDC(hwnd, ctx);
3973 static void real_palette_set(int n, int r, int g, int b)
3976 logpal->palPalEntry[n].peRed = r;
3977 logpal->palPalEntry[n].peGreen = g;
3978 logpal->palPalEntry[n].peBlue = b;
3979 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3980 colours[n] = PALETTERGB(r, g, b);
3981 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3983 colours[n] = RGB(r, g, b);
3986 void palette_set(void *frontend, int n, int r, int g, int b)
3988 static const int first[21] = {
3989 0, 2, 4, 6, 8, 10, 12, 14,
3990 1, 3, 5, 7, 9, 11, 13, 15,
3993 real_palette_set(first[n], r, g, b);
3995 real_palette_set(first[n] + 1, r, g, b);
3997 HDC hdc = get_ctx(frontend);
3998 UnrealizeObject(pal);
3999 RealizePalette(hdc);
4004 void palette_reset(void *frontend)
4008 for (i = 0; i < NCOLOURS; i++) {
4010 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4011 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4012 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4013 logpal->palPalEntry[i].peFlags = 0;
4014 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4015 defpal[i].rgbtGreen,
4016 defpal[i].rgbtBlue);
4018 colours[i] = RGB(defpal[i].rgbtRed,
4019 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
4024 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
4025 hdc = get_ctx(frontend);
4026 RealizePalette(hdc);
4031 void write_aclip(void *frontend, char *data, int len, int must_deselect)
4036 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
4039 lock = GlobalLock(clipdata);
4042 memcpy(lock, data, len);
4043 ((unsigned char *) lock)[len] = 0;
4044 GlobalUnlock(clipdata);
4047 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4049 if (OpenClipboard(hwnd)) {
4051 SetClipboardData(CF_TEXT, clipdata);
4054 GlobalFree(clipdata);
4057 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4061 * Note: unlike write_aclip() this will not append a nul.
4063 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4065 HGLOBAL clipdata, clipdata2, clipdata3;
4067 void *lock, *lock2, *lock3;
4069 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4071 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4072 len * sizeof(wchar_t));
4073 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4075 if (!clipdata || !clipdata2) {
4077 GlobalFree(clipdata);
4079 GlobalFree(clipdata2);
4082 if (!(lock = GlobalLock(clipdata)))
4084 if (!(lock2 = GlobalLock(clipdata2)))
4087 memcpy(lock, data, len * sizeof(wchar_t));
4088 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4090 if (cfg.rtf_paste) {
4091 wchar_t unitab[256];
4093 unsigned char *tdata = (unsigned char *)lock2;
4094 wchar_t *udata = (wchar_t *)lock;
4095 int rtflen = 0, uindex = 0, tindex = 0;
4097 int multilen, blen, alen, totallen, i;
4098 char before[16], after[4];
4100 get_unitab(CP_ACP, unitab, 0);
4102 rtfsize = 100 + strlen(cfg.font.name);
4103 rtf = smalloc(rtfsize);
4104 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4105 GetACP(), cfg.font.name);
4106 rtflen = strlen(rtf);
4109 * We want to construct a piece of RTF that specifies the
4110 * same Unicode text. To do this we will read back in
4111 * parallel from the Unicode data in `udata' and the
4112 * non-Unicode data in `tdata'. For each character in
4113 * `tdata' which becomes the right thing in `udata' when
4114 * looked up in `unitab', we just copy straight over from
4115 * tdata. For each one that doesn't, we must WCToMB it
4116 * individually and produce a \u escape sequence.
4118 * It would probably be more robust to just bite the bullet
4119 * and WCToMB each individual Unicode character one by one,
4120 * then MBToWC each one back to see if it was an accurate
4121 * translation; but that strikes me as a horrifying number
4122 * of Windows API calls so I want to see if this faster way
4123 * will work. If it screws up badly we can always revert to
4124 * the simple and slow way.
4126 while (tindex < len2 && uindex < len &&
4127 tdata[tindex] && udata[uindex]) {
4128 if (tindex + 1 < len2 &&
4129 tdata[tindex] == '\r' &&
4130 tdata[tindex+1] == '\n') {
4134 if (unitab[tdata[tindex]] == udata[uindex]) {
4140 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4141 NULL, 0, NULL, NULL);
4142 if (multilen != 1) {
4143 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4145 alen = 1; strcpy(after, "}");
4147 blen = sprintf(before, "\\u%d", udata[uindex]);
4148 alen = 0; after[0] = '\0';
4151 assert(tindex + multilen <= len2);
4152 totallen = blen + alen;
4153 for (i = 0; i < multilen; i++) {
4154 if (tdata[tindex+i] == '\\' ||
4155 tdata[tindex+i] == '{' ||
4156 tdata[tindex+i] == '}')
4158 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4159 totallen += 6; /* \par\r\n */
4160 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4166 if (rtfsize < rtflen + totallen + 3) {
4167 rtfsize = rtflen + totallen + 512;
4168 rtf = srealloc(rtf, rtfsize);
4171 strcpy(rtf + rtflen, before); rtflen += blen;
4172 for (i = 0; i < multilen; i++) {
4173 if (tdata[tindex+i] == '\\' ||
4174 tdata[tindex+i] == '{' ||
4175 tdata[tindex+i] == '}') {
4176 rtf[rtflen++] = '\\';
4177 rtf[rtflen++] = tdata[tindex+i];
4178 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4179 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4180 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4181 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4183 rtf[rtflen++] = tdata[tindex+i];
4186 strcpy(rtf + rtflen, after); rtflen += alen;
4192 strcpy(rtf + rtflen, "}");
4195 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4196 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4198 GlobalUnlock(clipdata3);
4204 GlobalUnlock(clipdata);
4205 GlobalUnlock(clipdata2);
4208 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4210 if (OpenClipboard(hwnd)) {
4212 SetClipboardData(CF_UNICODETEXT, clipdata);
4213 SetClipboardData(CF_TEXT, clipdata2);
4215 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4218 GlobalFree(clipdata);
4219 GlobalFree(clipdata2);
4223 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4226 void get_clip(void *frontend, wchar_t ** p, int *len)
4228 static HGLOBAL clipdata = NULL;
4229 static wchar_t *converted = 0;
4238 GlobalUnlock(clipdata);
4241 } else if (OpenClipboard(NULL)) {
4242 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4244 *p = GlobalLock(clipdata);
4246 for (p2 = *p; *p2; p2++);
4250 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4254 s = GlobalLock(clipdata);
4255 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4256 *p = converted = smalloc(i * sizeof(wchar_t));
4257 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4270 * Move `lines' lines from position `from' to position `to' in the
4273 void optimised_move(void *frontend, int to, int from, int lines)
4278 min = (to < from ? to : from);
4279 max = to + from - min;
4281 r.left = offset_width;
4282 r.right = offset_width + term->cols * font_width;
4283 r.top = offset_height + min * font_height;
4284 r.bottom = offset_height + (max + lines) * font_height;
4285 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4290 * Print a message box and perform a fatal exit.
4292 void fatalbox(char *fmt, ...)
4298 vsprintf(stuff, fmt, ap);
4300 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4305 * Print a modal (Really Bad) message box and perform a fatal exit.
4307 void modalfatalbox(char *fmt, ...)
4313 vsprintf(stuff, fmt, ap);
4315 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4316 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4321 * Manage window caption / taskbar flashing, if enabled.
4322 * 0 = stop, 1 = maintain, 2 = start
4324 static void flash_window(int mode)
4326 static long last_flash = 0;
4327 static int flashing = 0;
4328 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4331 FlashWindow(hwnd, FALSE);
4335 } else if (mode == 2) {
4338 last_flash = GetTickCount();
4340 FlashWindow(hwnd, TRUE);
4343 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4346 long now = GetTickCount();
4347 long fdiff = now - last_flash;
4348 if (fdiff < 0 || fdiff > 450) {
4350 FlashWindow(hwnd, TRUE); /* toggle */
4359 void beep(void *frontend, int mode)
4361 if (mode == BELL_DEFAULT) {
4363 * For MessageBeep style bells, we want to be careful of
4364 * timing, because they don't have the nice property of
4365 * PlaySound bells that each one cancels the previous
4366 * active one. So we limit the rate to one per 50ms or so.
4368 static long lastbeep = 0;
4371 beepdiff = GetTickCount() - lastbeep;
4372 if (beepdiff >= 0 && beepdiff < 50)
4376 * The above MessageBeep call takes time, so we record the
4377 * time _after_ it finishes rather than before it starts.
4379 lastbeep = GetTickCount();
4380 } else if (mode == BELL_WAVEFILE) {
4381 if (!PlaySound(cfg.bell_wavefile.path, NULL,
4382 SND_ASYNC | SND_FILENAME)) {
4383 char buf[sizeof(cfg.bell_wavefile) + 80];
4384 sprintf(buf, "Unable to play sound file\n%s\n"
4385 "Using default sound instead", cfg.bell_wavefile);
4386 MessageBox(hwnd, buf, "PuTTY Sound Error",
4387 MB_OK | MB_ICONEXCLAMATION);
4388 cfg.beep = BELL_DEFAULT;
4391 /* Otherwise, either visual bell or disabled; do nothing here */
4392 if (!term->has_focus) {
4393 flash_window(2); /* start */
4398 * Minimise or restore the window in response to a server-side
4401 void set_iconic(void *frontend, int iconic)
4403 if (IsIconic(hwnd)) {
4405 ShowWindow(hwnd, SW_RESTORE);
4408 ShowWindow(hwnd, SW_MINIMIZE);
4413 * Move the window in response to a server-side request.
4415 void move_window(void *frontend, int x, int y)
4417 if (cfg.resize_action == RESIZE_DISABLED ||
4418 cfg.resize_action == RESIZE_FONT ||
4422 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4426 * Move the window to the top or bottom of the z-order in response
4427 * to a server-side request.
4429 void set_zorder(void *frontend, int top)
4431 if (cfg.alwaysontop)
4432 return; /* ignore */
4433 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4434 SWP_NOMOVE | SWP_NOSIZE);
4438 * Refresh the window in response to a server-side request.
4440 void refresh_window(void *frontend)
4442 InvalidateRect(hwnd, NULL, TRUE);
4446 * Maximise or restore the window in response to a server-side
4449 void set_zoomed(void *frontend, int zoomed)
4451 if (IsZoomed(hwnd)) {
4453 ShowWindow(hwnd, SW_RESTORE);
4456 ShowWindow(hwnd, SW_MAXIMIZE);
4461 * Report whether the window is iconic, for terminal reports.
4463 int is_iconic(void *frontend)
4465 return IsIconic(hwnd);
4469 * Report the window's position, for terminal reports.
4471 void get_window_pos(void *frontend, int *x, int *y)
4474 GetWindowRect(hwnd, &r);
4480 * Report the window's pixel size, for terminal reports.
4482 void get_window_pixels(void *frontend, int *x, int *y)
4485 GetWindowRect(hwnd, &r);
4486 *x = r.right - r.left;
4487 *y = r.bottom - r.top;
4491 * Return the window or icon title.
4493 char *get_window_title(void *frontend, int icon)
4495 return icon ? icon_name : window_name;
4499 * See if we're in full-screen mode.
4501 int is_full_screen()
4503 if (!IsZoomed(hwnd))
4505 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4510 /* Get the rect/size of a full screen window using the nearest available
4511 * monitor in multimon systems; default to something sensible if only
4512 * one monitor is present. */
4513 static int get_fullscreen_rect(RECT * ss)
4515 #ifdef MONITOR_DEFAULTTONEAREST
4518 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4519 mi.cbSize = sizeof(mi);
4520 GetMonitorInfo(mon, &mi);
4522 /* structure copy */
4526 /* could also use code like this:
4527 ss->left = ss->top = 0;
4528 ss->right = GetSystemMetrics(SM_CXSCREEN);
4529 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4531 return GetClientRect(GetDesktopWindow(), ss);
4537 * Go full-screen. This should only be called when we are already
4540 void make_full_screen()
4545 assert(IsZoomed(hwnd));
4547 if (is_full_screen())
4550 /* Remove the window furniture. */
4551 style = GetWindowLong(hwnd, GWL_STYLE);
4552 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4553 if (cfg.scrollbar_in_fullscreen)
4554 style |= WS_VSCROLL;
4556 style &= ~WS_VSCROLL;
4557 SetWindowLong(hwnd, GWL_STYLE, style);
4559 /* Resize ourselves to exactly cover the nearest monitor. */
4560 get_fullscreen_rect(&ss);
4561 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4566 /* Tick the menu item in the System menu. */
4567 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4572 * Clear the full-screen attributes.
4574 void clear_full_screen()
4576 DWORD oldstyle, style;
4578 /* Reinstate the window furniture. */
4579 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4580 style |= WS_CAPTION | WS_BORDER;
4581 if (cfg.resize_action == RESIZE_DISABLED)
4582 style &= ~WS_THICKFRAME;
4584 style |= WS_THICKFRAME;
4586 style |= WS_VSCROLL;
4588 style &= ~WS_VSCROLL;
4589 if (style != oldstyle) {
4590 SetWindowLong(hwnd, GWL_STYLE, style);
4591 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4592 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4596 /* Untick the menu item in the System menu. */
4597 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4602 * Toggle full-screen mode.
4604 void flip_full_screen()
4606 if (is_full_screen()) {
4607 ShowWindow(hwnd, SW_RESTORE);
4608 } else if (IsZoomed(hwnd)) {
4611 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4612 ShowWindow(hwnd, SW_MAXIMIZE);
4616 void frontend_keypress(void *handle)
4619 * Keypress termination in non-Close-On-Exit mode is not
4620 * currently supported in PuTTY proper, because the window
4621 * always has a perfectly good Close button anyway. So we do