mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Rename topmost_window to host_window
Since apparently to keep REAPER happy we shouldn't take the _very_ topmost window.
This commit is contained in:
+46
-47
@@ -49,10 +49,10 @@ using namespace std::literals::chrono_literals;
|
||||
constexpr size_t idle_timer_id = 1337;
|
||||
|
||||
/**
|
||||
* The X11 event mask for the topmost window, which in most DAWs except for
|
||||
* Ardour and REAPER will be the same as `parent_window`.
|
||||
* The X11 event mask for the host window, which in most DAWs except for Ardour
|
||||
* and REAPER will be the same as `parent_window`.
|
||||
*/
|
||||
constexpr uint32_t topmost_event_mask =
|
||||
constexpr uint32_t host_event_mask =
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_VISIBILITY_CHANGE;
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ constexpr uint32_t topmost_event_mask =
|
||||
* as well to detect reparents.
|
||||
*/
|
||||
constexpr uint32_t parent_event_mask =
|
||||
topmost_event_mask | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
host_event_mask | XCB_EVENT_MASK_FOCUS_CHANGE |
|
||||
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW;
|
||||
|
||||
/**
|
||||
@@ -93,14 +93,14 @@ static const HCURSOR arrow_cursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
* Find the the ancestors for the given window. This returns a list of window
|
||||
* IDs that starts wit h`starting_at`, and then iteratively contains the parent
|
||||
* of the previous window in the list until we reach the root window. The
|
||||
* topmost window (i.e. the window that will show up in the user's window
|
||||
* manager) will be the last window in this list.
|
||||
* topmost window (i.e. the window closest to the root in the window stack) will
|
||||
* be the last window in this list.
|
||||
*
|
||||
* @param x11_connection The X11 connection to use.
|
||||
* @param starting_at The window we want to know the ancestor windows of.
|
||||
*
|
||||
* @return A non-empty list containing `starting_at` and all of its ancestor
|
||||
* windows `starting_at`.
|
||||
* windows `starting_at`.
|
||||
*/
|
||||
boost::container::small_vector<xcb_window_t, 8> find_ancestor_windows(
|
||||
xcb_connection_t& x11_connection,
|
||||
@@ -244,7 +244,7 @@ Editor::Editor(MainContext& main_context,
|
||||
idle_timer_proc(std::move(timer_proc)),
|
||||
parent_window(parent_window_handle),
|
||||
wine_window(get_x11_handle(win32_window.handle)),
|
||||
topmost_window(
|
||||
host_window(
|
||||
find_ancestor_windows(*x11_connection, parent_window).back()) {
|
||||
// Used for input focus grabbing to only grab focus when the window is
|
||||
// active. In case the atom does not exist or the WM does not support this
|
||||
@@ -293,8 +293,8 @@ Editor::Editor(MainContext& main_context,
|
||||
// release input focus as necessary.
|
||||
// If we do enable XEmbed support, we'll also listen for visibility changes
|
||||
// and trigger the embedding when the window becomes visible
|
||||
xcb_change_window_attributes(x11_connection.get(), topmost_window,
|
||||
XCB_CW_EVENT_MASK, &topmost_event_mask);
|
||||
xcb_change_window_attributes(x11_connection.get(), host_window,
|
||||
XCB_CW_EVENT_MASK, &host_event_mask);
|
||||
xcb_change_window_attributes(x11_connection.get(), parent_window,
|
||||
XCB_CW_EVENT_MASK, &parent_event_mask);
|
||||
xcb_flush(x11_connection.get());
|
||||
@@ -359,22 +359,22 @@ void Editor::handle_x11_events() noexcept {
|
||||
// NOTE: When reopening a closed editor window in REAPER, REAPER
|
||||
// will initialize the editor first, and only then will it
|
||||
// reparent `parent_window` to a new FX window. This means
|
||||
// that `topmost_window` will be the same as
|
||||
// `parent_window` in REAPER if you reopen a plugin GUI,
|
||||
// which breaks our input focus handling. To work around
|
||||
// this, we will just check if the topmost window has
|
||||
// changed whenever the parent window gets reparented.
|
||||
// that `host_window` will be the same as `parent_window`
|
||||
// in REAPER if you reopen a plugin GUI, which breaks our
|
||||
// input focus handling. To work around this, we will just
|
||||
// check if the host's window has changed whenever the
|
||||
// parent window gets reparented.
|
||||
case XCB_REPARENT_NOTIFY: {
|
||||
redetect_topmost_window();
|
||||
redetect_host_window();
|
||||
} break;
|
||||
// We're listening for `ConfigureNotify` events on the topmost
|
||||
// window before the root window, i.e. the window that's
|
||||
// actually going to get dragged around the by the user. In most
|
||||
// cases this is the same as `parent_window`. When either this
|
||||
// window gets moved, or when the user moves his mouse over our
|
||||
// window, the local coordinates should be updated. The
|
||||
// additional `EnterWindow` check is sometimes necessary for
|
||||
// using multiple editor windows within a single plugin group.
|
||||
// We're listening for `ConfigureNotify` events on the host's
|
||||
// window (i.e. the window that's actually going to get dragged
|
||||
// around the by the user). In most cases this is the same as
|
||||
// `parent_window`. When either this window gets moved, or when
|
||||
// the user moves his mouse over our window, the local
|
||||
// coordinates should be updated. The additional `EnterWindow`
|
||||
// check is sometimes necessary for using multiple editor
|
||||
// windows within a single plugin group.
|
||||
case XCB_CONFIGURE_NOTIFY: {
|
||||
if (!use_xembed) {
|
||||
fix_local_coordinates();
|
||||
@@ -511,7 +511,7 @@ void Editor::fix_local_coordinates() const {
|
||||
}
|
||||
|
||||
void Editor::set_input_focus(bool grab) const {
|
||||
const xcb_window_t focus_target = grab ? parent_window : topmost_window;
|
||||
const xcb_window_t focus_target = grab ? parent_window : host_window;
|
||||
|
||||
xcb_generic_error_t* error = nullptr;
|
||||
const xcb_get_input_focus_cookie_t focus_cookie =
|
||||
@@ -522,13 +522,13 @@ void Editor::set_input_focus(bool grab) const {
|
||||
|
||||
// Calling `set_input_focus(true)` can trigger another `FocusIn` event,
|
||||
// which will then once again call `set_input_focus(true)`. To work around
|
||||
// this we prevent unnecessary repeat keyboard focus grabs.
|
||||
// One thing that slightly complicates this is the use of unmapped input
|
||||
// proxy windows. When `topmost_window` gets foccused, some hosts will
|
||||
// reassign input focus to such a proxy window. To avoid fighting over
|
||||
// focus, when grabbing focus we don't just check whether `current_focus`
|
||||
// and `focus_target` are the same window but we'll also allow
|
||||
// `current_focus` to be a child of `focus_target`.
|
||||
// this we prevent unnecessary repeat keyboard focus grabs. One thing that
|
||||
// slightly complicates this is the use of unmapped input proxy windows.
|
||||
// When `host_window` gets foccused, some hosts will reassign input focus to
|
||||
// such a proxy window. To avoid fighting over focus, when grabbing focus we
|
||||
// don't just check whether `current_focus` and `focus_target` are the same
|
||||
// window but we'll also allow `current_focus` to be a child of
|
||||
// `focus_target`.
|
||||
const xcb_window_t current_focus = focus_reply->focus;
|
||||
if (current_focus == focus_target ||
|
||||
(grab && is_child_window_or_same(*x11_connection, current_focus,
|
||||
@@ -537,7 +537,7 @@ void Editor::set_input_focus(bool grab) const {
|
||||
}
|
||||
|
||||
// Explicitly request input focus when the user interacts with the window.
|
||||
// Without this `topmost_window` will capture all keyboard events in most
|
||||
// Without this, `host_window` will capture all keyboard events in most
|
||||
// hosts. Ideally we would just do this whenever the child window calls
|
||||
// `SetFocus()` (or no handling should be necessary), but as far as I'm
|
||||
// aware there is no way to do this. Right now we will grab input focus when
|
||||
@@ -587,32 +587,31 @@ bool Editor::is_wine_window_active() const {
|
||||
return is_child_window_or_same(*x11_connection, wine_window, active_window);
|
||||
}
|
||||
|
||||
void Editor::redetect_topmost_window() noexcept {
|
||||
const xcb_window_t new_topmost_window =
|
||||
void Editor::redetect_host_window() noexcept {
|
||||
const xcb_window_t new_host_window =
|
||||
find_ancestor_windows(*x11_connection, parent_window).back();
|
||||
if (new_topmost_window == topmost_window) {
|
||||
if (new_host_window == host_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to readjust the event masks for the new topmost window, keeping
|
||||
// the (very probable) possibility in mind that the old topmost window is
|
||||
// the same as the parent window or that the parent window now is the
|
||||
// topmost window.
|
||||
if (topmost_window != parent_window) {
|
||||
// We need to readjust the event masks for the new host window, keeping the
|
||||
// (very probable) possibility in mind that the old host window is the same
|
||||
// as the parent window or that the parent window now is the host window.
|
||||
if (host_window != parent_window) {
|
||||
constexpr uint32_t no_event_mask = XCB_EVENT_MASK_NO_EVENT;
|
||||
xcb_change_window_attributes(x11_connection.get(), topmost_window,
|
||||
xcb_change_window_attributes(x11_connection.get(), host_window,
|
||||
XCB_CW_EVENT_MASK, &no_event_mask);
|
||||
}
|
||||
|
||||
if (new_topmost_window == parent_window) {
|
||||
xcb_change_window_attributes(x11_connection.get(), new_topmost_window,
|
||||
if (new_host_window == parent_window) {
|
||||
xcb_change_window_attributes(x11_connection.get(), new_host_window,
|
||||
XCB_CW_EVENT_MASK, &parent_event_mask);
|
||||
} else {
|
||||
xcb_change_window_attributes(x11_connection.get(), new_topmost_window,
|
||||
XCB_CW_EVENT_MASK, &topmost_event_mask);
|
||||
xcb_change_window_attributes(x11_connection.get(), new_host_window,
|
||||
XCB_CW_EVENT_MASK, &host_event_mask);
|
||||
}
|
||||
|
||||
topmost_window = new_topmost_window;
|
||||
host_window = new_host_window;
|
||||
xcb_flush(x11_connection.get());
|
||||
}
|
||||
|
||||
|
||||
+20
-12
@@ -53,7 +53,8 @@ constexpr uint8_t xcb_event_type_mask = 0b0111'1111;
|
||||
/**
|
||||
* The name of the X11 property that indicates whether a window supports
|
||||
* drag-and-drop. If the `editor_force_dnd` option is enabled we'll remove this
|
||||
* property from `topmost_window` to work around a bug in REAPER.
|
||||
* property from all of `parent_window`'s ancestors to work around a bug in
|
||||
* REAPER.
|
||||
*/
|
||||
constexpr char xdnd_aware_property_name[] = "XdndAware";
|
||||
|
||||
@@ -197,7 +198,7 @@ class Editor {
|
||||
* details on when this is used.
|
||||
*
|
||||
* @param grab Whether to grab input focus (if `true`) or to give back input
|
||||
* focus to `topmost_window` (if `false`).
|
||||
* focus to `host_window` (if `false`).
|
||||
*/
|
||||
void set_input_focus(bool grab) const;
|
||||
|
||||
@@ -226,10 +227,11 @@ class Editor {
|
||||
bool is_wine_window_active() const;
|
||||
|
||||
/**
|
||||
* After `parent_window` gets reparented, we may need to redetect the
|
||||
* topmost window and adjust the events we're subscribed to accordingly.
|
||||
* After `parent_window` gets reparented, we may need to redetect which
|
||||
* toplevel-ish window the host is using and adjust the events we're
|
||||
* subscribed to accordingly.
|
||||
*/
|
||||
void redetect_topmost_window() noexcept;
|
||||
void redetect_host_window() noexcept;
|
||||
|
||||
/**
|
||||
* Send an XEmbed message to a window. This does not include a flush. See
|
||||
@@ -313,20 +315,26 @@ class Editor {
|
||||
*/
|
||||
const xcb_window_t wine_window;
|
||||
/**
|
||||
* The X11 window that's at the top of the window tree starting from
|
||||
* `parent_window`, i.e. a direct child of the root window. In most cases
|
||||
* this is going to be the same as `parent_window`, but some DAWs (such as
|
||||
* REAPER) embed `parent_window` into another window. We have to listen for
|
||||
* configuration changes on this topmost window to know when the window is
|
||||
* being dragged around.
|
||||
* The toplevel X11 window `parent_window` is contained in, or
|
||||
* `parent_window` if the host doesn't do any fancy window embedding. We'll
|
||||
* find this by looking for the topmost ancestor window of `parent_window`
|
||||
* that has `WM_STATE` set. This is similar to how `xprop` and `xwininfo`
|
||||
* select windows. In most cases this is going to be the same as
|
||||
* `parent_window`, but some DAWs (such as REAPER) embed `parent_window`
|
||||
* into another window. We have to listen for configuration changes on this
|
||||
* topmost window to know when the window is being dragged around, and when
|
||||
* returning keyboard focus to the host we'll focus this window.
|
||||
*
|
||||
* NOTE: When reopening a REAPER FX window that has previously been closed,
|
||||
* REAPER will initialize the first plugin's editor first before
|
||||
* opening the window. This means that the topmost FX window doesn't
|
||||
* actually exist yet at that point, so we need to redetect this
|
||||
* later.
|
||||
* NOTE: Taking the very topmost window is not an option, because for some
|
||||
* reason REAPER will only process keyboard input for that window when
|
||||
* the mouse is within the window.
|
||||
*/
|
||||
xcb_window_t topmost_window;
|
||||
xcb_window_t host_window;
|
||||
|
||||
/**
|
||||
* The atom corresponding to `_NET_ACTIVE_WINDOW`.
|
||||
|
||||
Reference in New Issue
Block a user