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:
Robbert van der Helm
2021-07-27 18:05:47 +02:00
parent e42448f758
commit d053eab35a
3 changed files with 107 additions and 13 deletions
+14 -5
View File
@@ -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
+81 -8
View File
@@ -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;
+12
View File
@@ -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