16 #define COMPILE_MULTIMON_STUBS
27 #define PUTTY_DO_GLOBALS /* actually _define_ globals */
34 #define IDM_SHOWLOG 0x0010
35 #define IDM_NEWSESS 0x0020
36 #define IDM_DUPSESS 0x0030
37 #define IDM_RECONF 0x0040
38 #define IDM_CLRSB 0x0050
39 #define IDM_RESET 0x0060
40 #define IDM_TEL_AYT 0x0070
41 #define IDM_TEL_BRK 0x0080
42 #define IDM_TEL_SYNCH 0x0090
43 #define IDM_TEL_EC 0x00a0
44 #define IDM_TEL_EL 0x00b0
45 #define IDM_TEL_GA 0x00c0
46 #define IDM_TEL_NOP 0x00d0
47 #define IDM_TEL_ABORT 0x00e0
48 #define IDM_TEL_AO 0x00f0
49 #define IDM_TEL_IP 0x0100
50 #define IDM_TEL_SUSP 0x0110
51 #define IDM_TEL_EOR 0x0120
52 #define IDM_TEL_EOF 0x0130
53 #define IDM_HELP 0x0140
54 #define IDM_ABOUT 0x0150
55 #define IDM_SAVEDSESS 0x0160
56 #define IDM_COPYALL 0x0170
57 #define IDM_FULLSCREEN 0x0180
59 #define IDM_SESSLGP 0x0250 /* log type printable */
60 #define IDM_SESSLGA 0x0260 /* log type all chars */
61 #define IDM_SESSLGE 0x0270 /* log end */
62 #define IDM_SAVED_MIN 0x1000
63 #define IDM_SAVED_MAX 0x2000
65 #define WM_IGNORE_CLIP (WM_XUSER + 2)
66 #define WM_FULLSCR_ON_MAX (WM_XUSER + 3)
68 /* Needed for Chinese support and apparently not always defined. */
70 #define VK_PROCESSKEY 0xE5
73 /* Mouse wheel support. */
75 #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */
78 #define WHEEL_DELTA 120
81 static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
82 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
83 unsigned char *output);
84 static void cfgtopalette(void);
85 static void init_palette(void);
86 static void init_fonts(int, int);
87 static void another_font(int);
88 static void deinit_fonts(void);
89 static void set_input_locale(HKL);
91 static int is_full_screen(void);
92 static void make_full_screen(void);
93 static void clear_full_screen(void);
94 static void flip_full_screen(void);
96 /* Window layout information */
97 static void reset_window(int);
98 static int extra_width, extra_height;
99 static int font_width, font_height, font_dualwidth;
100 static int offset_width, offset_height;
101 static int was_zoomed = 0;
102 static int prev_rows, prev_cols;
104 static int pending_netevent = 0;
105 static WPARAM pend_netevent_wParam = 0;
106 static LPARAM pend_netevent_lParam = 0;
107 static void enact_pending_netevent(void);
108 static void flash_window(int mode);
109 static void sys_cursor_update(void);
110 static int is_shift_pressed(void);
111 static int get_fullscreen_rect(RECT * ss);
113 static time_t last_movement = 0;
115 static int caret_x = -1, caret_y = -1;
117 static int kbd_codepage;
120 static Backend *back;
121 static void *backhandle;
123 static struct unicode_data ucsdata;
124 static int session_closed;
126 Config cfg; /* exported to windlg.c */
128 extern struct sesslist sesslist; /* imported from windlg.c */
130 #define FONT_NORMAL 0
132 #define FONT_UNDERLINE 2
133 #define FONT_BOLDUND 3
134 #define FONT_WIDE 0x04
135 #define FONT_HIGH 0x08
136 #define FONT_NARROW 0x10
138 #define FONT_OEM 0x20
139 #define FONT_OEMBOLD 0x21
140 #define FONT_OEMUND 0x22
141 #define FONT_OEMBOLDUND 0x23
143 #define FONT_MAXNO 0x2F
145 static HFONT fonts[FONT_MAXNO];
146 static LOGFONT lfont;
147 static int fontflag[FONT_MAXNO];
149 BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
157 static COLORREF colours[NCOLOURS];
159 static LPLOGPALETTE logpal;
160 static RGBTRIPLE defpal[NCOLOURS];
164 static HBITMAP caretbm;
166 static int dbltime, lasttime, lastact;
167 static Mouse_Button lastbtn;
169 /* this allows xterm-style mouse handling. */
170 static int send_raw_mouse = 0;
171 static int wheel_accumulator = 0;
173 static char *window_name, *icon_name;
175 static int compose_state = 0;
177 static int wsa_started = 0;
179 static OSVERSIONINFO osVersion;
181 static UINT wm_mousewheel = WM_MOUSEWHEEL;
183 /* Dummy routine, only required in plink. */
184 void ldisc_update(void *frontend, int echo, int edit)
188 int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
190 static char appname[] = "PuTTY";
195 int guess_width, guess_height;
198 flags = FLAG_VERBOSE | FLAG_INTERACTIVE;
200 winsock_ver = MAKEWORD(1, 1);
201 if (WSAStartup(winsock_ver, &wsadata)) {
202 MessageBox(NULL, "Unable to initialise WinSock", "WinSock Error",
203 MB_OK | MB_ICONEXCLAMATION);
206 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
207 MessageBox(NULL, "WinSock version is incompatible with 1.1",
208 "WinSock Error", MB_OK | MB_ICONEXCLAMATION);
213 /* WISHLIST: maybe allow config tweaking even if winsock not present? */
216 InitCommonControls();
218 /* Ensure a Maximize setting in Explorer doesn't maximise the
223 ZeroMemory(&osVersion, sizeof(osVersion));
224 osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
225 if (!GetVersionEx ( (OSVERSIONINFO *) &osVersion)) {
226 MessageBox(NULL, "Windows refuses to report a version",
227 "PuTTY Fatal Error", MB_OK | MB_ICONEXCLAMATION);
233 * If we're running a version of Windows that doesn't support
234 * WM_MOUSEWHEEL, find out what message number we should be
237 if (osVersion.dwMajorVersion < 4 ||
238 (osVersion.dwMajorVersion == 4 &&
239 osVersion.dwPlatformId != VER_PLATFORM_WIN32_NT))
240 wm_mousewheel = RegisterWindowMessage("MSWHEEL_ROLLMSG");
243 * See if we can find our Help file.
246 char b[2048], *p, *q, *r;
248 GetModuleFileName(NULL, b, sizeof(b) - 1);
250 p = strrchr(b, '\\');
251 if (p && p >= r) r = p+1;
253 if (q && q >= r) r = q+1;
254 strcpy(r, "putty.hlp");
255 if ( (fp = fopen(b, "r")) != NULL) {
256 help_path = dupstr(b);
260 strcpy(r, "putty.cnt");
261 if ( (fp = fopen(b, "r")) != NULL) {
262 help_has_contents = TRUE;
265 help_has_contents = FALSE;
269 * Process the command line.
275 default_protocol = be_default_protocol;
276 /* Find the appropriate default port. */
278 default_port = 0; /* illegal */
280 for (i = 0; backends[i].backend != NULL; i++)
281 if (backends[i].protocol == default_protocol) {
282 default_port = backends[i].backend->default_port;
286 cfg.logtype = LGTYP_NONE;
288 do_defaults(NULL, &cfg);
293 * Process a couple of command-line options which are more
294 * easily dealt with before the line is broken up into
295 * words. These are the soon-to-be-defunct @sessionname and
296 * the internal-use-only &sharedmemoryhandle, neither of
297 * which are combined with anything else.
299 while (*p && isspace(*p))
303 while (i > 1 && isspace(p[i - 1]))
306 do_defaults(p + 1, &cfg);
307 if (!*cfg.host && !do_config()) {
311 } else if (*p == '&') {
313 * An initial & means we've been given a command line
314 * containing the hex value of a HANDLE for a file
315 * mapping object, which we must then extract as a
320 if (sscanf(p + 1, "%p", &filemap) == 1 &&
321 (cp = MapViewOfFile(filemap, FILE_MAP_READ,
322 0, 0, sizeof(Config))) != NULL) {
325 CloseHandle(filemap);
326 } else if (!do_config()) {
332 * Otherwise, break up the command line and deal with
338 split_into_argv(cmdline, &argc, &argv, NULL);
340 for (i = 0; i < argc; i++) {
344 ret = cmdline_process_param(p, i+1<argc?argv[i+1]:NULL,
347 cmdline_error("option \"%s\" requires an argument", p);
348 } else if (ret == 2) {
349 i++; /* skip next argument */
350 } else if (ret == 1) {
351 continue; /* nothing further needs doing */
352 } else if (!strcmp(p, "-cleanup")) {
354 * `putty -cleanup'. Remove all registry
355 * entries associated with PuTTY, and also find
356 * and delete the random seed file.
359 "This procedure will remove ALL Registry\n"
360 "entries associated with PuTTY, and will\n"
361 "also remove the PuTTY random seed file.\n"
363 "THIS PROCESS WILL DESTROY YOUR SAVED\n"
364 "SESSIONS. Are you really sure you want\n"
367 MB_YESNO | MB_ICONWARNING) == IDYES) {
371 } else if (*p != '-') {
375 * If we already have a host name, treat
376 * this argument as a port number. NB we
377 * have to treat this as a saved -P
378 * argument, so that it will be deferred
379 * until it's a good moment to run it.
381 int ret = cmdline_process_param("-P", p, 1, &cfg);
383 } else if (!strncmp(q, "telnet:", 7)) {
385 * If the hostname starts with "telnet:",
386 * set the protocol to Telnet and process
387 * the string as a Telnet URL.
392 if (q[0] == '/' && q[1] == '/')
394 cfg.protocol = PROT_TELNET;
396 while (*p && *p != ':' && *p != '/')
405 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
406 cfg.host[sizeof(cfg.host) - 1] = '\0';
410 * Otherwise, treat this argument as a host
413 while (*p && !isspace(*p))
417 strncpy(cfg.host, q, sizeof(cfg.host) - 1);
418 cfg.host[sizeof(cfg.host) - 1] = '\0';
422 cmdline_error("unknown option \"%s\"", p);
427 cmdline_run_saved(&cfg);
429 if (!*cfg.host && !do_config()) {
435 * Trim leading whitespace off the hostname if it's there.
438 int space = strspn(cfg.host, " \t");
439 memmove(cfg.host, cfg.host+space, 1+strlen(cfg.host)-space);
442 /* See if host is of the form user@host */
443 if (cfg.host[0] != '\0') {
444 char *atsign = strchr(cfg.host, '@');
445 /* Make sure we're not overflowing the user field */
447 if (atsign - cfg.host < sizeof cfg.username) {
448 strncpy(cfg.username, cfg.host, atsign - cfg.host);
449 cfg.username[atsign - cfg.host] = '\0';
451 memmove(cfg.host, atsign + 1, 1 + strlen(atsign + 1));
456 * Trim a colon suffix off the hostname if it's there.
458 cfg.host[strcspn(cfg.host, ":")] = '\0';
461 * Remove any remaining whitespace from the hostname.
465 while (cfg.host[p2] != '\0') {
466 if (cfg.host[p2] != ' ' && cfg.host[p2] != '\t') {
467 cfg.host[p1] = cfg.host[p2];
477 * Select protocol. This is farmed out into a table in a
478 * separate file to enable an ssh-free variant.
483 for (i = 0; backends[i].backend != NULL; i++)
484 if (backends[i].protocol == cfg.protocol) {
485 back = backends[i].backend;
489 MessageBox(NULL, "Unsupported protocol number found",
490 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
496 /* Check for invalid Port number (i.e. zero) */
498 MessageBox(NULL, "Invalid Port Number",
499 "PuTTY Internal Error", MB_OK | MB_ICONEXCLAMATION);
506 wndclass.lpfnWndProc = WndProc;
507 wndclass.cbClsExtra = 0;
508 wndclass.cbWndExtra = 0;
509 wndclass.hInstance = inst;
510 wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
511 wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
512 wndclass.hbrBackground = NULL;
513 wndclass.lpszMenuName = NULL;
514 wndclass.lpszClassName = appname;
516 RegisterClass(&wndclass);
521 memset(&ucsdata, 0, sizeof(ucsdata));
523 term = term_init(&cfg, &ucsdata, NULL);
524 logctx = log_init(NULL, &cfg);
525 term_provide_logctx(term, logctx);
530 * Guess some defaults for the window size. This all gets
531 * updated later, so we don't really care too much. However, we
532 * do want the font width/height guesses to correspond to a
533 * large font rather than a small one...
540 term_size(term, cfg.height, cfg.width, cfg.savelines);
541 guess_width = extra_width + font_width * term->cols;
542 guess_height = extra_height + font_height * term->rows;
545 get_fullscreen_rect(&r);
546 if (guess_width > r.right - r.left)
547 guess_width = r.right - r.left;
548 if (guess_height > r.bottom - r.top)
549 guess_height = r.bottom - r.top;
553 int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
556 winmode &= ~(WS_VSCROLL);
557 if (cfg.resize_action == RESIZE_DISABLED)
558 winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
560 exwinmode |= WS_EX_TOPMOST;
562 exwinmode |= WS_EX_CLIENTEDGE;
563 hwnd = CreateWindowEx(exwinmode, appname, appname,
564 winmode, CW_USEDEFAULT, CW_USEDEFAULT,
565 guess_width, guess_height,
566 NULL, NULL, inst, NULL);
570 * Initialise the fonts, simultaneously correcting the guesses
571 * for font_{width,height}.
576 * Correct the guesses for extra_{width,height}.
580 GetWindowRect(hwnd, &wr);
581 GetClientRect(hwnd, &cr);
582 offset_width = offset_height = cfg.window_border;
583 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
584 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
588 * Resize the window, now we know what size we _really_ want it
591 guess_width = extra_width + font_width * term->cols;
592 guess_height = extra_height + font_height * term->rows;
593 SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height,
594 SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER);
597 * Set up a caret bitmap, with no content.
601 int size = (font_width + 15) / 16 * 2 * font_height;
602 bits = smalloc(size);
603 memset(bits, 0, size);
604 caretbm = CreateBitmap(font_width, font_height, 1, 1, bits);
607 CreateCaret(hwnd, caretbm, font_width, font_height);
610 * Initialise the scroll bar.
615 si.cbSize = sizeof(si);
616 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
618 si.nMax = term->rows - 1;
619 si.nPage = term->rows;
621 SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
625 * Start up the telnet connection.
629 char msg[1024], *title;
632 error = back->init((void *)term, &backhandle, &cfg,
633 cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
634 back->provide_logctx(backhandle, logctx);
636 sprintf(msg, "Unable to open connection to\n"
637 "%.800s\n" "%s", cfg.host, error);
638 MessageBox(NULL, msg, "PuTTY Error", MB_ICONERROR | MB_OK);
641 window_name = icon_name = NULL;
643 title = cfg.wintitle;
645 sprintf(msg, "%s - PuTTY", realhost);
649 set_title(NULL, title);
650 set_icon(NULL, title);
654 * Connect the terminal to the backend for resize purposes.
656 term_provide_resize_fn(term, back->size, backhandle);
659 * Set up a line discipline.
661 ldisc = ldisc_create(&cfg, term, back, backhandle, NULL);
663 session_closed = FALSE;
666 * Prepare the mouse handler.
668 lastact = MA_NOTHING;
669 lastbtn = MBT_NOTHING;
670 dbltime = GetDoubleClickTime();
673 * Set up the session-control options on the system menu.
676 HMENU m = GetSystemMenu(hwnd, FALSE);
680 AppendMenu(m, MF_SEPARATOR, 0, 0);
681 if (cfg.protocol == PROT_TELNET) {
683 AppendMenu(p, MF_ENABLED, IDM_TEL_AYT, "Are You There");
684 AppendMenu(p, MF_ENABLED, IDM_TEL_BRK, "Break");
685 AppendMenu(p, MF_ENABLED, IDM_TEL_SYNCH, "Synch");
686 AppendMenu(p, MF_SEPARATOR, 0, 0);
687 AppendMenu(p, MF_ENABLED, IDM_TEL_EC, "Erase Character");
688 AppendMenu(p, MF_ENABLED, IDM_TEL_EL, "Erase Line");
689 AppendMenu(p, MF_ENABLED, IDM_TEL_GA, "Go Ahead");
690 AppendMenu(p, MF_ENABLED, IDM_TEL_NOP, "No Operation");
691 AppendMenu(p, MF_SEPARATOR, 0, 0);
692 AppendMenu(p, MF_ENABLED, IDM_TEL_ABORT, "Abort Process");
693 AppendMenu(p, MF_ENABLED, IDM_TEL_AO, "Abort Output");
694 AppendMenu(p, MF_ENABLED, IDM_TEL_IP, "Interrupt Process");
695 AppendMenu(p, MF_ENABLED, IDM_TEL_SUSP, "Suspend Process");
696 AppendMenu(p, MF_SEPARATOR, 0, 0);
697 AppendMenu(p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
698 AppendMenu(p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
699 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) p,
701 AppendMenu(m, MF_SEPARATOR, 0, 0);
703 AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log");
704 AppendMenu(m, MF_SEPARATOR, 0, 0);
705 AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session...");
706 AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session");
708 get_sesslist(&sesslist, TRUE);
710 i < ((sesslist.nsessions < 256) ? sesslist.nsessions : 256);
712 AppendMenu(s, MF_ENABLED, IDM_SAVED_MIN + (16 * i),
713 sesslist.sessions[i]);
714 AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT) s, "Sa&ved Sessions");
715 AppendMenu(m, MF_ENABLED, IDM_RECONF, "Chan&ge Settings...");
716 AppendMenu(m, MF_SEPARATOR, 0, 0);
717 AppendMenu(m, MF_ENABLED, IDM_COPYALL, "C&opy All to Clipboard");
718 AppendMenu(m, MF_ENABLED, IDM_CLRSB, "C&lear Scrollback");
719 AppendMenu(m, MF_ENABLED, IDM_RESET, "Rese&t Terminal");
720 AppendMenu(m, MF_SEPARATOR, 0, 0);
721 AppendMenu(m, (cfg.resize_action == RESIZE_DISABLED) ?
722 MF_GRAYED : MF_ENABLED, IDM_FULLSCREEN, "&Full Screen");
723 AppendMenu(m, MF_SEPARATOR, 0, 0);
725 AppendMenu(m, MF_ENABLED, IDM_HELP, "&Help");
726 AppendMenu(m, MF_ENABLED, IDM_ABOUT, "&About PuTTY");
730 * Set up the initial input locale.
732 set_input_locale(GetKeyboardLayout(0));
735 * Open the initial log file if there is one.
740 * Finally show the window!
742 ShowWindow(hwnd, show);
743 SetForegroundWindow(hwnd);
746 * Set the palette up.
752 term->has_focus = (GetForegroundWindow() == hwnd);
755 if (GetMessage(&msg, NULL, 0, 0) == 1) {
756 int timer_id = 0, long_timer = 0;
758 while (msg.message != WM_QUIT) {
759 /* Sometimes DispatchMessage calls routines that use their own
760 * GetMessage loop, setup this timer so we get some control back.
762 * Also call term_update() from the timer so that if the host
763 * is sending data flat out we still do redraws.
765 if (timer_id && long_timer) {
766 KillTimer(hwnd, timer_id);
767 long_timer = timer_id = 0;
770 timer_id = SetTimer(hwnd, 1, 20, NULL);
771 if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
772 DispatchMessage(&msg);
774 /* Make sure we blink everything that needs it. */
777 /* Send the paste buffer if there's anything to send */
780 /* If there's nothing new in the queue then we can do everything
781 * we've delayed, reading the socket, writing, and repainting
784 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
787 if (pending_netevent) {
788 enact_pending_netevent();
790 /* Force the cursor blink on */
793 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
797 /* Okay there is now nothing to do so we make sure the screen is
798 * completely up to date then tell windows to call us in a little
802 KillTimer(hwnd, timer_id);
806 if (GetCapture() != hwnd ||
808 !(cfg.mouse_override && is_shift_pressed())))
813 flash_window(1); /* maintain */
815 /* The messages seem unreliable; especially if we're being tricky */
816 term->has_focus = (GetForegroundWindow() == hwnd);
819 /* Hmm, term_update didn't want to do an update too soon ... */
820 timer_id = SetTimer(hwnd, 1, 50, NULL);
821 else if (!term->has_focus)
822 timer_id = SetTimer(hwnd, 1, 500, NULL);
824 timer_id = SetTimer(hwnd, 1, 100, NULL);
827 /* There's no point rescanning everything in the message queue
828 * so we do an apparently unnecessary wait here
831 if (GetMessage(&msg, NULL, 0, 0) != 1)
836 cleanup_exit(msg.wParam); /* this doesn't return... */
837 return msg.wParam; /* ... but optimiser doesn't know */
843 void cleanup_exit(int code)
856 if (cfg.protocol == PROT_SSH) {
867 * Set up, or shut down, an AsyncSelect. Called from winnet.c.
869 char *do_select(SOCKET skt, int startup)
874 events = (FD_CONNECT | FD_READ | FD_WRITE |
875 FD_OOB | FD_CLOSE | FD_ACCEPT);
880 return "do_select(): internal error (hwnd==NULL)";
881 if (WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) {
882 switch (WSAGetLastError()) {
884 return "Network is down";
886 return "WSAAsyncSelect(): unknown error";
893 * set or clear the "raw mouse message" mode
895 void set_raw_mouse_mode(void *frontend, int activate)
897 activate = activate && !cfg.no_mouse_rep;
898 send_raw_mouse = activate;
899 SetCursor(LoadCursor(NULL, activate ? IDC_ARROW : IDC_IBEAM));
903 * Print a message box and close the connection.
905 void connection_fatal(void *frontend, char *fmt, ...)
911 vsprintf(stuff, fmt, ap);
913 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
914 if (cfg.close_on_exit == COE_ALWAYS)
917 session_closed = TRUE;
918 set_icon(NULL, "PuTTY (inactive)");
919 set_title(NULL, "PuTTY (inactive)");
924 * Report an error at the command-line parsing stage.
926 void cmdline_error(char *fmt, ...)
932 vsprintf(stuff, fmt, ap);
934 MessageBox(hwnd, stuff, "PuTTY Command Line Error", MB_ICONERROR | MB_OK);
939 * Actually do the job requested by a WM_NETEVENT
941 static void enact_pending_netevent(void)
943 static int reentering = 0;
944 extern int select_result(WPARAM, LPARAM);
948 return; /* don't unpend the pending */
950 pending_netevent = FALSE;
953 ret = select_result(pend_netevent_wParam, pend_netevent_lParam);
956 if (ret == 0 && !session_closed) {
957 /* Abnormal exits will already have set session_closed and taken
958 * appropriate action. */
959 if (cfg.close_on_exit == COE_ALWAYS ||
960 cfg.close_on_exit == COE_NORMAL) PostQuitMessage(0);
962 session_closed = TRUE;
963 set_icon(NULL, "PuTTY (inactive)");
964 set_title(NULL, "PuTTY (inactive)");
965 MessageBox(hwnd, "Connection closed by remote host",
966 "PuTTY", MB_OK | MB_ICONINFORMATION);
972 * Copy the colour palette from the configuration data into defpal.
973 * This is non-trivial because the colour indices are different.
975 static void cfgtopalette(void)
978 static const int ww[] = {
979 6, 7, 8, 9, 10, 11, 12, 13,
980 14, 15, 16, 17, 18, 19, 20, 21,
981 0, 1, 2, 3, 4, 4, 5, 5
984 for (i = 0; i < 24; i++) {
986 defpal[i].rgbtRed = cfg.colours[w][0];
987 defpal[i].rgbtGreen = cfg.colours[w][1];
988 defpal[i].rgbtBlue = cfg.colours[w][2];
993 * Set up the colour palette.
995 static void init_palette(void)
998 HDC hdc = GetDC(hwnd);
1000 if (cfg.try_palette && GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) {
1001 logpal = smalloc(sizeof(*logpal)
1002 - sizeof(logpal->palPalEntry)
1003 + NCOLOURS * sizeof(PALETTEENTRY));
1004 logpal->palVersion = 0x300;
1005 logpal->palNumEntries = NCOLOURS;
1006 for (i = 0; i < NCOLOURS; i++) {
1007 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
1008 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
1009 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
1010 logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
1012 pal = CreatePalette(logpal);
1014 SelectPalette(hdc, pal, FALSE);
1015 RealizePalette(hdc);
1016 SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
1019 ReleaseDC(hwnd, hdc);
1022 for (i = 0; i < NCOLOURS; i++)
1023 colours[i] = PALETTERGB(defpal[i].rgbtRed,
1024 defpal[i].rgbtGreen,
1025 defpal[i].rgbtBlue);
1027 for (i = 0; i < NCOLOURS; i++)
1028 colours[i] = RGB(defpal[i].rgbtRed,
1029 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
1033 * Initialise all the fonts we will need initially. There may be as many as
1034 * three or as few as one. The other (poentially) twentyone fonts are done
1035 * if/when they are needed.
1039 * - check the font width and height, correcting our guesses if
1042 * - verify that the bold font is the same width as the ordinary
1043 * one, and engage shadow bolding if not.
1045 * - verify that the underlined font is the same width as the
1046 * ordinary one (manual underlining by means of line drawing can
1047 * be done in a pinch).
1049 static void init_fonts(int pick_width, int pick_height)
1056 int fw_dontcare, fw_bold;
1058 for (i = 0; i < FONT_MAXNO; i++)
1061 bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
1062 und_mode = UND_FONT;
1064 if (cfg.fontisbold) {
1065 fw_dontcare = FW_BOLD;
1068 fw_dontcare = FW_DONTCARE;
1075 font_height = pick_height;
1077 font_height = cfg.fontheight;
1078 if (font_height > 0) {
1080 -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1083 font_width = pick_width;
1085 #define f(i,c,w,u) \
1086 fonts[i] = CreateFont (font_height, font_width, 0, 0, w, FALSE, u, FALSE, \
1087 c, OUT_DEFAULT_PRECIS, \
1088 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
1089 FIXED_PITCH | FF_DONTCARE, cfg.font)
1091 f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
1093 lfont.lfHeight = font_height;
1094 lfont.lfWidth = font_width;
1095 lfont.lfEscapement = 0;
1096 lfont.lfOrientation = 0;
1097 lfont.lfWeight = fw_dontcare;
1098 lfont.lfItalic = FALSE;
1099 lfont.lfUnderline = FALSE;
1100 lfont.lfStrikeOut = FALSE;
1101 lfont.lfCharSet = cfg.fontcharset;
1102 lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1103 lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1104 lfont.lfQuality = DEFAULT_QUALITY;
1105 lfont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1106 strncpy(lfont.lfFaceName, cfg.font, LF_FACESIZE);
1108 SelectObject(hdc, fonts[FONT_NORMAL]);
1109 GetTextMetrics(hdc, &tm);
1111 if (pick_width == 0 || pick_height == 0) {
1112 font_height = tm.tmHeight;
1113 font_width = tm.tmAveCharWidth;
1115 font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth);
1117 #ifdef RDB_DEBUG_PATCH
1118 debug(23, "Primary font H=%d, AW=%d, MW=%d",
1119 tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth);
1124 DWORD cset = tm.tmCharSet;
1125 memset(&info, 0xFF, sizeof(info));
1127 /* !!! Yes the next line is right */
1128 if (cset == OEM_CHARSET)
1129 ucsdata.font_codepage = GetOEMCP();
1131 if (TranslateCharsetInfo ((DWORD *) cset, &info, TCI_SRCCHARSET))
1132 ucsdata.font_codepage = info.ciACP;
1134 ucsdata.font_codepage = -1;
1136 GetCPInfo(ucsdata.font_codepage, &cpinfo);
1137 ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);
1140 f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
1143 * Some fonts, e.g. 9-pt Courier, draw their underlines
1144 * outside their character cell. We successfully prevent
1145 * screen corruption by clipping the text output, but then
1146 * we lose the underline completely. Here we try to work
1147 * out whether this is such a font, and if it is, we set a
1148 * flag that causes underlines to be drawn by hand.
1150 * Having tried other more sophisticated approaches (such
1151 * as examining the TEXTMETRIC structure or requesting the
1152 * height of a string), I think we'll do this the brute
1153 * force way: we create a small bitmap, draw an underlined
1154 * space on it, and test to see whether any pixels are
1155 * foreground-coloured. (Since we expect the underline to
1156 * go all the way across the character cell, we only search
1157 * down a single column of the bitmap, half way across.)
1161 HBITMAP und_bm, und_oldbm;
1165 und_dc = CreateCompatibleDC(hdc);
1166 und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
1167 und_oldbm = SelectObject(und_dc, und_bm);
1168 SelectObject(und_dc, fonts[FONT_UNDERLINE]);
1169 SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
1170 SetTextColor(und_dc, RGB(255, 255, 255));
1171 SetBkColor(und_dc, RGB(0, 0, 0));
1172 SetBkMode(und_dc, OPAQUE);
1173 ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
1175 for (i = 0; i < font_height; i++) {
1176 c = GetPixel(und_dc, font_width / 2, i);
1177 if (c != RGB(0, 0, 0))
1180 SelectObject(und_dc, und_oldbm);
1181 DeleteObject(und_bm);
1184 und_mode = UND_LINE;
1185 DeleteObject(fonts[FONT_UNDERLINE]);
1186 fonts[FONT_UNDERLINE] = 0;
1190 if (bold_mode == BOLD_FONT) {
1191 f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
1195 descent = tm.tmAscent + 1;
1196 if (descent >= font_height)
1197 descent = font_height - 1;
1199 for (i = 0; i < 3; i++) {
1201 if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
1202 fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
1209 ReleaseDC(hwnd, hdc);
1211 if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
1212 und_mode = UND_LINE;
1213 DeleteObject(fonts[FONT_UNDERLINE]);
1214 fonts[FONT_UNDERLINE] = 0;
1217 if (bold_mode == BOLD_FONT &&
1218 fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
1219 bold_mode = BOLD_SHADOW;
1220 DeleteObject(fonts[FONT_BOLD]);
1221 fonts[FONT_BOLD] = 0;
1223 fontflag[0] = fontflag[1] = fontflag[2] = 1;
1225 init_ucs(&cfg, &ucsdata);
1228 static void another_font(int fontno)
1231 int fw_dontcare, fw_bold;
1235 if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
1238 basefont = (fontno & ~(FONT_BOLDUND));
1239 if (basefont != fontno && !fontflag[basefont])
1240 another_font(basefont);
1242 if (cfg.fontisbold) {
1243 fw_dontcare = FW_BOLD;
1246 fw_dontcare = FW_DONTCARE;
1250 c = cfg.fontcharset;
1256 if (fontno & FONT_WIDE)
1258 if (fontno & FONT_NARROW)
1260 if (fontno & FONT_OEM)
1262 if (fontno & FONT_BOLD)
1264 if (fontno & FONT_UNDERLINE)
1268 CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
1269 FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
1270 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
1271 FIXED_PITCH | FF_DONTCARE, s);
1273 fontflag[fontno] = 1;
1276 static void deinit_fonts(void)
1279 for (i = 0; i < FONT_MAXNO; i++) {
1281 DeleteObject(fonts[i]);
1287 void request_resize(void *frontend, int w, int h)
1291 /* If the window is maximized supress resizing attempts */
1292 if (IsZoomed(hwnd)) {
1293 if (cfg.resize_action == RESIZE_TERM)
1297 if (cfg.resize_action == RESIZE_DISABLED) return;
1298 if (h == term->rows && w == term->cols) return;
1300 /* Sanity checks ... */
1302 static int first_time = 1;
1305 switch (first_time) {
1307 /* Get the size of the screen */
1308 if (get_fullscreen_rect(&ss))
1309 /* first_time = 0 */ ;
1315 /* Make sure the values are sane */
1316 width = (ss.right - ss.left - extra_width) / 4;
1317 height = (ss.bottom - ss.top - extra_height) / 6;
1319 if (w > width || h > height)
1328 term_size(term, h, w, cfg.savelines);
1330 if (cfg.resize_action != RESIZE_FONT && !IsZoomed(hwnd)) {
1331 width = extra_width + font_width * w;
1332 height = extra_height + font_height * h;
1334 SetWindowPos(hwnd, NULL, 0, 0, width, height,
1335 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1336 SWP_NOMOVE | SWP_NOZORDER);
1340 InvalidateRect(hwnd, NULL, TRUE);
1343 static void reset_window(int reinit) {
1345 * This function decides how to resize or redraw when the
1346 * user changes something.
1348 * This function doesn't like to change the terminal size but if the
1349 * font size is locked that may be it's only soluion.
1351 int win_width, win_height;
1354 #ifdef RDB_DEBUG_PATCH
1355 debug((27, "reset_window()"));
1358 /* Current window sizes ... */
1359 GetWindowRect(hwnd, &wr);
1360 GetClientRect(hwnd, &cr);
1362 win_width = cr.right - cr.left;
1363 win_height = cr.bottom - cr.top;
1365 if (cfg.resize_action == RESIZE_DISABLED) reinit = 2;
1367 /* Are we being forced to reload the fonts ? */
1369 #ifdef RDB_DEBUG_PATCH
1370 debug((27, "reset_window() -- Forced deinit"));
1376 /* Oh, looks like we're minimised */
1377 if (win_width == 0 || win_height == 0)
1380 /* Is the window out of position ? */
1382 (offset_width != (win_width-font_width*term->cols)/2 ||
1383 offset_height != (win_height-font_height*term->rows)/2) ){
1384 offset_width = (win_width-font_width*term->cols)/2;
1385 offset_height = (win_height-font_height*term->rows)/2;
1386 InvalidateRect(hwnd, NULL, TRUE);
1387 #ifdef RDB_DEBUG_PATCH
1388 debug((27, "reset_window() -> Reposition terminal"));
1392 if (IsZoomed(hwnd)) {
1393 /* We're fullscreen, this means we must not change the size of
1394 * the window so it's the font size or the terminal itself.
1397 extra_width = wr.right - wr.left - cr.right + cr.left;
1398 extra_height = wr.bottom - wr.top - cr.bottom + cr.top;
1400 if (cfg.resize_action != RESIZE_TERM) {
1401 if ( font_width != win_width/term->cols ||
1402 font_height != win_height/term->rows) {
1404 init_fonts(win_width/term->cols, win_height/term->rows);
1405 offset_width = (win_width-font_width*term->cols)/2;
1406 offset_height = (win_height-font_height*term->rows)/2;
1407 InvalidateRect(hwnd, NULL, TRUE);
1408 #ifdef RDB_DEBUG_PATCH
1409 debug((25, "reset_window() -> Z font resize to (%d, %d)",
1410 font_width, font_height));
1414 if ( font_width != win_width/term->cols ||
1415 font_height != win_height/term->rows) {
1416 /* Our only choice at this point is to change the
1417 * size of the terminal; Oh well.
1419 term_size(term, win_height/font_height, win_width/font_width,
1421 offset_width = (win_width-font_width*term->cols)/2;
1422 offset_height = (win_height-font_height*term->rows)/2;
1423 InvalidateRect(hwnd, NULL, TRUE);
1424 #ifdef RDB_DEBUG_PATCH
1425 debug((27, "reset_window() -> Zoomed term_size"));
1432 /* Hmm, a force re-init means we should ignore the current window
1433 * so we resize to the default font size.
1436 #ifdef RDB_DEBUG_PATCH
1437 debug((27, "reset_window() -> Forced re-init"));
1440 offset_width = offset_height = cfg.window_border;
1441 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1442 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1444 if (win_width != font_width*term->cols + offset_width*2 ||
1445 win_height != font_height*term->rows + offset_height*2) {
1447 /* If this is too large windows will resize it to the maximum
1448 * allowed window size, we will then be back in here and resize
1449 * the font or terminal to fit.
1451 SetWindowPos(hwnd, NULL, 0, 0,
1452 font_width*term->cols + extra_width,
1453 font_height*term->rows + extra_height,
1454 SWP_NOMOVE | SWP_NOZORDER);
1457 InvalidateRect(hwnd, NULL, TRUE);
1461 /* Okay the user doesn't want us to change the font so we try the
1462 * window. But that may be too big for the screen which forces us
1463 * to change the terminal.
1465 if ((cfg.resize_action == RESIZE_TERM && reinit<=0) ||
1466 (cfg.resize_action == RESIZE_EITHER && reinit<0) ||
1468 offset_width = offset_height = cfg.window_border;
1469 extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2;
1470 extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2;
1472 if (win_width != font_width*term->cols + offset_width*2 ||
1473 win_height != font_height*term->rows + offset_height*2) {
1478 get_fullscreen_rect(&ss);
1480 width = (ss.right - ss.left - extra_width) / font_width;
1481 height = (ss.bottom - ss.top - extra_height) / font_height;
1484 if ( term->rows > height || term->cols > width ) {
1485 if (cfg.resize_action == RESIZE_EITHER) {
1486 /* Make the font the biggest we can */
1487 if (term->cols > width)
1488 font_width = (ss.right - ss.left - extra_width)
1490 if (term->rows > height)
1491 font_height = (ss.bottom - ss.top - extra_height)
1495 init_fonts(font_width, font_height);
1497 width = (ss.right - ss.left - extra_width) / font_width;
1498 height = (ss.bottom - ss.top - extra_height) / font_height;
1500 if ( height > term->rows ) height = term->rows;
1501 if ( width > term->cols ) width = term->cols;
1502 term_size(term, height, width, cfg.savelines);
1503 #ifdef RDB_DEBUG_PATCH
1504 debug((27, "reset_window() -> term resize to (%d,%d)",
1510 SetWindowPos(hwnd, NULL, 0, 0,
1511 font_width*term->cols + extra_width,
1512 font_height*term->rows + extra_height,
1513 SWP_NOMOVE | SWP_NOZORDER);
1515 InvalidateRect(hwnd, NULL, TRUE);
1516 #ifdef RDB_DEBUG_PATCH
1517 debug((27, "reset_window() -> window resize to (%d,%d)",
1518 font_width*term->cols + extra_width,
1519 font_height*term->rows + extra_height));
1525 /* We're allowed to or must change the font but do we want to ? */
1527 if (font_width != (win_width-cfg.window_border*2)/term->cols ||
1528 font_height != (win_height-cfg.window_border*2)/term->rows) {
1531 init_fonts((win_width-cfg.window_border*2)/term->cols,
1532 (win_height-cfg.window_border*2)/term->rows);
1533 offset_width = (win_width-font_width*term->cols)/2;
1534 offset_height = (win_height-font_height*term->rows)/2;
1536 extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2;
1537 extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2;
1539 InvalidateRect(hwnd, NULL, TRUE);
1540 #ifdef RDB_DEBUG_PATCH
1541 debug((25, "reset_window() -> font resize to (%d,%d)",
1542 font_width, font_height));
1547 static void set_input_locale(HKL kl)
1551 GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE,
1552 lbuf, sizeof(lbuf));
1554 kbd_codepage = atoi(lbuf);
1557 static void click(Mouse_Button b, int x, int y, int shift, int ctrl, int alt)
1559 int thistime = GetMessageTime();
1561 if (send_raw_mouse && !(cfg.mouse_override && shift)) {
1562 lastbtn = MBT_NOTHING;
1563 term_mouse(term, b, MA_CLICK, x, y, shift, ctrl, alt);
1567 if (lastbtn == b && thistime - lasttime < dbltime) {
1568 lastact = (lastact == MA_CLICK ? MA_2CLK :
1569 lastact == MA_2CLK ? MA_3CLK :
1570 lastact == MA_3CLK ? MA_CLICK : MA_NOTHING);
1575 if (lastact != MA_NOTHING)
1576 term_mouse(term, b, lastact, x, y, shift, ctrl, alt);
1577 lasttime = thistime;
1581 * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT)
1582 * into a cooked one (SELECT, EXTEND, PASTE).
1584 Mouse_Button translate_button(void *frontend, Mouse_Button button)
1586 if (button == MBT_LEFT)
1588 if (button == MBT_MIDDLE)
1589 return cfg.mouse_is_xterm ? MBT_PASTE : MBT_EXTEND;
1590 if (button == MBT_RIGHT)
1591 return cfg.mouse_is_xterm ? MBT_EXTEND : MBT_PASTE;
1592 return 0; /* shouldn't happen */
1595 static void show_mouseptr(int show)
1597 static int cursor_visible = 1;
1598 if (!cfg.hide_mouseptr) /* override if this feature disabled */
1600 if (cursor_visible && !show)
1602 else if (!cursor_visible && show)
1604 cursor_visible = show;
1607 static int is_alt_pressed(void)
1610 int r = GetKeyboardState(keystate);
1613 if (keystate[VK_MENU] & 0x80)
1615 if (keystate[VK_RMENU] & 0x80)
1620 static int is_shift_pressed(void)
1623 int r = GetKeyboardState(keystate);
1626 if (keystate[VK_SHIFT] & 0x80)
1631 static int resizing;
1633 static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
1634 WPARAM wParam, LPARAM lParam)
1637 static int ignore_clip = FALSE;
1638 static int need_backend_resize = FALSE;
1639 static int fullscr_on_max = FALSE;
1643 if (pending_netevent)
1644 enact_pending_netevent();
1645 if (GetCapture() != hwnd ||
1646 (send_raw_mouse && !(cfg.mouse_override && is_shift_pressed())))
1652 if (cfg.ping_interval > 0) {
1655 if (now - last_movement > cfg.ping_interval) {
1656 back->special(backhandle, TS_PING);
1657 last_movement = now;
1660 net_pending_errors();
1666 if (!cfg.warn_on_close || session_closed ||
1668 "Are you sure you want to close this session?",
1669 "PuTTY Exit Confirmation",
1670 MB_ICONWARNING | MB_OKCANCEL) == IDOK)
1671 DestroyWindow(hwnd);
1678 switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
1690 PROCESS_INFORMATION pi;
1691 HANDLE filemap = NULL;
1693 if (wParam == IDM_DUPSESS) {
1695 * Allocate a file-mapping memory chunk for the
1698 SECURITY_ATTRIBUTES sa;
1701 sa.nLength = sizeof(sa);
1702 sa.lpSecurityDescriptor = NULL;
1703 sa.bInheritHandle = TRUE;
1704 filemap = CreateFileMapping((HANDLE) 0xFFFFFFFF,
1707 0, sizeof(Config), NULL);
1709 p = (Config *) MapViewOfFile(filemap,
1711 0, 0, sizeof(Config));
1713 *p = cfg; /* structure copy */
1717 sprintf(c, "putty &%p", filemap);
1719 } else if (wParam == IDM_SAVEDSESS) {
1720 if ((lParam - IDM_SAVED_MIN) / 16 < sesslist.nsessions) {
1722 sesslist.sessions[(lParam - IDM_SAVED_MIN) / 16];
1723 cl = smalloc(16 + strlen(session));
1724 /* 8, but play safe */
1727 /* not a very important failure mode */
1729 sprintf(cl, "putty @%s", session);
1737 GetModuleFileName(NULL, b, sizeof(b) - 1);
1739 si.lpReserved = NULL;
1740 si.lpDesktop = NULL;
1744 si.lpReserved2 = NULL;
1745 CreateProcess(b, cl, NULL, NULL, TRUE,
1746 NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
1749 CloseHandle(filemap);
1759 GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
1762 if (!do_reconfig(hwnd))
1766 /* Disable full-screen if resizing forbidden */
1767 HMENU m = GetSystemMenu (hwnd, FALSE);
1768 EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
1769 (cfg.resize_action == RESIZE_DISABLED)
1770 ? MF_GRAYED : MF_ENABLED);
1771 /* Gracefully unzoom if necessary */
1772 if (IsZoomed(hwnd) &&
1773 (cfg.resize_action == RESIZE_DISABLED)) {
1774 ShowWindow(hwnd, SW_RESTORE);
1778 /* Pass new config data to the logging module */
1779 log_reconfig(logctx, &cfg);
1783 * Flush the line discipline's edit buffer in the
1784 * case where local editing has just been disabled.
1786 ldisc_send(ldisc, NULL, 0, 0);
1794 /* Pass new config data to the terminal */
1795 term_reconfig(term, &cfg);
1797 /* Pass new config data to the back end */
1798 back->reconfig(backhandle, &cfg);
1800 /* Screen size changed ? */
1801 if (cfg.height != prev_cfg.height ||
1802 cfg.width != prev_cfg.width ||
1803 cfg.savelines != prev_cfg.savelines ||
1804 cfg.resize_action == RESIZE_FONT ||
1805 (cfg.resize_action == RESIZE_EITHER && IsZoomed(hwnd)) ||
1806 cfg.resize_action == RESIZE_DISABLED)
1807 term_size(term, cfg.height, cfg.width, cfg.savelines);
1809 /* Enable or disable the scroll bar, etc */
1811 LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
1812 LONG nexflag, exflag =
1813 GetWindowLong(hwnd, GWL_EXSTYLE);
1816 if (cfg.alwaysontop != prev_cfg.alwaysontop) {
1817 if (cfg.alwaysontop) {
1818 nexflag |= WS_EX_TOPMOST;
1819 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
1820 SWP_NOMOVE | SWP_NOSIZE);
1822 nexflag &= ~(WS_EX_TOPMOST);
1823 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
1824 SWP_NOMOVE | SWP_NOSIZE);
1827 if (cfg.sunken_edge)
1828 nexflag |= WS_EX_CLIENTEDGE;
1830 nexflag &= ~(WS_EX_CLIENTEDGE);
1833 if (is_full_screen() ?
1834 cfg.scrollbar_in_fullscreen : cfg.scrollbar)
1837 nflg &= ~WS_VSCROLL;
1839 if (cfg.resize_action == RESIZE_DISABLED ||
1841 nflg &= ~WS_THICKFRAME;
1843 nflg |= WS_THICKFRAME;
1845 if (cfg.resize_action == RESIZE_DISABLED)
1846 nflg &= ~WS_MAXIMIZEBOX;
1848 nflg |= WS_MAXIMIZEBOX;
1850 if (nflg != flag || nexflag != exflag) {
1852 SetWindowLong(hwnd, GWL_STYLE, nflg);
1853 if (nexflag != exflag)
1854 SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
1856 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1857 SWP_NOACTIVATE | SWP_NOCOPYBITS |
1858 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1866 if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
1871 set_title(NULL, cfg.wintitle);
1872 if (IsIconic(hwnd)) {
1874 cfg.win_name_always ? window_name :
1878 if (strcmp(cfg.font, prev_cfg.font) != 0 ||
1879 strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
1880 cfg.fontisbold != prev_cfg.fontisbold ||
1881 cfg.fontheight != prev_cfg.fontheight ||
1882 cfg.fontcharset != prev_cfg.fontcharset ||
1883 cfg.vtmode != prev_cfg.vtmode ||
1884 cfg.bold_colour != prev_cfg.bold_colour ||
1885 cfg.resize_action == RESIZE_DISABLED ||
1886 cfg.resize_action == RESIZE_EITHER ||
1887 (cfg.resize_action != prev_cfg.resize_action))
1890 InvalidateRect(hwnd, NULL, TRUE);
1891 reset_window(init_lvl);
1892 net_pending_errors();
1903 ldisc_send(ldisc, NULL, 0, 0);
1906 back->special(backhandle, TS_AYT);
1907 net_pending_errors();
1910 back->special(backhandle, TS_BRK);
1911 net_pending_errors();
1914 back->special(backhandle, TS_SYNCH);
1915 net_pending_errors();
1918 back->special(backhandle, TS_EC);
1919 net_pending_errors();
1922 back->special(backhandle, TS_EL);
1923 net_pending_errors();
1926 back->special(backhandle, TS_GA);
1927 net_pending_errors();
1930 back->special(backhandle, TS_NOP);
1931 net_pending_errors();
1934 back->special(backhandle, TS_ABORT);
1935 net_pending_errors();
1938 back->special(backhandle, TS_AO);
1939 net_pending_errors();
1942 back->special(backhandle, TS_IP);
1943 net_pending_errors();
1946 back->special(backhandle, TS_SUSP);
1947 net_pending_errors();
1950 back->special(backhandle, TS_EOR);
1951 net_pending_errors();
1954 back->special(backhandle, TS_EOF);
1955 net_pending_errors();
1961 WinHelp(hwnd, help_path,
1962 help_has_contents ? HELP_FINDER : HELP_CONTENTS, 0);
1966 * We get this if the System menu has been activated
1973 * We get this if the System menu has been activated
1974 * using the keyboard. This might happen from within
1975 * TranslateKey, in which case it really wants to be
1976 * followed by a `space' character to actually _bring
1977 * the menu up_ rather than just sitting there in
1978 * `ready to appear' state.
1980 show_mouseptr(1); /* make sure pointer is visible */
1982 PostMessage(hwnd, WM_CHAR, ' ', 0);
1984 case IDM_FULLSCREEN:
1988 if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
1989 SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
1994 #define X_POS(l) ((int)(short)LOWORD(l))
1995 #define Y_POS(l) ((int)(short)HIWORD(l))
1997 #define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
1998 #define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
1999 case WM_LBUTTONDOWN:
2000 case WM_MBUTTONDOWN:
2001 case WM_RBUTTONDOWN:
2009 case WM_LBUTTONDOWN:
2013 case WM_MBUTTONDOWN:
2014 button = MBT_MIDDLE;
2017 case WM_RBUTTONDOWN:
2026 button = MBT_MIDDLE;
2034 button = press = 0; /* shouldn't happen */
2038 * Special case: in full-screen mode, if the left
2039 * button is clicked in the very top left corner of the
2040 * window, we put up the System menu instead of doing
2043 if (is_full_screen() && press && button == MBT_LEFT &&
2044 X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
2045 SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
2050 TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
2051 wParam & MK_SHIFT, wParam & MK_CONTROL,
2055 term_mouse(term, button, MA_RELEASE,
2056 TO_CHR_X(X_POS(lParam)),
2057 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2058 wParam & MK_CONTROL, is_alt_pressed());
2066 * Add the mouse position and message time to the random
2069 noise_ultralight(lParam);
2071 if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) &&
2072 GetCapture() == hwnd) {
2074 if (wParam & MK_LBUTTON)
2076 else if (wParam & MK_MBUTTON)
2080 term_mouse(term, b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
2081 TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
2082 wParam & MK_CONTROL, is_alt_pressed());
2085 case WM_NCMOUSEMOVE:
2087 noise_ultralight(lParam);
2089 case WM_IGNORE_CLIP:
2090 ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
2092 case WM_DESTROYCLIPBOARD:
2094 term_deselect(term);
2095 ignore_clip = FALSE;
2101 hdc = BeginPaint(hwnd, &p);
2103 SelectPalette(hdc, pal, TRUE);
2104 RealizePalette(hdc);
2106 term_paint(term, hdc,
2107 (p.rcPaint.left-offset_width)/font_width,
2108 (p.rcPaint.top-offset_height)/font_height,
2109 (p.rcPaint.right-offset_width-1)/font_width,
2110 (p.rcPaint.bottom-offset_height-1)/font_height,
2114 p.rcPaint.left < offset_width ||
2115 p.rcPaint.top < offset_height ||
2116 p.rcPaint.right >= offset_width + font_width*term->cols ||
2117 p.rcPaint.bottom>= offset_height + font_height*term->rows)
2119 HBRUSH fillcolour, oldbrush;
2121 fillcolour = CreateSolidBrush (
2122 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2123 oldbrush = SelectObject(hdc, fillcolour);
2124 edge = CreatePen(PS_SOLID, 0,
2125 colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
2126 oldpen = SelectObject(hdc, edge);
2129 * Jordan Russell reports that this apparently
2130 * ineffectual IntersectClipRect() call masks a
2131 * Windows NT/2K bug causing strange display
2132 * problems when the PuTTY window is taller than
2133 * the primary monitor. It seems harmless enough...
2135 IntersectClipRect(hdc,
2136 p.rcPaint.left, p.rcPaint.top,
2137 p.rcPaint.right, p.rcPaint.bottom);
2139 ExcludeClipRect(hdc,
2140 offset_width, offset_height,
2141 offset_width+font_width*term->cols,
2142 offset_height+font_height*term->rows);
2144 Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
2145 p.rcPaint.right, p.rcPaint.bottom);
2147 // SelectClipRgn(hdc, NULL);
2149 SelectObject(hdc, oldbrush);
2150 DeleteObject(fillcolour);
2151 SelectObject(hdc, oldpen);
2154 SelectObject(hdc, GetStockObject(SYSTEM_FONT));
2155 SelectObject(hdc, GetStockObject(WHITE_PEN));
2161 /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
2162 * but the only one that's likely to try to overload us is FD_READ.
2163 * This means buffering just one is fine.
2165 if (pending_netevent)
2166 enact_pending_netevent();
2168 pending_netevent = TRUE;
2169 pend_netevent_wParam = wParam;
2170 pend_netevent_lParam = lParam;
2171 if (WSAGETSELECTEVENT(lParam) != FD_READ)
2172 enact_pending_netevent();
2174 time(&last_movement);
2177 term->has_focus = TRUE;
2178 CreateCaret(hwnd, caretbm, font_width, font_height);
2180 flash_window(0); /* stop */
2187 term->has_focus = FALSE;
2189 caret_x = caret_y = -1; /* ensure caret is replaced next time */
2193 case WM_ENTERSIZEMOVE:
2194 #ifdef RDB_DEBUG_PATCH
2195 debug((27, "WM_ENTERSIZEMOVE"));
2199 need_backend_resize = FALSE;
2201 case WM_EXITSIZEMOVE:
2204 #ifdef RDB_DEBUG_PATCH
2205 debug((27, "WM_EXITSIZEMOVE"));
2207 if (need_backend_resize) {
2208 term_size(term, cfg.height, cfg.width, cfg.savelines);
2209 InvalidateRect(hwnd, NULL, TRUE);
2214 * This does two jobs:
2215 * 1) Keep the sizetip uptodate
2216 * 2) Make sure the window size is _stepped_ in units of the font size.
2218 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2219 int width, height, w, h, ew, eh;
2220 LPRECT r = (LPRECT) lParam;
2222 if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
2223 (cfg.height != term->rows || cfg.width != term->cols )) {
2225 * Great! It seems that both the terminal size and the
2226 * font size have been changed and the user is now dragging.
2228 * It will now be difficult to get back to the configured
2231 * This would be easier but it seems to be too confusing.
2233 term_size(term, cfg.height, cfg.width, cfg.savelines);
2236 cfg.height=term->rows; cfg.width=term->cols;
2238 InvalidateRect(hwnd, NULL, TRUE);
2239 need_backend_resize = TRUE;
2242 width = r->right - r->left - extra_width;
2243 height = r->bottom - r->top - extra_height;
2244 w = (width + font_width / 2) / font_width;
2247 h = (height + font_height / 2) / font_height;
2250 UpdateSizeTip(hwnd, w, h);
2251 ew = width - w * font_width;
2252 eh = height - h * font_height;
2254 if (wParam == WMSZ_LEFT ||
2255 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2261 if (wParam == WMSZ_TOP ||
2262 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2272 int width, height, w, h, rv = 0;
2273 int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
2274 int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
2275 LPRECT r = (LPRECT) lParam;
2277 width = r->right - r->left - ex_width;
2278 height = r->bottom - r->top - ex_height;
2280 w = (width + term->cols/2)/term->cols;
2281 h = (height + term->rows/2)/term->rows;
2282 if ( r->right != r->left + w*term->cols + ex_width)
2285 if (wParam == WMSZ_LEFT ||
2286 wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
2287 r->left = r->right - w*term->cols - ex_width;
2289 r->right = r->left + w*term->cols + ex_width;
2291 if (r->bottom != r->top + h*term->rows + ex_height)
2294 if (wParam == WMSZ_TOP ||
2295 wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
2296 r->top = r->bottom - h*term->rows - ex_height;
2298 r->bottom = r->top + h*term->rows + ex_height;
2302 /* break; (never reached) */
2303 case WM_FULLSCR_ON_MAX:
2304 fullscr_on_max = TRUE;
2307 sys_cursor_update();
2310 #ifdef RDB_DEBUG_PATCH
2311 debug((27, "WM_SIZE %s (%d,%d)",
2312 (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
2313 (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
2314 (wParam == SIZE_RESTORED && resizing) ? "to":
2315 (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
2317 LOWORD(lParam), HIWORD(lParam)));
2319 if (wParam == SIZE_MINIMIZED)
2321 cfg.win_name_always ? window_name : icon_name);
2322 if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
2323 SetWindowText(hwnd, window_name);
2324 if (wParam == SIZE_RESTORED)
2325 clear_full_screen();
2326 if (wParam == SIZE_MAXIMIZED && fullscr_on_max) {
2327 fullscr_on_max = FALSE;
2331 if (cfg.resize_action == RESIZE_DISABLED) {
2332 /* A resize, well it better be a minimize. */
2336 int width, height, w, h;
2338 width = LOWORD(lParam);
2339 height = HIWORD(lParam);
2342 if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
2344 prev_rows = term->rows;
2345 prev_cols = term->cols;
2346 if (cfg.resize_action == RESIZE_TERM) {
2347 w = width / font_width;
2349 h = height / font_height;
2352 term_size(term, h, w, cfg.savelines);
2355 } else if (wParam == SIZE_RESTORED && was_zoomed) {
2357 if (cfg.resize_action == RESIZE_TERM)
2358 term_size(term, prev_rows, prev_cols, cfg.savelines);
2359 if (cfg.resize_action != RESIZE_FONT)
2364 /* This is an unexpected resize, these will normally happen
2365 * if the window is too large. Probably either the user
2366 * selected a huge font or the screen size has changed.
2368 * This is also called with minimize.
2370 else reset_window(-1);
2374 * Don't call back->size in mid-resize. (To prevent
2375 * massive numbers of resize events getting sent
2376 * down the connection during an NT opaque drag.)
2379 if (cfg.resize_action != RESIZE_FONT && !is_alt_pressed()) {
2380 need_backend_resize = TRUE;
2381 w = (width-cfg.window_border*2) / font_width;
2383 h = (height-cfg.window_border*2) / font_height;
2392 sys_cursor_update();
2395 switch (LOWORD(wParam)) {
2397 term_scroll(term, -1, 0);
2400 term_scroll(term, +1, 0);
2403 term_scroll(term, 0, +1);
2406 term_scroll(term, 0, -1);
2409 term_scroll(term, 0, +term->rows / 2);
2412 term_scroll(term, 0, -term->rows / 2);
2414 case SB_THUMBPOSITION:
2416 term_scroll(term, 1, HIWORD(wParam));
2420 case WM_PALETTECHANGED:
2421 if ((HWND) wParam != hwnd && pal != NULL) {
2422 HDC hdc = get_ctx(NULL);
2424 if (RealizePalette(hdc) > 0)
2430 case WM_QUERYNEWPALETTE:
2432 HDC hdc = get_ctx(NULL);
2434 if (RealizePalette(hdc) > 0)
2446 * Add the scan code and keypress timing to the random
2449 noise_ultralight(lParam);
2452 * We don't do TranslateMessage since it disassociates the
2453 * resulting CHAR message from the KEYDOWN that sparked it,
2454 * which we occasionally don't want. Instead, we process
2455 * KEYDOWN, and call the Win32 translator functions so that
2456 * we get the translations under _our_ control.
2459 unsigned char buf[20];
2462 if (wParam == VK_PROCESSKEY) {
2465 m.message = WM_KEYDOWN;
2467 m.lParam = lParam & 0xdfff;
2468 TranslateMessage(&m);
2470 len = TranslateKey(message, wParam, lParam, buf);
2472 return DefWindowProc(hwnd, message, wParam, lParam);
2476 * Interrupt an ongoing paste. I'm not sure
2477 * this is sensible, but for the moment it's
2478 * preferable to having to faff about buffering
2484 * We need not bother about stdin backlogs
2485 * here, because in GUI PuTTY we can't do
2486 * anything about it anyway; there's no means
2487 * of asking Windows to hold off on KEYDOWN
2488 * messages. We _have_ to buffer everything
2491 term_seen_key_event(term);
2492 ldisc_send(ldisc, buf, len, 1);
2497 net_pending_errors();
2499 case WM_INPUTLANGCHANGE:
2500 /* wParam == Font number */
2501 /* lParam == Locale */
2502 set_input_locale((HKL)lParam);
2503 sys_cursor_update();
2506 if(wParam == IMN_SETOPENSTATUS) {
2507 HIMC hImc = ImmGetContext(hwnd);
2508 ImmSetCompositionFont(hImc, &lfont);
2509 ImmReleaseContext(hwnd, hImc);
2513 case WM_IME_COMPOSITION:
2519 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
2520 osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
2522 if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
2523 break; /* fall back to DefWindowProc */
2525 hIMC = ImmGetContext(hwnd);
2526 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
2530 buff = (char*) smalloc(n);
2531 ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
2533 * Jaeyoun Chung reports that Korean character
2534 * input doesn't work correctly if we do a single
2535 * luni_send() covering the whole of buff. So
2536 * instead we luni_send the characters one by one.
2538 term_seen_key_event(term);
2539 for (i = 0; i < n; i += 2) {
2540 luni_send(ldisc, (unsigned short *)(buff+i), 1, 1);
2544 ImmReleaseContext(hwnd, hIMC);
2549 if (wParam & 0xFF00) {
2550 unsigned char buf[2];
2553 buf[0] = wParam >> 8;
2554 term_seen_key_event(term);
2555 lpage_send(ldisc, kbd_codepage, buf, 2, 1);
2557 char c = (unsigned char) wParam;
2558 term_seen_key_event(term);
2559 lpage_send(ldisc, kbd_codepage, &c, 1, 1);
2565 * Nevertheless, we are prepared to deal with WM_CHAR
2566 * messages, should they crop up. So if someone wants to
2567 * post the things to us as part of a macro manoeuvre,
2568 * we're ready to cope.
2571 char c = (unsigned char)wParam;
2572 term_seen_key_event(term);
2573 lpage_send(ldisc, CP_ACP, &c, 1, 1);
2577 if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
2578 SetCursor(LoadCursor(NULL, IDC_ARROW));
2582 if (message == wm_mousewheel || message == WM_MOUSEWHEEL) {
2583 int shift_pressed=0, control_pressed=0;
2585 if (message == WM_MOUSEWHEEL) {
2586 wheel_accumulator += (short)HIWORD(wParam);
2587 shift_pressed=LOWORD(wParam) & MK_SHIFT;
2588 control_pressed=LOWORD(wParam) & MK_CONTROL;
2591 wheel_accumulator += (int)wParam;
2592 if (GetKeyboardState(keys)!=0) {
2593 shift_pressed=keys[VK_SHIFT]&0x80;
2594 control_pressed=keys[VK_CONTROL]&0x80;
2598 /* process events when the threshold is reached */
2599 while (abs(wheel_accumulator) >= WHEEL_DELTA) {
2602 /* reduce amount for next time */
2603 if (wheel_accumulator > 0) {
2605 wheel_accumulator -= WHEEL_DELTA;
2606 } else if (wheel_accumulator < 0) {
2608 wheel_accumulator += WHEEL_DELTA;
2612 if (send_raw_mouse &&
2613 !(cfg.mouse_override && shift_pressed)) {
2614 /* send a mouse-down followed by a mouse up */
2617 TO_CHR_X(X_POS(lParam)),
2618 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2619 control_pressed, is_alt_pressed());
2620 term_mouse(term, b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
2621 TO_CHR_Y(Y_POS(lParam)), shift_pressed,
2622 control_pressed, is_alt_pressed());
2624 /* trigger a scroll */
2625 term_scroll(term, 0,
2627 -term->rows / 2 : term->rows / 2);
2634 return DefWindowProc(hwnd, message, wParam, lParam);
2638 * Move the system caret. (We maintain one, even though it's
2639 * invisible, for the benefit of blind people: apparently some
2640 * helper software tracks the system caret, so we should arrange to
2643 void sys_cursor(void *frontend, int x, int y)
2647 if (!term->has_focus) return;
2650 * Avoid gratuitously re-updating the cursor position and IMM
2651 * window if there's no actual change required.
2653 cx = x * font_width + offset_width;
2654 cy = y * font_height + offset_height;
2655 if (cx == caret_x && cy == caret_y)
2660 sys_cursor_update();
2663 static void sys_cursor_update(void)
2668 if (!term->has_focus) return;
2670 if (caret_x < 0 || caret_y < 0)
2673 SetCaretPos(caret_x, caret_y);
2675 /* IMM calls on Win98 and beyond only */
2676 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
2678 if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
2679 osVersion.dwMinorVersion == 0) return; /* 95 */
2681 /* we should have the IMM functions */
2682 hIMC = ImmGetContext(hwnd);
2683 cf.dwStyle = CFS_POINT;
2684 cf.ptCurrentPos.x = caret_x;
2685 cf.ptCurrentPos.y = caret_y;
2686 ImmSetCompositionWindow(hIMC, &cf);
2688 ImmReleaseContext(hwnd, hIMC);
2692 * Draw a line of text in the window, at given character
2693 * coordinates, in given attributes.
2695 * We are allowed to fiddle with the contents of `text'.
2697 void do_text(Context ctx, int x, int y, char *text, int len,
2698 unsigned long attr, int lattr)
2701 int nfg, nbg, nfont;
2704 int force_manual_underline = 0;
2705 int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
2706 int char_width = fnt_width;
2707 int text_adjust = 0;
2708 static int *IpDx = 0, IpDxLEN = 0;
2710 if (attr & ATTR_WIDE)
2713 if (len > IpDxLEN || IpDx[0] != char_width) {
2715 if (len > IpDxLEN) {
2717 IpDx = smalloc((len + 16) * sizeof(int));
2718 IpDxLEN = (len + 16);
2720 for (i = 0; i < IpDxLEN; i++)
2721 IpDx[i] = char_width;
2724 /* Only want the left half of double width lines */
2725 if (lattr != LATTR_NORM && x*2 >= term->cols)
2733 if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || term->big_cursor)) {
2734 attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
2735 attr ^= ATTR_CUR_XOR;
2739 if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
2740 /* Assume a poorman font is borken in other ways too. */
2750 nfont |= FONT_WIDE + FONT_HIGH;
2753 if (attr & ATTR_NARROW)
2754 nfont |= FONT_NARROW;
2756 /* Special hack for the VT100 linedraw glyphs. */
2757 if ((attr & CSET_MASK) == 0x2300) {
2758 if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
2759 switch ((unsigned char) (text[0])) {
2761 text_adjust = -2 * font_height / 5;
2764 text_adjust = -1 * font_height / 5;
2767 text_adjust = font_height / 5;
2770 text_adjust = 2 * font_height / 5;
2773 if (lattr == LATTR_TOP || lattr == LATTR_BOT)
2776 text[0] = (char) (ucsdata.unitab_xterm['q'] & CHAR_MASK);
2777 attr |= (ucsdata.unitab_xterm['q'] & CSET_MASK);
2778 if (attr & ATTR_UNDER) {
2779 attr &= ~ATTR_UNDER;
2780 force_manual_underline = 1;
2785 /* Anything left as an original character set is unprintable. */
2786 if (DIRECT_CHAR(attr)) {
2789 memset(text, 0xFD, len);
2793 if ((attr & CSET_MASK) == ATTR_OEMCP)
2796 nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
2797 nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
2798 if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
2800 if (und_mode == UND_FONT && (attr & ATTR_UNDER))
2801 nfont |= FONT_UNDERLINE;
2802 another_font(nfont);
2803 if (!fonts[nfont]) {
2804 if (nfont & FONT_UNDERLINE)
2805 force_manual_underline = 1;
2806 /* Don't do the same for manual bold, it could be bad news. */
2808 nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
2810 another_font(nfont);
2812 nfont = FONT_NORMAL;
2813 if (attr & ATTR_REVERSE) {
2818 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
2820 if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
2824 SelectObject(hdc, fonts[nfont]);
2825 SetTextColor(hdc, fg);
2826 SetBkColor(hdc, bg);
2827 SetBkMode(hdc, OPAQUE);
2830 line_box.right = x + char_width * len;
2831 line_box.bottom = y + font_height;
2833 /* Only want the left half of double width lines */
2834 if (line_box.right > font_width*term->cols+offset_width)
2835 line_box.right = font_width*term->cols+offset_width;
2837 /* We're using a private area for direct to font. (512 chars.) */
2838 if (ucsdata.dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
2839 /* Ho Hum, dbcs fonts are a PITA! */
2840 /* To display on W9x I have to convert to UCS */
2841 static wchar_t *uni_buf = 0;
2842 static int uni_len = 0;
2844 if (len > uni_len) {
2846 uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
2849 for(nlen = mptr = 0; mptr<len; mptr++) {
2850 uni_buf[nlen] = 0xFFFD;
2851 if (IsDBCSLeadByteEx(ucsdata.font_codepage, (BYTE) text[mptr])) {
2852 IpDx[nlen] += char_width;
2853 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2854 text+mptr, 2, uni_buf+nlen, 1);
2859 MultiByteToWideChar(ucsdata.font_codepage, MB_USEGLYPHCHARS,
2860 text+mptr, 1, uni_buf+nlen, 1);
2868 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2869 ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
2870 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2871 SetBkMode(hdc, TRANSPARENT);
2872 ExtTextOutW(hdc, x - 1,
2873 y - font_height * (lattr ==
2874 LATTR_BOT) + text_adjust,
2875 ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
2879 } else if (DIRECT_FONT(attr)) {
2881 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2882 ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
2883 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2884 SetBkMode(hdc, TRANSPARENT);
2886 /* GRR: This draws the character outside it's box and can leave
2887 * 'droppings' even with the clip box! I suppose I could loop it
2888 * one character at a time ... yuk.
2890 * Or ... I could do a test print with "W", and use +1 or -1 for this
2891 * shift depending on if the leftmost column is blank...
2893 ExtTextOut(hdc, x - 1,
2894 y - font_height * (lattr ==
2895 LATTR_BOT) + text_adjust,
2896 ETO_CLIPPED, &line_box, text, len, IpDx);
2899 /* And 'normal' unicode characters */
2900 static WCHAR *wbuf = NULL;
2901 static int wlen = 0;
2906 wbuf = smalloc(wlen * sizeof(WCHAR));
2908 for (i = 0; i < len; i++)
2909 wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
2912 y - font_height * (lattr == LATTR_BOT) + text_adjust,
2913 ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
2915 /* And the shadow bold hack. */
2916 if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
2917 SetBkMode(hdc, TRANSPARENT);
2918 ExtTextOutW(hdc, x - 1,
2919 y - font_height * (lattr ==
2920 LATTR_BOT) + text_adjust,
2921 ETO_CLIPPED, &line_box, wbuf, len, IpDx);
2924 if (lattr != LATTR_TOP && (force_manual_underline ||
2925 (und_mode == UND_LINE
2926 && (attr & ATTR_UNDER)))) {
2929 if (lattr == LATTR_BOT)
2930 dec = dec * 2 - font_height;
2932 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
2933 MoveToEx(hdc, x, y + dec, NULL);
2934 LineTo(hdc, x + len * char_width, y + dec);
2935 oldpen = SelectObject(hdc, oldpen);
2936 DeleteObject(oldpen);
2940 void do_cursor(Context ctx, int x, int y, char *text, int len,
2941 unsigned long attr, int lattr)
2947 int ctype = cfg.cursor_type;
2949 if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) {
2950 if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
2951 do_text(ctx, x, y, text, len, attr, lattr);
2955 attr |= TATTR_RIGHTCURS;
2958 fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
2959 if (attr & ATTR_WIDE)
2966 if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) {
2969 pts[0].x = pts[1].x = pts[4].x = x;
2970 pts[2].x = pts[3].x = x + char_width - 1;
2971 pts[0].y = pts[3].y = pts[4].y = y;
2972 pts[1].y = pts[2].y = y + font_height - 1;
2973 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2974 Polyline(hdc, pts, 5);
2975 oldpen = SelectObject(hdc, oldpen);
2976 DeleteObject(oldpen);
2977 } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
2978 int startx, starty, dx, dy, length, i;
2981 starty = y + descent;
2984 length = char_width;
2987 if (attr & TATTR_RIGHTCURS)
2988 xadjust = char_width - 1;
2989 startx = x + xadjust;
2993 length = font_height;
2995 if (attr & TATTR_ACTCURS) {
2998 SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
2999 MoveToEx(hdc, startx, starty, NULL);
3000 LineTo(hdc, startx + dx * length, starty + dy * length);
3001 oldpen = SelectObject(hdc, oldpen);
3002 DeleteObject(oldpen);
3004 for (i = 0; i < length; i++) {
3006 SetPixel(hdc, startx, starty, colours[23]);
3015 /* This function gets the actual width of a character in the normal font.
3017 int char_width(Context ctx, int uc) {
3021 /* If the font max is the same as the font ave width then this
3022 * function is a no-op.
3024 if (!font_dualwidth) return 1;
3026 switch (uc & CSET_MASK) {
3028 uc = ucsdata.unitab_line[uc & 0xFF];
3031 uc = ucsdata.unitab_xterm[uc & 0xFF];
3034 uc = ucsdata.unitab_scoacs[uc & 0xFF];
3037 if (DIRECT_FONT(uc)) {
3038 if (ucsdata.dbcs_screenfont) return 1;
3040 /* Speedup, I know of no font where ascii is the wrong width */
3041 if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
3044 if ( (uc & CSET_MASK) == ATTR_ACP ) {
3045 SelectObject(hdc, fonts[FONT_NORMAL]);
3046 } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
3047 another_font(FONT_OEM);
3048 if (!fonts[FONT_OEM]) return 0;
3050 SelectObject(hdc, fonts[FONT_OEM]);
3054 if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
3055 GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
3058 /* Speedup, I know of no font where ascii is the wrong width */
3059 if (uc >= ' ' && uc <= '~') return 1;
3061 SelectObject(hdc, fonts[FONT_NORMAL]);
3062 if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
3063 /* Okay that one worked */ ;
3064 else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
3065 /* This should work on 9x too, but it's "less accurate" */ ;
3070 ibuf += font_width / 2 -1;
3077 * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
3078 * codes. Returns number of bytes used or zero to drop the message
3079 * or -1 to forward the message to windows.
3081 static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
3082 unsigned char *output)
3085 int scan, left_alt = 0, key_down, shift_state;
3087 unsigned char *p = output;
3088 static int alt_sum = 0;
3090 HKL kbd_layout = GetKeyboardLayout(0);
3092 static WORD keys[3];
3093 static int compose_char = 0;
3094 static WPARAM compose_key = 0;
3096 r = GetKeyboardState(keystate);
3098 memset(keystate, 0, sizeof(keystate));
3101 #define SHOW_TOASCII_RESULT
3102 { /* Tell us all about key events */
3103 static BYTE oldstate[256];
3104 static int first = 1;
3108 memcpy(oldstate, keystate, sizeof(oldstate));
3111 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
3113 } else if ((HIWORD(lParam) & KF_UP)
3114 && scan == (HIWORD(lParam) & 0xFF)) {
3118 if (wParam >= VK_F1 && wParam <= VK_F20)
3119 debug(("K_F%d", wParam + 1 - VK_F1));
3132 debug(("VK_%02x", wParam));
3134 if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
3136 debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
3138 ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
3139 if (ch >= ' ' && ch <= '~')
3140 debug((", '%c'", ch));
3142 debug((", $%02x", ch));
3145 debug((", KB0=%02x", keys[0]));
3147 debug((", KB1=%02x", keys[1]));
3149 debug((", KB2=%02x", keys[2]));
3151 if ((keystate[VK_SHIFT] & 0x80) != 0)
3153 if ((keystate[VK_CONTROL] & 0x80) != 0)
3155 if ((HIWORD(lParam) & KF_EXTENDED))
3157 if ((HIWORD(lParam) & KF_UP))
3161 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
3162 else if ((HIWORD(lParam) & KF_UP))
3163 oldstate[wParam & 0xFF] ^= 0x80;
3165 oldstate[wParam & 0xFF] ^= 0x81;
3167 for (ch = 0; ch < 256; ch++)
3168 if (oldstate[ch] != keystate[ch])
3169 debug((", M%02x=%02x", ch, keystate[ch]));
3171 memcpy(oldstate, keystate, sizeof(oldstate));
3175 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
3176 keystate[VK_RMENU] = keystate[VK_MENU];
3180 /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
3181 if ((cfg.funky_type == 3 ||
3182 (cfg.funky_type <= 1 && term->app_keypad_keys &&
3184 && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
3186 wParam = VK_EXECUTE;
3188 /* UnToggle NUMLock */
3189 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
3190 keystate[VK_NUMLOCK] ^= 1;
3193 /* And write back the 'adjusted' state */
3194 SetKeyboardState(keystate);
3197 /* Disable Auto repeat if required */
3198 if (term->repeat_off &&
3199 (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
3202 if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
3205 key_down = ((HIWORD(lParam) & KF_UP) == 0);
3207 /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
3208 if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
3209 if (cfg.ctrlaltkeys)
3210 keystate[VK_MENU] = 0;
3212 keystate[VK_RMENU] = 0x80;
3217 scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
3218 shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
3219 + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
3221 /* Note if AltGr was pressed and if it was used as a compose key */
3222 if (!compose_state) {
3223 compose_key = 0x100;
3224 if (cfg.compose_key) {
3225 if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
3226 compose_key = wParam;
3228 if (wParam == VK_APPS)
3229 compose_key = wParam;
3232 if (wParam == compose_key) {
3233 if (compose_state == 0
3234 && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
3236 else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
3240 } else if (compose_state == 1 && wParam != VK_CONTROL)
3243 if (compose_state > 1 && left_alt)
3246 /* Sanitize the number pad if not using a PC NumPad */
3247 if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
3248 && cfg.funky_type != 2)
3249 || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
3250 if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
3254 nParam = VK_NUMPAD0;
3257 nParam = VK_NUMPAD1;
3260 nParam = VK_NUMPAD2;
3263 nParam = VK_NUMPAD3;
3266 nParam = VK_NUMPAD4;
3269 nParam = VK_NUMPAD5;
3272 nParam = VK_NUMPAD6;
3275 nParam = VK_NUMPAD7;
3278 nParam = VK_NUMPAD8;
3281 nParam = VK_NUMPAD9;
3284 nParam = VK_DECIMAL;
3288 if (keystate[VK_NUMLOCK] & 1)
3295 /* If a key is pressed and AltGr is not active */
3296 if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
3297 /* Okay, prepare for most alts then ... */
3301 /* Lets see if it's a pattern we know all about ... */
3302 if (wParam == VK_PRIOR && shift_state == 1) {
3303 SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
3306 if (wParam == VK_PRIOR && shift_state == 2) {
3307 SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
3310 if (wParam == VK_NEXT && shift_state == 1) {
3311 SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
3314 if (wParam == VK_NEXT && shift_state == 2) {
3315 SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
3318 if (wParam == VK_INSERT && shift_state == 1) {
3319 term_do_paste(term);
3322 if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
3325 if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
3326 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
3329 if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
3330 (cfg.resize_action != RESIZE_DISABLED)) {
3331 if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
3335 /* Control-Numlock for app-keypad mode switch */
3336 if (wParam == VK_PAUSE && shift_state == 2) {
3337 term->app_keypad_keys ^= 1;
3341 /* Nethack keypad */
3342 if (cfg.nethack_keypad && !left_alt) {
3345 *p++ = shift_state ? 'B' : 'b';
3348 *p++ = shift_state ? 'J' : 'j';
3351 *p++ = shift_state ? 'N' : 'n';
3354 *p++ = shift_state ? 'H' : 'h';
3357 *p++ = shift_state ? '.' : '.';
3360 *p++ = shift_state ? 'L' : 'l';
3363 *p++ = shift_state ? 'Y' : 'y';
3366 *p++ = shift_state ? 'K' : 'k';
3369 *p++ = shift_state ? 'U' : 'u';
3374 /* Application Keypad */
3378 if (cfg.funky_type == 3 ||
3379 (cfg.funky_type <= 1 &&
3380 term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
3394 if (term->app_keypad_keys && !cfg.no_applic_k)
3431 if (cfg.funky_type == 2) {
3436 } else if (shift_state)
3443 if (cfg.funky_type == 2)
3447 if (cfg.funky_type == 2)
3451 if (cfg.funky_type == 2)
3456 if (HIWORD(lParam) & KF_EXTENDED)
3461 if (term->vt52_mode) {
3462 if (xkey >= 'P' && xkey <= 'S')
3463 p += sprintf((char *) p, "\x1B%c", xkey);
3465 p += sprintf((char *) p, "\x1B?%c", xkey);
3467 p += sprintf((char *) p, "\x1BO%c", xkey);
3472 if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
3473 *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
3477 if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
3478 /* We do the opposite of what is configured */
3479 *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
3483 if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
3489 if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
3493 if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
3497 if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
3502 if (wParam == VK_PAUSE) { /* Break/Pause */
3507 /* Control-2 to Control-8 are special */
3508 if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
3509 *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
3512 if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) {
3516 if (shift_state == 2 && wParam == 0xDF) {
3520 if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
3527 * Next, all the keys that do tilde codes. (ESC '[' nn '~',
3528 * for integer decimal nn.)
3530 * We also deal with the weird ones here. Linux VCs replace F1
3531 * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
3532 * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
3538 code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
3541 code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
3544 code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
3547 code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
3550 code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
3553 code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
3556 code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
3559 code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
3562 code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
3565 code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
3598 if ((shift_state&2) == 0) switch (wParam) {
3618 /* Reorder edit keys to physical order */
3619 if (cfg.funky_type == 3 && code <= 6)
3620 code = "\0\2\1\4\5\3\6"[code];
3622 if (term->vt52_mode && code > 0 && code <= 6) {
3623 p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
3627 if (cfg.funky_type == 5 && /* SCO function keys */
3628 code >= 11 && code <= 34) {
3629 char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
3632 case VK_F1: index = 0; break;
3633 case VK_F2: index = 1; break;
3634 case VK_F3: index = 2; break;
3635 case VK_F4: index = 3; break;
3636 case VK_F5: index = 4; break;
3637 case VK_F6: index = 5; break;
3638 case VK_F7: index = 6; break;
3639 case VK_F8: index = 7; break;
3640 case VK_F9: index = 8; break;
3641 case VK_F10: index = 9; break;
3642 case VK_F11: index = 10; break;
3643 case VK_F12: index = 11; break;
3645 if (keystate[VK_SHIFT] & 0x80) index += 12;
3646 if (keystate[VK_CONTROL] & 0x80) index += 24;
3647 p += sprintf((char *) p, "\x1B[%c", codes[index]);
3650 if (cfg.funky_type == 5 && /* SCO small keypad */
3651 code >= 1 && code <= 6) {
3652 char codes[] = "HL.FIG";
3656 p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
3660 if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
3666 if (term->vt52_mode)
3667 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
3670 sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
3673 if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
3674 p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
3677 if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
3678 if (term->vt52_mode)
3679 p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
3681 p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
3684 if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
3685 p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
3689 p += sprintf((char *) p, "\x1B[%d~", code);
3694 * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
3695 * some reason seems to send VK_CLEAR to Windows...).
3717 if (term->vt52_mode)
3718 p += sprintf((char *) p, "\x1B%c", xkey);
3720 int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
3723 * RDB: VT100 & VT102 manuals both state the
3724 * app cursor keys only work if the app keypad
3727 * SGT: That may well be true, but xterm
3728 * disagrees and so does at least one
3729 * application, so I've #if'ed this out and the
3730 * behaviour is back to PuTTY's original: app
3731 * cursor and app keypad are independently
3732 * switchable modes. If anyone complains about
3733 * _this_ I'll have to put in a configurable
3736 if (!term->app_keypad_keys)
3739 /* Useful mapping of Ctrl-arrows */
3740 if (shift_state == 2)
3744 p += sprintf((char *) p, "\x1BO%c", xkey);
3746 p += sprintf((char *) p, "\x1B[%c", xkey);
3753 * Finally, deal with Return ourselves. (Win95 seems to
3754 * foul it up when Alt is pressed, for some reason.)
3756 if (wParam == VK_RETURN) { /* Return */
3762 if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
3763 alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
3768 /* Okay we've done everything interesting; let windows deal with
3769 * the boring stuff */
3773 /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
3774 if(cfg.xlat_capslockcyr && keystate[VK_CAPITAL] != 0) {
3776 keystate[VK_CAPITAL] = 0;
3779 r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
3780 #ifdef SHOW_TOASCII_RESULT
3781 if (r == 1 && !key_down) {
3783 if (in_utf(term) || ucsdata.dbcs_screenfont)
3784 debug((", (U+%04x)", alt_sum));
3786 debug((", LCH(%d)", alt_sum));
3788 debug((", ACH(%d)", keys[0]));
3793 for (r1 = 0; r1 < r; r1++) {
3794 debug(("%s%d", r1 ? "," : "", keys[r1]));
3803 * Interrupt an ongoing paste. I'm not sure this is
3804 * sensible, but for the moment it's preferable to
3805 * having to faff about buffering things.
3810 for (i = 0; i < r; i++) {
3811 unsigned char ch = (unsigned char) keys[i];
3813 if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
3818 if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
3822 if ((nc = check_compose(compose_char, ch)) == -1) {
3823 MessageBeep(MB_ICONHAND);
3827 term_seen_key_event(term);
3828 luni_send(ldisc, &keybuf, 1, 1);
3836 if (in_utf(term) || ucsdata.dbcs_screenfont) {
3838 term_seen_key_event(term);
3839 luni_send(ldisc, &keybuf, 1, 1);
3841 ch = (char) alt_sum;
3843 * We need not bother about stdin
3844 * backlogs here, because in GUI PuTTY
3845 * we can't do anything about it
3846 * anyway; there's no means of asking
3847 * Windows to hold off on KEYDOWN
3848 * messages. We _have_ to buffer
3849 * everything we're sent.
3851 term_seen_key_event(term);
3852 ldisc_send(ldisc, &ch, 1, 1);
3856 term_seen_key_event(term);
3857 lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
3859 if(capsOn && ch < 0x80) {
3862 cbuf[1] = xlat_uskbd2cyrllic(ch);
3863 term_seen_key_event(term);
3864 luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
3869 term_seen_key_event(term);
3870 lpage_send(ldisc, kbd_codepage,
3871 cbuf+!left_alt, 1+!!left_alt, 1);
3877 /* This is so the ALT-Numpad and dead keys work correctly. */
3882 /* If we're definitly not building up an ALT-54321 then clear it */
3885 /* If we will be using alt_sum fix the 256s */
3886 else if (keys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
3891 * ALT alone may or may not want to bring up the System menu.
3892 * If it's not meant to, we return 0 on presses or releases of
3893 * ALT, to show that we've swallowed the keystroke. Otherwise
3894 * we return -1, which means Windows will give the keystroke
3895 * its default handling (i.e. bring up the System menu).
3897 if (wParam == VK_MENU && !cfg.alt_only)
3903 void request_paste(void *frontend)
3906 * In Windows, pasting is synchronous: we can read the
3907 * clipboard with no difficulty, so request_paste() can just go
3910 term_do_paste(term);
3913 void set_title(void *frontend, char *title)
3916 window_name = smalloc(1 + strlen(title));
3917 strcpy(window_name, title);
3918 if (cfg.win_name_always || !IsIconic(hwnd))
3919 SetWindowText(hwnd, title);
3922 void set_icon(void *frontend, char *title)
3925 icon_name = smalloc(1 + strlen(title));
3926 strcpy(icon_name, title);
3927 if (!cfg.win_name_always && IsIconic(hwnd))
3928 SetWindowText(hwnd, title);
3931 void set_sbar(void *frontend, int total, int start, int page)
3935 if (is_full_screen() ? !cfg.scrollbar_in_fullscreen : !cfg.scrollbar)
3938 si.cbSize = sizeof(si);
3939 si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
3941 si.nMax = total - 1;
3945 SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
3948 Context get_ctx(void *frontend)
3954 SelectPalette(hdc, pal, FALSE);
3960 void free_ctx(Context ctx)
3962 SelectPalette(ctx, GetStockObject(DEFAULT_PALETTE), FALSE);
3963 ReleaseDC(hwnd, ctx);
3966 static void real_palette_set(int n, int r, int g, int b)
3969 logpal->palPalEntry[n].peRed = r;
3970 logpal->palPalEntry[n].peGreen = g;
3971 logpal->palPalEntry[n].peBlue = b;
3972 logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE;
3973 colours[n] = PALETTERGB(r, g, b);
3974 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
3976 colours[n] = RGB(r, g, b);
3979 void palette_set(void *frontend, int n, int r, int g, int b)
3981 static const int first[21] = {
3982 0, 2, 4, 6, 8, 10, 12, 14,
3983 1, 3, 5, 7, 9, 11, 13, 15,
3986 real_palette_set(first[n], r, g, b);
3988 real_palette_set(first[n] + 1, r, g, b);
3990 HDC hdc = get_ctx(frontend);
3991 UnrealizeObject(pal);
3992 RealizePalette(hdc);
3997 void palette_reset(void *frontend)
4001 for (i = 0; i < NCOLOURS; i++) {
4003 logpal->palPalEntry[i].peRed = defpal[i].rgbtRed;
4004 logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen;
4005 logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue;
4006 logpal->palPalEntry[i].peFlags = 0;
4007 colours[i] = PALETTERGB(defpal[i].rgbtRed,
4008 defpal[i].rgbtGreen,
4009 defpal[i].rgbtBlue);
4011 colours[i] = RGB(defpal[i].rgbtRed,
4012 defpal[i].rgbtGreen, defpal[i].rgbtBlue);
4017 SetPaletteEntries(pal, 0, NCOLOURS, logpal->palPalEntry);
4018 hdc = get_ctx(frontend);
4019 RealizePalette(hdc);
4024 void write_aclip(void *frontend, char *data, int len, int must_deselect)
4029 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1);
4032 lock = GlobalLock(clipdata);
4035 memcpy(lock, data, len);
4036 ((unsigned char *) lock)[len] = 0;
4037 GlobalUnlock(clipdata);
4040 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4042 if (OpenClipboard(hwnd)) {
4044 SetClipboardData(CF_TEXT, clipdata);
4047 GlobalFree(clipdata);
4050 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4054 * Note: unlike write_aclip() this will not append a nul.
4056 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
4058 HGLOBAL clipdata, clipdata2, clipdata3;
4060 void *lock, *lock2, *lock3;
4062 len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
4064 clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
4065 len * sizeof(wchar_t));
4066 clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
4068 if (!clipdata || !clipdata2) {
4070 GlobalFree(clipdata);
4072 GlobalFree(clipdata2);
4075 if (!(lock = GlobalLock(clipdata)))
4077 if (!(lock2 = GlobalLock(clipdata2)))
4080 memcpy(lock, data, len * sizeof(wchar_t));
4081 WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
4083 if (cfg.rtf_paste) {
4084 wchar_t unitab[256];
4086 unsigned char *tdata = (unsigned char *)lock2;
4087 wchar_t *udata = (wchar_t *)lock;
4088 int rtflen = 0, uindex = 0, tindex = 0;
4090 int multilen, blen, alen, totallen, i;
4091 char before[16], after[4];
4093 get_unitab(CP_ACP, unitab, 0);
4095 rtfsize = 100 + strlen(cfg.font);
4096 rtf = smalloc(rtfsize);
4097 sprintf(rtf, "{\\rtf1\\ansi%d{\\fonttbl\\f0\\fmodern %s;}\\f0",
4098 GetACP(), cfg.font);
4099 rtflen = strlen(rtf);
4102 * We want to construct a piece of RTF that specifies the
4103 * same Unicode text. To do this we will read back in
4104 * parallel from the Unicode data in `udata' and the
4105 * non-Unicode data in `tdata'. For each character in
4106 * `tdata' which becomes the right thing in `udata' when
4107 * looked up in `unitab', we just copy straight over from
4108 * tdata. For each one that doesn't, we must WCToMB it
4109 * individually and produce a \u escape sequence.
4111 * It would probably be more robust to just bite the bullet
4112 * and WCToMB each individual Unicode character one by one,
4113 * then MBToWC each one back to see if it was an accurate
4114 * translation; but that strikes me as a horrifying number
4115 * of Windows API calls so I want to see if this faster way
4116 * will work. If it screws up badly we can always revert to
4117 * the simple and slow way.
4119 while (tindex < len2 && uindex < len &&
4120 tdata[tindex] && udata[uindex]) {
4121 if (tindex + 1 < len2 &&
4122 tdata[tindex] == '\r' &&
4123 tdata[tindex+1] == '\n') {
4127 if (unitab[tdata[tindex]] == udata[uindex]) {
4133 multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1,
4134 NULL, 0, NULL, NULL);
4135 if (multilen != 1) {
4136 blen = sprintf(before, "{\\uc%d\\u%d", multilen,
4138 alen = 1; strcpy(after, "}");
4140 blen = sprintf(before, "\\u%d", udata[uindex]);
4141 alen = 0; after[0] = '\0';
4144 assert(tindex + multilen <= len2);
4145 totallen = blen + alen;
4146 for (i = 0; i < multilen; i++) {
4147 if (tdata[tindex+i] == '\\' ||
4148 tdata[tindex+i] == '{' ||
4149 tdata[tindex+i] == '}')
4151 else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A)
4152 totallen += 6; /* \par\r\n */
4153 else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20)
4159 if (rtfsize < rtflen + totallen + 3) {
4160 rtfsize = rtflen + totallen + 512;
4161 rtf = srealloc(rtf, rtfsize);
4164 strcpy(rtf + rtflen, before); rtflen += blen;
4165 for (i = 0; i < multilen; i++) {
4166 if (tdata[tindex+i] == '\\' ||
4167 tdata[tindex+i] == '{' ||
4168 tdata[tindex+i] == '}') {
4169 rtf[rtflen++] = '\\';
4170 rtf[rtflen++] = tdata[tindex+i];
4171 } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) {
4172 rtflen += sprintf(rtf+rtflen, "\\par\r\n");
4173 } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) {
4174 rtflen += sprintf(rtf+rtflen, "\\'%02x", tdata[tindex+i]);
4176 rtf[rtflen++] = tdata[tindex+i];
4179 strcpy(rtf + rtflen, after); rtflen += alen;
4185 strcpy(rtf + rtflen, "}");
4188 clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtflen);
4189 if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) {
4191 GlobalUnlock(clipdata3);
4197 GlobalUnlock(clipdata);
4198 GlobalUnlock(clipdata2);
4201 SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
4203 if (OpenClipboard(hwnd)) {
4205 SetClipboardData(CF_UNICODETEXT, clipdata);
4206 SetClipboardData(CF_TEXT, clipdata2);
4208 SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3);
4211 GlobalFree(clipdata);
4212 GlobalFree(clipdata2);
4216 SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
4219 void get_clip(void *frontend, wchar_t ** p, int *len)
4221 static HGLOBAL clipdata = NULL;
4222 static wchar_t *converted = 0;
4231 GlobalUnlock(clipdata);
4234 } else if (OpenClipboard(NULL)) {
4235 if ((clipdata = GetClipboardData(CF_UNICODETEXT))) {
4237 *p = GlobalLock(clipdata);
4239 for (p2 = *p; *p2; p2++);
4243 } else if ( (clipdata = GetClipboardData(CF_TEXT)) ) {
4247 s = GlobalLock(clipdata);
4248 i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
4249 *p = converted = smalloc(i * sizeof(wchar_t));
4250 MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
4263 * Move `lines' lines from position `from' to position `to' in the
4266 void optimised_move(void *frontend, int to, int from, int lines)
4271 min = (to < from ? to : from);
4272 max = to + from - min;
4274 r.left = offset_width;
4275 r.right = offset_width + term->cols * font_width;
4276 r.top = offset_height + min * font_height;
4277 r.bottom = offset_height + (max + lines) * font_height;
4278 ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
4283 * Print a message box and perform a fatal exit.
4285 void fatalbox(char *fmt, ...)
4291 vsprintf(stuff, fmt, ap);
4293 MessageBox(hwnd, stuff, "PuTTY Fatal Error", MB_ICONERROR | MB_OK);
4298 * Print a modal (Really Bad) message box and perform a fatal exit.
4300 void modalfatalbox(char *fmt, ...)
4306 vsprintf(stuff, fmt, ap);
4308 MessageBox(hwnd, stuff, "PuTTY Fatal Error",
4309 MB_SYSTEMMODAL | MB_ICONERROR | MB_OK);
4314 * Manage window caption / taskbar flashing, if enabled.
4315 * 0 = stop, 1 = maintain, 2 = start
4317 static void flash_window(int mode)
4319 static long last_flash = 0;
4320 static int flashing = 0;
4321 if ((mode == 0) || (cfg.beep_ind == B_IND_DISABLED)) {
4324 FlashWindow(hwnd, FALSE);
4328 } else if (mode == 2) {
4331 last_flash = GetTickCount();
4333 FlashWindow(hwnd, TRUE);
4336 } else if ((mode == 1) && (cfg.beep_ind == B_IND_FLASH)) {
4339 long now = GetTickCount();
4340 long fdiff = now - last_flash;
4341 if (fdiff < 0 || fdiff > 450) {
4343 FlashWindow(hwnd, TRUE); /* toggle */
4352 void beep(void *frontend, int mode)
4354 if (mode == BELL_DEFAULT) {
4356 * For MessageBeep style bells, we want to be careful of
4357 * timing, because they don't have the nice property of
4358 * PlaySound bells that each one cancels the previous
4359 * active one. So we limit the rate to one per 50ms or so.
4361 static long lastbeep = 0;
4364 beepdiff = GetTickCount() - lastbeep;
4365 if (beepdiff >= 0 && beepdiff < 50)
4369 * The above MessageBeep call takes time, so we record the
4370 * time _after_ it finishes rather than before it starts.
4372 lastbeep = GetTickCount();
4373 } else if (mode == BELL_WAVEFILE) {
4374 if (!PlaySound(cfg.bell_wavefile, NULL, SND_ASYNC | SND_FILENAME)) {
4375 char buf[sizeof(cfg.bell_wavefile) + 80];
4376 sprintf(buf, "Unable to play sound file\n%s\n"
4377 "Using default sound instead", cfg.bell_wavefile);
4378 MessageBox(hwnd, buf, "PuTTY Sound Error",
4379 MB_OK | MB_ICONEXCLAMATION);
4380 cfg.beep = BELL_DEFAULT;
4383 /* Otherwise, either visual bell or disabled; do nothing here */
4384 if (!term->has_focus) {
4385 flash_window(2); /* start */
4390 * Minimise or restore the window in response to a server-side
4393 void set_iconic(void *frontend, int iconic)
4395 if (IsIconic(hwnd)) {
4397 ShowWindow(hwnd, SW_RESTORE);
4400 ShowWindow(hwnd, SW_MINIMIZE);
4405 * Move the window in response to a server-side request.
4407 void move_window(void *frontend, int x, int y)
4409 if (cfg.resize_action == RESIZE_DISABLED ||
4410 cfg.resize_action == RESIZE_FONT ||
4414 SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
4418 * Move the window to the top or bottom of the z-order in response
4419 * to a server-side request.
4421 void set_zorder(void *frontend, int top)
4423 if (cfg.alwaysontop)
4424 return; /* ignore */
4425 SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0,
4426 SWP_NOMOVE | SWP_NOSIZE);
4430 * Refresh the window in response to a server-side request.
4432 void refresh_window(void *frontend)
4434 InvalidateRect(hwnd, NULL, TRUE);
4438 * Maximise or restore the window in response to a server-side
4441 void set_zoomed(void *frontend, int zoomed)
4443 if (IsZoomed(hwnd)) {
4445 ShowWindow(hwnd, SW_RESTORE);
4448 ShowWindow(hwnd, SW_MAXIMIZE);
4453 * Report whether the window is iconic, for terminal reports.
4455 int is_iconic(void *frontend)
4457 return IsIconic(hwnd);
4461 * Report the window's position, for terminal reports.
4463 void get_window_pos(void *frontend, int *x, int *y)
4466 GetWindowRect(hwnd, &r);
4472 * Report the window's pixel size, for terminal reports.
4474 void get_window_pixels(void *frontend, int *x, int *y)
4477 GetWindowRect(hwnd, &r);
4478 *x = r.right - r.left;
4479 *y = r.bottom - r.top;
4483 * Return the window or icon title.
4485 char *get_window_title(void *frontend, int icon)
4487 return icon ? icon_name : window_name;
4491 * See if we're in full-screen mode.
4493 int is_full_screen()
4495 if (!IsZoomed(hwnd))
4497 if (GetWindowLong(hwnd, GWL_STYLE) & WS_CAPTION)
4502 /* Get the rect/size of a full screen window using the nearest available
4503 * monitor in multimon systems; default to something sensible if only
4504 * one monitor is present. */
4505 static int get_fullscreen_rect(RECT * ss)
4507 #ifdef MONITOR_DEFAULTTONEAREST
4510 mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
4511 mi.cbSize = sizeof(mi);
4512 GetMonitorInfo(mon, &mi);
4514 /* structure copy */
4518 /* could also use code like this:
4519 ss->left = ss->top = 0;
4520 ss->right = GetSystemMetrics(SM_CXSCREEN);
4521 ss->bottom = GetSystemMetrics(SM_CYSCREEN);
4523 return GetClientRect(GetDesktopWindow(), ss);
4529 * Go full-screen. This should only be called when we are already
4532 void make_full_screen()
4537 assert(IsZoomed(hwnd));
4539 if (is_full_screen())
4542 /* Remove the window furniture. */
4543 style = GetWindowLong(hwnd, GWL_STYLE);
4544 style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
4545 if (cfg.scrollbar_in_fullscreen)
4546 style |= WS_VSCROLL;
4548 style &= ~WS_VSCROLL;
4549 SetWindowLong(hwnd, GWL_STYLE, style);
4551 /* Resize ourselves to exactly cover the nearest monitor. */
4552 get_fullscreen_rect(&ss);
4553 SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
4558 /* Tick the menu item in the System menu. */
4559 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4564 * Clear the full-screen attributes.
4566 void clear_full_screen()
4568 DWORD oldstyle, style;
4570 /* Reinstate the window furniture. */
4571 style = oldstyle = GetWindowLong(hwnd, GWL_STYLE);
4572 style |= WS_CAPTION | WS_BORDER;
4573 if (cfg.resize_action == RESIZE_DISABLED)
4574 style &= ~WS_THICKFRAME;
4576 style |= WS_THICKFRAME;
4578 style |= WS_VSCROLL;
4580 style &= ~WS_VSCROLL;
4581 if (style != oldstyle) {
4582 SetWindowLong(hwnd, GWL_STYLE, style);
4583 SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
4584 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
4588 /* Untick the menu item in the System menu. */
4589 CheckMenuItem(GetSystemMenu(hwnd, FALSE), IDM_FULLSCREEN,
4594 * Toggle full-screen mode.
4596 void flip_full_screen()
4598 if (is_full_screen()) {
4599 ShowWindow(hwnd, SW_RESTORE);
4600 } else if (IsZoomed(hwnd)) {
4603 SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0);
4604 ShowWindow(hwnd, SW_MAXIMIZE);
4608 void frontend_keypress(void *handle)
4611 * Keypress termination in non-Close-On-Exit mode is not
4612 * currently supported in PuTTY proper, because the window
4613 * always has a perfectly good Close button anyway. So we do