mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 12:10:09 +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
|
||||
|
||||
- Changed the filter in the Wine->X11 drag-and-drop implementation for
|
||||
distinguishing between Wine windows and other windows (so that we don't
|
||||
interfere with Wine->Wine drag-and-drop) to be 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.
|
||||
- Changed how input focus releasing works by more specifically ignoring events
|
||||
where the mouse pointer is still hovering over a Wine window instead of
|
||||
ignoring a wider class of events. This should fix some edge cases where input
|
||||
focus would not be given back to the host, or where dropdown menus could close
|
||||
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
|
||||
|
||||
|
||||
@@ -597,13 +597,52 @@ void Editor::handle_x11_events() noexcept {
|
||||
const auto event =
|
||||
reinterpret_cast<xcb_leave_notify_event_t*>(
|
||||
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([&]() {
|
||||
return "DEBUG: LeaveNotify for window " +
|
||||
std::to_string(event->child) + " (wine window " +
|
||||
(is_wine_window_active() ? "active"
|
||||
: "inactive") +
|
||||
", detail " + std::to_string(event->detail) +
|
||||
")";
|
||||
std::ostringstream message;
|
||||
message << "DEBUG: LeaveNotify for window "
|
||||
<< event->child;
|
||||
message << " (wine window "
|
||||
<< (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
|
||||
@@ -617,9 +656,9 @@ void Editor::handle_x11_events() noexcept {
|
||||
// these fake dropdowns would immediately close when
|
||||
// hovering over them.
|
||||
if (event->child == wrapper_window.window &&
|
||||
event->detail != XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL &&
|
||||
supports_ewmh_active_window() &&
|
||||
is_wine_window_active()) {
|
||||
is_wine_window_active() &&
|
||||
!is_cursor_in_wine_window(windows_pointer_pos)) {
|
||||
set_input_focus(false);
|
||||
}
|
||||
} 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 {
|
||||
if (!supports_ewmh_active_window()) {
|
||||
return false;
|
||||
|
||||
@@ -262,6 +262,18 @@ class Editor {
|
||||
const bool use_xembed;
|
||||
|
||||
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
|
||||
* `_NET_ACTIVE_WINDOW`) contains `wine_window`. If the window manager does
|
||||
|
||||
Reference in New Issue
Block a user