mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-06 19:40:10 +02:00
Fix window wrapping by implementing a minimal ICCCM window manager.
This commit is contained in:
committed by
Robbert van der Helm
parent
918d24a16e
commit
2c6c21409c
@@ -77,7 +77,8 @@ constexpr uint32_t parent_event_mask =
|
|||||||
* slightly when the mouse is already inside of the editor window when
|
* slightly when the mouse is already inside of the editor window when
|
||||||
* opening it.
|
* opening it.
|
||||||
*/
|
*/
|
||||||
constexpr uint32_t wrapper_event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
constexpr uint32_t wrapper_event_mask = XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT |
|
||||||
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||||
XCB_EVENT_MASK_KEY_PRESS |
|
XCB_EVENT_MASK_KEY_PRESS |
|
||||||
XCB_EVENT_MASK_KEY_RELEASE;
|
XCB_EVENT_MASK_KEY_RELEASE;
|
||||||
|
|
||||||
@@ -268,6 +269,8 @@ Editor::Editor(MainContext& main_context,
|
|||||||
dnd_proxy_handle_(WineXdndProxy::get_handle()),
|
dnd_proxy_handle_(WineXdndProxy::get_handle()),
|
||||||
client_area_(get_maximum_screen_dimensions(*x11_connection_)),
|
client_area_(get_maximum_screen_dimensions(*x11_connection_)),
|
||||||
wrapper_window_size_({128, 128}),
|
wrapper_window_size_({128, 128}),
|
||||||
|
host_window_config_({}),
|
||||||
|
parent_window_config_({}),
|
||||||
// Create a window without any decoratiosn for easy embedding. The
|
// Create a window without any decoratiosn for easy embedding. The
|
||||||
// combination of `WS_EX_TOOLWINDOW` and `WS_POPUP` causes the window to
|
// combination of `WS_EX_TOOLWINDOW` and `WS_POPUP` causes the window to
|
||||||
// be drawn without any decorations (making resizes behave as you'd
|
// be drawn without any decorations (making resizes behave as you'd
|
||||||
@@ -427,6 +430,8 @@ void Editor::resize(uint16_t width, uint16_t height) {
|
|||||||
const std::array<uint32_t, 2> values{width, height};
|
const std::array<uint32_t, 2> values{width, height};
|
||||||
xcb_configure_window(x11_connection_.get(), wrapper_window_.window_,
|
xcb_configure_window(x11_connection_.get(), wrapper_window_.window_,
|
||||||
value_mask, values.data());
|
value_mask, values.data());
|
||||||
|
xcb_configure_window(x11_connection_.get(), wine_window_,
|
||||||
|
value_mask, values.data());
|
||||||
xcb_flush(x11_connection_.get());
|
xcb_flush(x11_connection_.get());
|
||||||
|
|
||||||
// NOTE: This lets us skip resize requests in CLAP plugins when the plugin
|
// NOTE: This lets us skip resize requests in CLAP plugins when the plugin
|
||||||
@@ -558,6 +563,47 @@ void Editor::handle_x11_events() noexcept {
|
|||||||
std::to_string(event->window);
|
std::to_string(event->window);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If the host window is different from the parent window then
|
||||||
|
// the Wine window is at a non-zero offset from the top-left
|
||||||
|
// corner. The host window will always receive absolute position
|
||||||
|
// information in its events, sent from the window manager, while
|
||||||
|
// the parent window might receive position changes relative to
|
||||||
|
// the host window when it is a child window.
|
||||||
|
if (event->window == host_window_ &&
|
||||||
|
is_synthetic_event) {
|
||||||
|
host_window_config_ = *event;
|
||||||
|
}
|
||||||
|
if (event->window == parent_window_ &&
|
||||||
|
host_window_ != parent_window_ &&
|
||||||
|
!is_synthetic_event) {
|
||||||
|
parent_window_config_ = *event;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window managers are expected to send ConfigureNotify to
|
||||||
|
// their managed windows whenever the window is being moved
|
||||||
|
// or resized by the user, so that application don't have to
|
||||||
|
// do all the relative positioning computation themselves.
|
||||||
|
// Wine also expects this and ignores position changes on its
|
||||||
|
// window parents, and its window position would get out of
|
||||||
|
// sync without this event.
|
||||||
|
if (event->window == host_window_ ||
|
||||||
|
event->window == parent_window_) {
|
||||||
|
xcb_configure_notify_event_t translated_event{};
|
||||||
|
translated_event.response_type = XCB_CONFIGURE_NOTIFY;
|
||||||
|
translated_event.event = wine_window_;
|
||||||
|
translated_event.window = wine_window_;
|
||||||
|
translated_event.width = event->width;
|
||||||
|
translated_event.height = event->height;
|
||||||
|
translated_event.x = host_window_config_.x + parent_window_config_.x;
|
||||||
|
translated_event.y = host_window_config_.y + parent_window_config_.y;
|
||||||
|
|
||||||
|
xcb_send_event(
|
||||||
|
x11_connection_.get(), false, wine_window_,
|
||||||
|
XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
|
||||||
|
reinterpret_cast<char*>(&translated_event));
|
||||||
|
xcb_flush(x11_connection_.get());
|
||||||
|
}
|
||||||
|
|
||||||
if (event->window == host_window_ ||
|
if (event->window == host_window_ ||
|
||||||
event->window == parent_window_ ||
|
event->window == parent_window_ ||
|
||||||
event->window == wrapper_window_.window_) {
|
event->window == wrapper_window_.window_) {
|
||||||
@@ -580,6 +626,53 @@ void Editor::handle_x11_events() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
// We're listening for `ConfigureRequest` events on the
|
||||||
|
// wrapper window. This is received whenever Wine wants
|
||||||
|
// to configure its window, and we need to adjust the
|
||||||
|
// configuration so that it stays within our wrapper.
|
||||||
|
// Here, wwe could translate window position changes by
|
||||||
|
// moving the wrapper window itself but this isn't really
|
||||||
|
// necessary. Instead, we prevent Wine from actually moving
|
||||||
|
// its window.
|
||||||
|
case XCB_CONFIGURE_REQUEST: {
|
||||||
|
const auto event =
|
||||||
|
reinterpret_cast<xcb_configure_request_event_t*>(
|
||||||
|
generic_event.get());
|
||||||
|
logger_.log_editor_trace([&]() {
|
||||||
|
return "DEBUG: ConfigureRequest for window " +
|
||||||
|
std::to_string(event->window);
|
||||||
|
});
|
||||||
|
const uint16_t value_mask = XCB_CONFIG_WINDOW_X |
|
||||||
|
XCB_CONFIG_WINDOW_Y |
|
||||||
|
XCB_CONFIG_WINDOW_WIDTH |
|
||||||
|
XCB_CONFIG_WINDOW_HEIGHT;
|
||||||
|
const std::array<uint32_t, 4> values{0, 0, event->width, event->height};
|
||||||
|
xcb_configure_window(x11_connection_.get(), wine_window_,
|
||||||
|
value_mask, values.data());
|
||||||
|
xcb_flush(x11_connection_.get());
|
||||||
|
} break;
|
||||||
|
// We're listening for `MapRequest` events on the wrapper
|
||||||
|
// window. This is received whenever Wine wants to map its
|
||||||
|
// window, and we need to forward the request to the X server.
|
||||||
|
// Wine also expects the window manager to change the WM_STATE
|
||||||
|
// property whenever it has finished mapping the window. We
|
||||||
|
// effectively implement a sub window manager here, so update
|
||||||
|
// the property as we should.
|
||||||
|
case XCB_MAP_REQUEST: {
|
||||||
|
const auto event =
|
||||||
|
reinterpret_cast<xcb_map_request_event_t*>(
|
||||||
|
generic_event.get());
|
||||||
|
logger_.log_editor_trace([&]() {
|
||||||
|
return "DEBUG: MapRequest for window " +
|
||||||
|
std::to_string(event->window);
|
||||||
|
});
|
||||||
|
xcb_map_window(x11_connection_.get(), wine_window_);
|
||||||
|
|
||||||
|
const std::array<uint32_t, 2> values{XCB_ICCCM_WM_STATE_NORMAL, 0};
|
||||||
|
xcb_change_property(x11_connection_.get(), XCB_PROP_MODE_REPLACE, wine_window_, xcb_wm_state_property_,
|
||||||
|
xcb_wm_state_property_, 32, 2, values.data());
|
||||||
|
xcb_flush(x11_connection_.get());
|
||||||
|
} break;
|
||||||
// Start the XEmbed procedure when the window becomes visible,
|
// Start the XEmbed procedure when the window becomes visible,
|
||||||
// since most hosts will only show the window after the plugin
|
// since most hosts will only show the window after the plugin
|
||||||
// has embedded itself into it.
|
// has embedded itself into it.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#pragma push_macro("_WIN32")
|
#pragma push_macro("_WIN32")
|
||||||
#undef _WIN32
|
#undef _WIN32
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xcb_icccm.h>
|
||||||
#pragma pop_macro("_WIN32")
|
#pragma pop_macro("_WIN32")
|
||||||
|
|
||||||
#include "../common/configuration.h"
|
#include "../common/configuration.h"
|
||||||
@@ -394,6 +395,12 @@ class Editor {
|
|||||||
*/
|
*/
|
||||||
Size wrapper_window_size_;
|
Size wrapper_window_size_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last received configurations for the host and parent windows.
|
||||||
|
*/
|
||||||
|
xcb_configure_notify_event_t host_window_config_;
|
||||||
|
xcb_configure_notify_event_t parent_window_config_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The handle for the window created through Wine that the plugin uses to
|
* The handle for the window created through Wine that the plugin uses to
|
||||||
* embed itself in.
|
* embed itself in.
|
||||||
|
|||||||
Reference in New Issue
Block a user