Bug fixes for reclaiming focus.
This commit is contained in:
1 parent
ea509509e4
commit
52a3fddc77
6 files changed
+30
-43
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# Maintainer: Storm Dragon <storm_dragon@stormux.org>
|
||||
pkgname=wine-access-git
|
||||
pkgver=r43.ge3de71e
|
||||
pkgver=r44.gea50950
|
||||
pkgrel=1
|
||||
pkgdesc='Wine accessibility helper with MSAA, NVDA Controller, and Tolk compatibility for screen readers'
|
||||
arch=('x86_64')
|
||||
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
// Recover a virtual desktop which regained X11 focus with no Win32 foreground.
|
||||
// Leave existing focus, other desktops and ambiguous application sets alone.
|
||||
// Returns whether activation was queued; the application restores control focus.
|
||||
bool restore_virtual_desktop_focus();
|
||||
+8
-17
@@ -112,26 +112,17 @@ bool wine_x11_restore_focus(HWND window) {
|
||||
const uint64_t deadline = GetTickCount64() + 20;
|
||||
auto tree = reply<xcb_query_tree_reply_t>(xcb_query_tree(connection.value, xid).sequence, deadline);
|
||||
if (!tree || tree->parent != desktop) return false;
|
||||
// Native focus informs Wine's application thread of activation. A helper's
|
||||
// SetForegroundWindow can be denied by the Win32 foreground-lock policy.
|
||||
constexpr char protocolsName[] = "WM_PROTOCOLS";
|
||||
constexpr char focusName[] = "WM_TAKE_FOCUS";
|
||||
auto protocols = reply<xcb_intern_atom_reply_t>(xcb_intern_atom(connection.value, 1,
|
||||
sizeof(protocolsName) - 1, protocolsName).sequence, deadline);
|
||||
auto takeFocus = reply<xcb_intern_atom_reply_t>(xcb_intern_atom(connection.value, 1,
|
||||
sizeof(focusName) - 1, focusName).sequence, deadline);
|
||||
if (!protocols || !protocols->atom || !takeFocus || !takeFocus->atom) return false;
|
||||
if (GetForegroundWindow() || !IsWindowVisible(window) || !IsWindowEnabled(window) ||
|
||||
IsIconic(window) || !wine_x11_desktop_focused(window))
|
||||
return false;
|
||||
xcb_client_message_event_t event{};
|
||||
event.response_type = XCB_CLIENT_MESSAGE;
|
||||
event.format = 32;
|
||||
event.window = xid;
|
||||
event.type = protocols->atom;
|
||||
event.data.data32[0] = takeFocus->atom;
|
||||
event.data.data32[1] = XCB_CURRENT_TIME;
|
||||
xcb_send_event(connection.value, 0, xid, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
|
||||
// WM_TAKE_FOCUS can be ignored while Wine awaits WM_STATE, which can remain
|
||||
// unset for virtual-desktop children. Queue activation on the owner's thread:
|
||||
// a helper-thread SetForegroundWindow can be denied by Wine's input policy.
|
||||
// Async dispatch avoids waiting on an unresponsive application's window proc.
|
||||
if (!SetWindowPos(window, HWND_TOP, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_ASYNCWINDOWPOS) ||
|
||||
!wine_x11_desktop_focused(window))
|
||||
return false;
|
||||
xcb_set_input_focus(connection.value, XCB_INPUT_FOCUS_PARENT, xid, XCB_CURRENT_TIME);
|
||||
auto focus = reply<xcb_get_input_focus_reply_t>(xcb_get_input_focus(connection.value).sequence, deadline);
|
||||
return focus && focus->focus == xid;
|
||||
|
||||
+2
-1
@@ -11,5 +11,6 @@ bool review_x11_geometry(HWND window, RECT &bounds, POINT &origin);
|
||||
// the verified application's X11 child hierarchy.
|
||||
bool wine_x11_desktop_focused(HWND window = nullptr);
|
||||
|
||||
// Deliver native focus to the application's actual child of this desktop.
|
||||
// Queue Win32 activation on the verified child's thread and set native focus.
|
||||
// A true result means dispatched; control focus arrives asynchronously.
|
||||
bool wine_x11_restore_focus(HWND window);
|
||||
+13
-4
@@ -203,10 +203,19 @@ activation, then checks recovery of its native foreground and button focus.
|
||||
It also checks Linux focus, mismatched window-manager/keyboard focus, disabled
|
||||
windows, multiple visible applications, hidden windows, existing foreground,
|
||||
and repeated recovery. It also reproduces X11 focus on the launcher child
|
||||
with no Win32 foreground, as seen after AudioQuake exits: recovery must send
|
||||
Wine's activation notification even when X11 focus already selects that child.
|
||||
The fixture acknowledges ICCCM window visibility on
|
||||
the private display; the runner propagates the fixture's actual exit status.
|
||||
with no Win32 foreground, as seen after AudioQuake exits. Recovery must restore
|
||||
both Win32 activation/control focus and native keyboard focus, even when X11
|
||||
already selects that child. The fixture deliberately leaves `WM_STATE` unset,
|
||||
matching Wine's real virtual-desktop children. Adding a synthetic window-manager
|
||||
acknowledgement masks the failure: native `WM_TAKE_FOCUS` alone can otherwise be
|
||||
ignored while Wine awaits that state change. The runner propagates the fixture's
|
||||
actual exit status.
|
||||
|
||||
Recovery queues `SetWindowPos` activation on the application's own window
|
||||
thread and sets native X11 focus without changing its size or position. It
|
||||
does not wait on the application's window procedure. A dispatched request
|
||||
alone is not acceptance: the fixture then checks Win32 foreground and the
|
||||
restored button focus after the application processes activation.
|
||||
|
||||
After installing the updated helper and restarting the reader, open AudioQuake,
|
||||
play and close a game, then Tab and Shift+Tab through the launcher. Check speech
|
||||
|
||||
@@ -57,21 +57,6 @@ int main(int argc, char **argv) {
|
||||
if (!atomReply) return 2;
|
||||
const auto activeAtom = atomReply->atom;
|
||||
std::free(atomReply);
|
||||
constexpr char stateName[] = "WM_STATE";
|
||||
auto *stateReply = xcb_intern_atom_reply(connection,
|
||||
xcb_intern_atom(connection, 0, sizeof(stateName) - 1, stateName), nullptr);
|
||||
if (!stateReply) return 2;
|
||||
const auto stateAtom = stateReply->atom;
|
||||
std::free(stateReply);
|
||||
auto acknowledge = [&](HWND window) {
|
||||
const auto xid = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(
|
||||
GetPropW(window, L"__wine_x11_whole_window")));
|
||||
const uint32_t state[] = {1, XCB_WINDOW_NONE}; // ICCCM NormalState.
|
||||
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, xid,
|
||||
stateAtom, stateAtom, 32, 2, state);
|
||||
xcb_flush(connection);
|
||||
pump();
|
||||
};
|
||||
const auto linuxWindow = xcb_generate_id(connection);
|
||||
xcb_create_window(connection, XCB_COPY_FROM_PARENT, linuxWindow, screen->root,
|
||||
0, 0, 20, 20, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, 0, nullptr);
|
||||
@@ -97,7 +82,7 @@ int main(int argc, char **argv) {
|
||||
ShowWindow(launcher, SW_HIDE);
|
||||
pump();
|
||||
ShowWindow(launcher, SW_SHOWNOACTIVATE);
|
||||
acknowledge(launcher);
|
||||
pump();
|
||||
focus(linuxWindow, linuxWindow);
|
||||
ok &= check(!GetForegroundWindow(), "Linux focus clears Wine foreground");
|
||||
ok &= check(!restore_virtual_desktop_focus() && !GetForegroundWindow(), "Linux focus is never taken");
|
||||
@@ -111,7 +96,7 @@ int main(int argc, char **argv) {
|
||||
ok &= check(!restore_virtual_desktop_focus(), "disabled application is not activated");
|
||||
EnableWindow(launcher, TRUE);
|
||||
ShowWindow(other, SW_SHOWNOACTIVATE);
|
||||
acknowledge(other);
|
||||
pump();
|
||||
focus(linuxWindow, linuxWindow);
|
||||
focus(desktop, desktop);
|
||||
ok &= check(!restore_virtual_desktop_focus() && !GetForegroundWindow(), "ambiguous applications are not chosen");
|
||||
@@ -126,7 +111,7 @@ int main(int argc, char **argv) {
|
||||
ShowWindow(launcher, SW_HIDE);
|
||||
pump();
|
||||
ShowWindow(launcher, SW_SHOWNOACTIVATE);
|
||||
acknowledge(launcher);
|
||||
pump();
|
||||
focus(linuxWindow, linuxWindow);
|
||||
focus(desktop, desktop);
|
||||
ok &= check(restore_virtual_desktop_focus(), "later workspace return recovers again");
|
||||
@@ -134,14 +119,14 @@ int main(int argc, char **argv) {
|
||||
ShowWindow(launcher, SW_HIDE);
|
||||
pump();
|
||||
ShowWindow(launcher, SW_SHOWNOACTIVATE);
|
||||
acknowledge(launcher);
|
||||
pump();
|
||||
focus(linuxWindow, linuxWindow);
|
||||
const auto launcherXid = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(
|
||||
GetPropW(launcher, L"__wine_x11_whole_window")));
|
||||
focus(launcherXid, linuxWindow);
|
||||
ok &= check(!restore_virtual_desktop_focus(), "focused child requires window manager desktop ownership");
|
||||
ShowWindow(other, SW_SHOWNOACTIVATE);
|
||||
acknowledge(other);
|
||||
pump();
|
||||
focus(launcherXid, desktop);
|
||||
ok &= check(!restore_virtual_desktop_focus(), "focused child does not bypass ambiguous applications");
|
||||
ShowWindow(other, SW_HIDE);
|
||||
|
||||
Reference in new issue
Block a user