mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Free the drag-and-drop proxy after closing editors
Apparently X11 connections are a scarce resource, so it seems like a good idea to not hang on to them for too long. Now this is sort of a hybrid between COM-style memory management and a singleton.
This commit is contained in:
@@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
#include "xdnd-proxy.h"
|
|
||||||
|
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
// A catchable alternative to `assert()`. Normally all of our `assert(!error)`
|
// A catchable alternative to `assert()`. Normally all of our `assert(!error)`
|
||||||
@@ -204,6 +202,7 @@ Editor::Editor(MainContext& main_context,
|
|||||||
std::optional<fu2::unique_function<void()>> timer_proc)
|
std::optional<fu2::unique_function<void()>> timer_proc)
|
||||||
: use_xembed(config.editor_xembed),
|
: use_xembed(config.editor_xembed),
|
||||||
x11_connection(xcb_connect(nullptr, nullptr), xcb_disconnect),
|
x11_connection(xcb_connect(nullptr, nullptr), xcb_disconnect),
|
||||||
|
dnd_proxy_handle(WineXdndProxy::init_proxy()),
|
||||||
client_area(get_maximum_screen_dimensions(*x11_connection)),
|
client_area(get_maximum_screen_dimensions(*x11_connection)),
|
||||||
// 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
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "../common/configuration.h"
|
#include "../common/configuration.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "xdnd-proxy.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of Win32 messages to handle per message loop. This is
|
* The maximum number of Win32 messages to handle per message loop. This is
|
||||||
@@ -223,8 +224,17 @@ class Editor {
|
|||||||
*/
|
*/
|
||||||
void do_xembed() const;
|
void do_xembed() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every editor window gets its own X11 connection.
|
||||||
|
*/
|
||||||
std::shared_ptr<xcb_connection_t> x11_connection;
|
std::shared_ptr<xcb_connection_t> x11_connection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handle for our Wine->X11 drag-and-drop proxy. We only have one of these
|
||||||
|
* per process, and it gets freed again when the last handle gets dropped.
|
||||||
|
*/
|
||||||
|
WineXdndProxy::Handle dnd_proxy_handle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Wine window's client area, or the maximum size of that window. This
|
* The Wine window's client area, or the maximum size of that window. This
|
||||||
* will be set to a size that's large enough to be able to enter full screen
|
* will be set to a size that's large enough to be able to enter full screen
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include "xdnd-proxy.h"
|
#include "xdnd-proxy.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
// FIXME: Remove
|
// FIXME: Remove
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@@ -145,12 +147,34 @@ WineXdndProxy::WineXdndProxy()
|
|||||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS),
|
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS),
|
||||||
UnhookWinEvent) {}
|
UnhookWinEvent) {}
|
||||||
|
|
||||||
WineXdndProxy& WineXdndProxy::init_proxy() {
|
/**
|
||||||
static std::unique_ptr<WineXdndProxy> instance;
|
* The number of handles to our Wine->X11 drag-and-drop proxy object. To prevent
|
||||||
if (!instance) {
|
* running out of X11 connections when opening and closing a lot of plugin
|
||||||
// Protected constructors, hooray!
|
* editors in a project, we'll free this again after the last editor in this
|
||||||
instance.reset(new WineXdndProxy{});
|
* process gets closed.
|
||||||
|
*/
|
||||||
|
static std::atomic_size_t instance_reference_count = 0;
|
||||||
|
|
||||||
|
WineXdndProxy::Handle::Handle(WineXdndProxy& proxy) : proxy(proxy) {}
|
||||||
|
|
||||||
|
WineXdndProxy::Handle::~Handle() noexcept {
|
||||||
|
if (instance_reference_count.fetch_sub(1) == 1) {
|
||||||
|
delete &proxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WineXdndProxy::Handle WineXdndProxy::init_proxy() {
|
||||||
|
// We're doing a bit of a hybrid between a COM-style reference counted smart
|
||||||
|
// pointer and a singleton here because we need to ensure that there's only
|
||||||
|
// one proxy per process, but we want to free up the X11 connection when
|
||||||
|
// it's not needed anymore. Because of that this pointer may point to
|
||||||
|
// deallocated memory, so the reference count should be leading here. Oh and
|
||||||
|
// explained elsewhere, we won't even bother making this thread safe because
|
||||||
|
// it can only be called from the GUI thread anyways.
|
||||||
|
static WineXdndProxy* instance = nullptr;
|
||||||
|
if (instance_reference_count.fetch_add(1) == 0) {
|
||||||
|
instance = new WineXdndProxy{};
|
||||||
}
|
}
|
||||||
|
|
||||||
return *instance;
|
return Handle(*instance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,35 @@ class WineXdndProxy {
|
|||||||
WineXdndProxy();
|
WineXdndProxy();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* A sort of smart pointer for `WineXdndProxy`, similar to how the COM/VST3
|
||||||
|
* pointers work. We want to unregister the hooks and drop the X11
|
||||||
|
* connection when the last editor closes in a plugin group. This is not
|
||||||
|
* strictly necessary, but there's an open X11 client limit and otherwise
|
||||||
|
* opening and closing a bunch of editors would get you very close to that
|
||||||
|
* limit.
|
||||||
|
*/
|
||||||
|
class Handle {
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Before calling this, the reference count should be increased by one
|
||||||
|
* in `WineXdndProxy::init_proxy()`.
|
||||||
|
*/
|
||||||
|
Handle(WineXdndProxy& proxy);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Reduces the reference count by one, and frees `proxy` if this was the
|
||||||
|
* last handle.
|
||||||
|
*/
|
||||||
|
~Handle() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
WineXdndProxy& proxy;
|
||||||
|
|
||||||
|
friend WineXdndProxy;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the Wine->X11 drag-and-drop proxy. Calling this will hook into
|
* Initialize the Wine->X11 drag-and-drop proxy. Calling this will hook into
|
||||||
* Wine's OLE drag and drop system by listening for the creation of special
|
* Wine's OLE drag and drop system by listening for the creation of special
|
||||||
@@ -47,8 +76,15 @@ class WineXdndProxy {
|
|||||||
* once from every plugin host instance. Because the actual data is stored
|
* once from every plugin host instance. Because the actual data is stored
|
||||||
* in a COM object, we can only handle drag-and-drop coming form this
|
* in a COM object, we can only handle drag-and-drop coming form this
|
||||||
* process.
|
* process.
|
||||||
|
*
|
||||||
|
* This is sort of a singleton but not quite, as the `WineXdndProxy` is only
|
||||||
|
* alive for as long as there are open editors in this process. This is done
|
||||||
|
* to avoid opening too many X11 connections.
|
||||||
|
*
|
||||||
|
* @note This function, like everything other GUI realted, should be called
|
||||||
|
* from the main thread that's running the Win32 message loop.
|
||||||
*/
|
*/
|
||||||
static WineXdndProxy& init_proxy();
|
static WineXdndProxy::Handle init_proxy();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> x11_connection;
|
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> x11_connection;
|
||||||
|
|||||||
Reference in New Issue
Block a user