mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Filter LeaveNotify based on window under pointer
Instead of ignoring all `NonlinearVirtual` events. This lets us release focus when instantly moving the mouse from a plugin GUI to something else. This generates `NonlinearVirtual` event, and previously we ignored those because that also happens when opening a dropdown menu in a TDR plugin (which uses popup windows instead of actual dropdowns).
This commit is contained in:
+14
-5
@@ -15,11 +15,20 @@ Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Changed the filter in the Wine->X11 drag-and-drop implementation for
|
- Changed how input focus releasing works by more specifically ignoring events
|
||||||
distinguishing between Wine windows and other windows (so that we don't
|
where the mouse pointer is still hovering over a Wine window instead of
|
||||||
interfere with Wine->Wine drag-and-drop) to be more specific. Before this
|
ignoring a wider class of events. This should fix some edge cases where input
|
||||||
change we might use our own XDND implementation when dragging from a plugin to
|
focus would not be given back to the host, or where dropdown menus could close
|
||||||
a standalone Wine application running within the same Wine prefix.
|
immediately when moving your mouse outsdie of them. The first case would in
|
||||||
|
practice only happen when using a touchscreen or drawing tablet, since that
|
||||||
|
would require the mouse to instantly move from the plugin GUI to another
|
||||||
|
window without going over the window's borders.
|
||||||
|
- Similarly, the filter in the Wine->X11 drag-and-drop implementation for
|
||||||
|
distinguishing between Wine windows and other windows (so that we won't
|
||||||
|
interfere with Wine's own internal drag-and-drop mechanism) has also been made
|
||||||
|
more specific. Before this change we might use our own XDND implementation
|
||||||
|
when dragging from a plugin to a standalone Wine application running within
|
||||||
|
the same Wine prefix.
|
||||||
|
|
||||||
## [3.5.0] - 2021-06-23
|
## [3.5.0] - 2021-06-23
|
||||||
|
|
||||||
|
|||||||
@@ -597,13 +597,52 @@ void Editor::handle_x11_events() noexcept {
|
|||||||
const auto event =
|
const auto event =
|
||||||
reinterpret_cast<xcb_leave_notify_event_t*>(
|
reinterpret_cast<xcb_leave_notify_event_t*>(
|
||||||
generic_event.get());
|
generic_event.get());
|
||||||
|
|
||||||
|
// HACK: We need to do a `WindowFromPoint()` query inside of
|
||||||
|
// `is_cursor_in_wine_window()`, and
|
||||||
|
// `GetCursorPos()`'s value only updates once every
|
||||||
|
// 100 milliseconds:
|
||||||
|
// https://github.com/wine-mirror/wine/blob/25271032dfb3f126a8b0dff2adb9b96a7d09241d/dlls/user32/input.c#L345
|
||||||
|
//
|
||||||
|
// To avoid this, we will use the X11 cursor position.
|
||||||
|
// For this to work we will need to translate X11 root
|
||||||
|
// window coordinates into Wine virtual screen
|
||||||
|
// coordinates, like so:
|
||||||
|
// https://github.com/wine-mirror/wine/tree/25271032dfb3f126a8b0dff2adb9b96a7d09241d/dlls/winex11.drv/display.c
|
||||||
|
//
|
||||||
|
// This function is sadly not exposed, so instead we
|
||||||
|
// will get the root window cursor position, and then
|
||||||
|
// add to that the difference between `wine_window`'s
|
||||||
|
// root-relative X11 position and its Win32 position.
|
||||||
|
// The alternative is sleeping for 100 milliseconds,
|
||||||
|
// but this is faster.
|
||||||
|
const std::optional<POINT> windows_pointer_pos =
|
||||||
|
get_current_pointer_position();
|
||||||
|
|
||||||
logger.log_editor_trace([&]() {
|
logger.log_editor_trace([&]() {
|
||||||
return "DEBUG: LeaveNotify for window " +
|
std::ostringstream message;
|
||||||
std::to_string(event->child) + " (wine window " +
|
message << "DEBUG: LeaveNotify for window "
|
||||||
(is_wine_window_active() ? "active"
|
<< event->child;
|
||||||
: "inactive") +
|
message << " (wine window "
|
||||||
", detail " + std::to_string(event->detail) +
|
<< (is_wine_window_active() ? "active"
|
||||||
")";
|
: "inactive");
|
||||||
|
message << ", detail: "
|
||||||
|
<< static_cast<int>(event->detail);
|
||||||
|
message << ", pointer pos: ";
|
||||||
|
if (windows_pointer_pos) {
|
||||||
|
message << windows_pointer_pos->x << ", "
|
||||||
|
<< windows_pointer_pos->y;
|
||||||
|
} else {
|
||||||
|
message << "<unknown>";
|
||||||
|
}
|
||||||
|
message
|
||||||
|
<< ", pointer "
|
||||||
|
<< (is_cursor_in_wine_window(windows_pointer_pos)
|
||||||
|
? "is"
|
||||||
|
: "is not")
|
||||||
|
<< " in Wine window)";
|
||||||
|
|
||||||
|
return message.str();
|
||||||
});
|
});
|
||||||
|
|
||||||
// This extra check for the `NonlinearVirtual` detail is
|
// This extra check for the `NonlinearVirtual` detail is
|
||||||
@@ -617,9 +656,9 @@ void Editor::handle_x11_events() noexcept {
|
|||||||
// these fake dropdowns would immediately close when
|
// these fake dropdowns would immediately close when
|
||||||
// hovering over them.
|
// hovering over them.
|
||||||
if (event->child == wrapper_window.window &&
|
if (event->child == wrapper_window.window &&
|
||||||
event->detail != XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL &&
|
|
||||||
supports_ewmh_active_window() &&
|
supports_ewmh_active_window() &&
|
||||||
is_wine_window_active()) {
|
is_wine_window_active() &&
|
||||||
|
!is_cursor_in_wine_window(windows_pointer_pos)) {
|
||||||
set_input_focus(false);
|
set_input_focus(false);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
@@ -815,6 +854,40 @@ void Editor::maybe_run_timer_proc() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<POINT> Editor::get_current_pointer_position() const {
|
||||||
|
xcb_generic_error_t* error = nullptr;
|
||||||
|
const xcb_query_pointer_cookie_t query_pointer_cookie =
|
||||||
|
xcb_query_pointer(x11_connection.get(), wine_window);
|
||||||
|
const std::unique_ptr<xcb_query_pointer_reply_t> query_pointer_reply(
|
||||||
|
xcb_query_pointer_reply(x11_connection.get(), query_pointer_cookie,
|
||||||
|
&error));
|
||||||
|
if (error) {
|
||||||
|
free(error);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We know the mouse coordinates relative to the root window, and we know
|
||||||
|
// the mouse coordinates relative to `wine_window`, so we can skip a request
|
||||||
|
// by calculating Wine window's coordinates ourself.
|
||||||
|
const uint16_t x11_x_pos =
|
||||||
|
query_pointer_reply->root_x - query_pointer_reply->win_x;
|
||||||
|
const uint16_t x11_y_pos =
|
||||||
|
query_pointer_reply->root_y - query_pointer_reply->win_y;
|
||||||
|
|
||||||
|
// We need to offset the root-relative pointer position with the difference
|
||||||
|
// between `wine_window`'s X11 and Win32 coordinates. Wine sadly does not
|
||||||
|
// expose a function that just lets us translate X11 coordinates into
|
||||||
|
// Windows coordinates.
|
||||||
|
RECT win32_pos{};
|
||||||
|
if (!GetWindowRect(get_win32_handle(), &win32_pos)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return POINT{
|
||||||
|
.x = query_pointer_reply->root_x + (win32_pos.left - x11_x_pos),
|
||||||
|
.y = query_pointer_reply->root_y + (win32_pos.top - x11_y_pos)};
|
||||||
|
}
|
||||||
|
|
||||||
bool Editor::is_wine_window_active() const {
|
bool Editor::is_wine_window_active() const {
|
||||||
if (!supports_ewmh_active_window()) {
|
if (!supports_ewmh_active_window()) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -262,6 +262,18 @@ class Editor {
|
|||||||
const bool use_xembed;
|
const bool use_xembed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Get the current cursor position, in Win32 screen coordinates. This is
|
||||||
|
* needed for our `LeaveNotify` handling because `GetCursorPos()` only
|
||||||
|
* updates once every 100 ms. This takes the X11 mouse cursor position, and
|
||||||
|
* then adds to that the difference between `wine_window`'s X11 coordinates
|
||||||
|
* and its Win32 coordinates. This is kind of a workaround for Wine's
|
||||||
|
* X11drv's `root_to_virtual_screen()` function not being exposed.
|
||||||
|
*
|
||||||
|
* If we cannot obtain the X11 cursor position, then this returns a nullopt.
|
||||||
|
*/
|
||||||
|
std::optional<POINT> get_current_pointer_position() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `true` if the currently active window (as per
|
* Returns `true` if the currently active window (as per
|
||||||
* `_NET_ACTIVE_WINDOW`) contains `wine_window`. If the window manager does
|
* `_NET_ACTIVE_WINDOW`) contains `wine_window`. If the window manager does
|
||||||
|
|||||||
Reference in New Issue
Block a user