1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-04-10 15:48:06 -05:00

Windows: runtime switch between Unicode and ANSI windows.

Turns out that PuTTY hasn't run successfully on legacy Windows since
0.66, in spite of an ongoing intention to keep it working. Among the
reasons for this is that CreateWindowExW simply fails with
ERROR_CALL_NOT_IMPLEMENTED: apparently Win95 and its ilk just didn't
have fully-Unicode windows as an option.

Fixed by resurrecting the previous code from the git history (in
particular, code removed by commit 67e5ceb9a8e6bc2 was useful), and
including it as a runtime alternative.

One subtlety was that I found I had to name the A and W window classes
differently (by appending ".ansi" to the A one): apparently they
occupy the same namespace even though the names are in different
character sets, so if you somehow manage to register both classes,
they'll collide with each other without that tweak.
This commit is contained in:
Simon Tatham 2022-03-12 06:43:59 +00:00
parent 01c007121a
commit f500d24a95

View File

@ -450,6 +450,72 @@ static void close_session(void *ignored_context)
}
}
/*
* Some machinery to deal with switching the window type between ANSI
* and Unicode. We prefer Unicode, but some PuTTY builds still try to
* run on machines so old that they don't support that mode. So we're
* prepared to fall back to an ANSI window if we have to. For this
* purpose, we swap out a few Windows API functions, and wrap
* SetWindowText so that if we're not in Unicode mode we first convert
* the wide string we're given.
*/
static bool unicode_window;
static BOOL (WINAPI *sw_PeekMessage)(LPMSG, HWND, UINT, UINT, UINT);
static LRESULT (WINAPI *sw_DispatchMessage)(const MSG *);
static LRESULT (WINAPI *sw_DefWindowProc)(HWND, UINT, WPARAM, LPARAM);
static void sw_SetWindowText(HWND hwnd, wchar_t *text)
{
if (unicode_window) {
SetWindowTextW(hwnd, text);
} else {
char *mb = dup_wc_to_mb(DEFAULT_CODEPAGE, 0, text, "?", &ucsdata);
SetWindowTextA(hwnd, mb);
sfree(mb);
}
}
static HINSTANCE hprev;
/*
* Also, registering window classes has to be done in a fiddly way.
*/
#define SETUP_WNDCLASS(wndclass, classname) do { \
wndclass.style = 0; \
wndclass.lpfnWndProc = WndProc; \
wndclass.cbClsExtra = 0; \
wndclass.cbWndExtra = 0; \
wndclass.hInstance = hinst; \
wndclass.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_MAINICON)); \
wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM); \
wndclass.hbrBackground = NULL; \
wndclass.lpszMenuName = NULL; \
wndclass.lpszClassName = classname; \
} while (0)
wchar_t *terminal_window_class_w(void)
{
static wchar_t *classname = NULL;
if (!classname)
classname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
if (!hprev) {
WNDCLASSW wndclassw;
SETUP_WNDCLASS(wndclassw, classname);
RegisterClassW(&wndclassw);
}
return classname;
}
char *terminal_window_class_a(void)
{
static char *classname = NULL;
if (!classname)
classname = dupcat(appname, ".ansi");
if (!hprev) {
WNDCLASSA wndclassa;
SETUP_WNDCLASS(wndclassa, classname);
RegisterClassA(&wndclassa);
}
return classname;
}
const unsigned cmdline_tooltype =
TOOLTYPE_HOST_ARG |
TOOLTYPE_PORT_ARG |
@ -466,6 +532,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
dll_hijacking_protection();
hinst = inst;
hprev = prev;
sk_init();
@ -514,23 +581,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
*/
gui_term_process_cmdline(conf, cmdline);
if (!prev) {
WNDCLASSW wndclass;
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = inst;
wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON));
wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM);
wndclass.hbrBackground = NULL;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
RegisterClassW(&wndclass);
}
memset(&ucsdata, 0, sizeof(ucsdata));
conf_cache_data();
@ -577,9 +627,38 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
exwinmode |= WS_EX_TOPMOST;
if (conf_get_bool(conf, CONF_sunken_edge))
exwinmode |= WS_EX_CLIENTEDGE;
#ifdef TEST_ANSI_WINDOW
/* For developer testing of ANSI window support, pretend
* CreateWindowExW failed */
wgs.term_hwnd = NULL;
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
#else
unicode_window = true;
sw_PeekMessage = PeekMessageW;
sw_DispatchMessage = DispatchMessageW;
sw_DefWindowProc = DefWindowProcW;
wgs.term_hwnd = CreateWindowExW(
exwinmode, uappname, uappname, winmode, CW_USEDEFAULT,
CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL);
exwinmode, terminal_window_class_w(), uappname,
winmode, CW_USEDEFAULT, CW_USEDEFAULT,
guess_width, guess_height, NULL, NULL, inst, NULL);
#endif
#if defined LEGACY_WINDOWS || defined TEST_ANSI_WINDOW
if (!wgs.term_hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
/* Fall back to an ANSI window, swapping in all the ANSI
* window message handling functions */
unicode_window = false;
sw_PeekMessage = PeekMessageA;
sw_DispatchMessage = DispatchMessageA;
sw_DefWindowProc = DefWindowProcA;
wgs.term_hwnd = CreateWindowExA(
exwinmode, terminal_window_class_a(), appname,
winmode, CW_USEDEFAULT, CW_USEDEFAULT,
guess_width, guess_height, NULL, NULL, inst, NULL);
}
#endif
if (!wgs.term_hwnd) {
modalfatalbox("Unable to create terminal window: %s",
win_strerror(GetLastError()));
@ -776,13 +855,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
handle_wait_activate(hwl, n - WAIT_OBJECT_0);
handle_wait_list_free(hwl);
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
while (sw_PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
goto finished; /* two-level break */
HWND logbox = event_log_window();
if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg)))
DispatchMessageW(&msg);
sw_DispatchMessage(&msg);
/*
* WM_NETEVENT messages seem to jump ahead of others in
@ -2202,7 +2281,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
* within an interactive scrollbar-drag can be handled
* differently. */
in_scrollbar_loop = true;
LRESULT result = DefWindowProcW(hwnd, message, wParam, lParam);
LRESULT result = sw_DefWindowProc(
hwnd, message, wParam, lParam);
in_scrollbar_loop = false;
return result;
}
@ -2996,11 +3076,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
term, r.right - r.left, r.bottom - r.top);
}
if (wParam == SIZE_MINIMIZED)
SetWindowTextW(hwnd,
conf_get_bool(conf, CONF_win_name_always) ?
window_name : icon_name);
sw_SetWindowText(hwnd,
conf_get_bool(conf, CONF_win_name_always) ?
window_name : icon_name);
if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
SetWindowTextW(hwnd, window_name);
sw_SetWindowText(hwnd, window_name);
if (wParam == SIZE_RESTORED) {
processed_resize = false;
clear_full_screen();
@ -3222,7 +3302,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} else {
len = TranslateKey(message, wParam, lParam, buf);
if (len == -1)
return DefWindowProcW(hwnd, message, wParam, lParam);
return sw_DefWindowProc(hwnd, message, wParam, lParam);
if (len != 0) {
/*
@ -3320,7 +3400,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
* post the things to us as part of a macro manoeuvre,
* we're ready to cope.
*/
{
if (unicode_window) {
static wchar_t pending_surrogate = 0;
wchar_t c = wParam;
@ -3334,6 +3414,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} else if (!IS_SURROGATE(c)) {
term_keyinputw(term, &c, 1);
}
} else {
char c = (unsigned char)wParam;
term_seen_key_event(term);
if (ldisc)
term_keyinput(term, CP_ACP, &c, 1);
}
return 0;
case WM_SYSCOLORCHANGE:
@ -3409,7 +3494,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
* Any messages we don't process completely above are passed through to
* DefWindowProc() for default processing.
*/
return DefWindowProcW(hwnd, message, wParam, lParam);
return sw_DefWindowProc(hwnd, message, wParam, lParam);
}
/*
@ -4771,7 +4856,7 @@ static void wintw_set_title(TermWin *tw, const char *title, int codepage)
sfree(window_name);
window_name = new_window_name;
if (conf_get_bool(conf, CONF_win_name_always) || !IsIconic(wgs.term_hwnd))
SetWindowTextW(wgs.term_hwnd, window_name);
sw_SetWindowText(wgs.term_hwnd, window_name);
}
static void wintw_set_icon_title(TermWin *tw, const char *title, int codepage)
@ -4784,7 +4869,7 @@ static void wintw_set_icon_title(TermWin *tw, const char *title, int codepage)
sfree(icon_name);
icon_name = new_icon_name;
if (!conf_get_bool(conf, CONF_win_name_always) && IsIconic(wgs.term_hwnd))
SetWindowTextW(wgs.term_hwnd, icon_name);
sw_SetWindowText(wgs.term_hwnd, icon_name);
}
static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page)