mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Continue polling while LMB is held
Instead of while the tracker window is alive. Technically the plugin may clean up their files after the tracker window is gone, but in practice I haven't seen it happen.
This commit is contained in:
@@ -194,9 +194,9 @@ WineXdndProxy::Handle WineXdndProxy::get_handle() {
|
||||
return Handle(instance);
|
||||
}
|
||||
|
||||
void WineXdndProxy::begin_xdnd(const boost::container::small_vector_base<
|
||||
boost::filesystem::path>& file_paths,
|
||||
HWND tracker_window) {
|
||||
void WineXdndProxy::begin_xdnd(
|
||||
const boost::container::small_vector_base<boost::filesystem::path>&
|
||||
file_paths) {
|
||||
if (file_paths.empty()) {
|
||||
throw std::runtime_error("Cannot drag-and-drop without any files");
|
||||
}
|
||||
@@ -232,14 +232,10 @@ void WineXdndProxy::begin_xdnd(const boost::container::small_vector_base<
|
||||
|
||||
// Normally at this point you would grab the mouse pointer and track what
|
||||
// windows it's moving over. Wine is already doing this, so as a hacky
|
||||
// workaround we will instead just periodically poll the pointer position in
|
||||
// `WineXdndProxy::handle_x11_events()`, and we'll consider the
|
||||
// disappearance of `tracker_window` to indicate that the drag-and-drop has
|
||||
// either been cancelled or it has succeeded.
|
||||
this->tracker_window = tracker_window;
|
||||
|
||||
// Because Wine is blocking the GUI thread, we need to do our XDND polling
|
||||
// from another thread. Luckily the X11 API is thread safe.
|
||||
// workaround we will just poll the mouse position every millisecond until
|
||||
// the left mouse button gets released. Because Wine is also blocking the
|
||||
// GUI thread, we need to do our XDND polling from another thread. Luckily
|
||||
// the X11 API is thread safe.
|
||||
xdnd_handler = Win32Thread([&]() { run_xdnd_loop(); });
|
||||
}
|
||||
|
||||
@@ -301,11 +297,12 @@ void WineXdndProxy::run_xdnd_loop() {
|
||||
|
||||
// We cannot just grab the pointer because Wine is already doing that, and
|
||||
// it's also blocking the GUI thread. So instead we will periodically poll
|
||||
// the mouse cursor position, and we will consider the disappearance of
|
||||
// `tracker_window` to mean that the drag-and-drop operation has ended.
|
||||
// the mouse cursor position, and we will end the drag once the left mouse
|
||||
// button gets released.
|
||||
bool left_mouse_button_held = true;
|
||||
std::optional<uint16_t> last_pointer_x;
|
||||
std::optional<uint16_t> last_pointer_y;
|
||||
while (IsWindow(tracker_window)) {
|
||||
while (left_mouse_button_held) {
|
||||
usleep(1000);
|
||||
|
||||
std::unique_ptr<xcb_generic_event_t> generic_event;
|
||||
@@ -360,9 +357,19 @@ void WineXdndProxy::run_xdnd_loop() {
|
||||
// coordinates.
|
||||
const std::unique_ptr<xcb_query_pointer_reply_t> xdnd_window_query =
|
||||
query_xdnd_aware_window_at_pointer(root_window);
|
||||
if (!xdnd_window_query ||
|
||||
(xdnd_window_query->root_x == last_pointer_x &&
|
||||
xdnd_window_query->root_y == last_pointer_y)) {
|
||||
if (!xdnd_window_query) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We will stop the dragging operation as soon as the left mouse button
|
||||
// gets released
|
||||
// NOTE: In soem cases Wine's own drag-and-drop operation ends
|
||||
// prematurely. This seems to often happen with JUCE plugins. We
|
||||
// will still continue with the dragging operation, although at
|
||||
// that point the mouse pointer isn't grabbed by anything anymore.
|
||||
left_mouse_button_held = xdnd_window_query->mask & XCB_BUTTON_MASK_1;
|
||||
if (xdnd_window_query->root_x == last_pointer_x &&
|
||||
xdnd_window_query->root_y == last_pointer_y) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -431,7 +438,7 @@ void WineXdndProxy::run_xdnd_loop() {
|
||||
|
||||
// After the loop has finished we either:
|
||||
// 1) Finish the drop, if `last_xdnd_window` is a valid XDND window
|
||||
// 2) Cancel the drop, if the escape key is held, or
|
||||
// 2) Cancel the drop, if the escape key is being held, or
|
||||
// 3) Don't do antyhing, if `last_xdnd_window` is a nullopt
|
||||
// TODO: Check if the escape key is pressed to allow cancelling the drop. In
|
||||
// that case we should call `maybe_leave_last_window()`
|
||||
@@ -440,10 +447,10 @@ void WineXdndProxy::run_xdnd_loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// After the tracker window has disappeared we will try to send the drop to
|
||||
// the last window we hovered over, if it was a valid XDND aware window. We
|
||||
// should however wait with this until the window has accepted our
|
||||
// `XdndPosition` message with an `XdndStatus`
|
||||
// After the left mouse button has been released we will try to send the
|
||||
// drop to the last window we hovered over, if it was a valid XDND aware
|
||||
// window. We should however wait with this until the window has accepted
|
||||
// our `XdndPosition` message with an `XdndStatus`
|
||||
bool drop_finished = false;
|
||||
const std::chrono::steady_clock::time_point wait_start =
|
||||
std::chrono::steady_clock::now();
|
||||
@@ -801,7 +808,7 @@ void CALLBACK dnd_winevent_callback(HWINEVENTHOOK /*hWinEventHook*/,
|
||||
}
|
||||
|
||||
try {
|
||||
instance->begin_xdnd(dragged_files, hwnd);
|
||||
instance->begin_xdnd(dragged_files);
|
||||
} catch (const std::exception& error) {
|
||||
std::cerr << "XDND initialization failed:" << std::endl;
|
||||
std::cerr << error.what() << std::endl;
|
||||
|
||||
+18
-30
@@ -66,7 +66,7 @@ class ProxyWindow {
|
||||
/**
|
||||
* A simple wrapper that registers a WinEvents hook to listen for new windows
|
||||
* being created, and handles XDND client messages to achieve the behaviour
|
||||
* described in `WineXdndProxy::init_proxy()`.
|
||||
* described in `WineXdndProxy::get_handle()`.
|
||||
*/
|
||||
class WineXdndProxy {
|
||||
protected:
|
||||
@@ -88,7 +88,7 @@ class WineXdndProxy {
|
||||
protected:
|
||||
/**
|
||||
* Before calling this, the reference count should be increased by one
|
||||
* in `WineXdndProxy::init_proxy()`.
|
||||
* in `WineXdndProxy::get_handle()`.
|
||||
*/
|
||||
Handle(WineXdndProxy* proxy);
|
||||
|
||||
@@ -114,14 +114,14 @@ class WineXdndProxy {
|
||||
/**
|
||||
* 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
|
||||
* proxy windows created by the Wine server. When a drag and drop operation
|
||||
* is started, we will initiate the XDND protocol with the same file. This
|
||||
* will allow us to drag files from Wine windows to X11 applications,
|
||||
* something that's normally not possible. Calling this function more than
|
||||
* once doesn't have any effect, but this should still be called at least
|
||||
* 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
|
||||
* process.
|
||||
* tracker windows created by the Wine server. When a drag and drop
|
||||
* operation is started, we will initiate the XDND protocol with the files
|
||||
* referenced by that tracker window. This will allow us to drag files from
|
||||
* Wine windows to X11 applications, something that's normally not possible.
|
||||
* Calling this function more than once doesn't have any effect, but this
|
||||
* should still be called at least 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 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
|
||||
@@ -136,9 +136,9 @@ class WineXdndProxy {
|
||||
* Initiate the XDDN protocol by taking ownership of the `XdndSelection`
|
||||
* selection and setting up the event listeners.
|
||||
*/
|
||||
void begin_xdnd(const boost::container::small_vector_base<
|
||||
boost::filesystem::path>& file_paths,
|
||||
HWND tracker_window);
|
||||
void begin_xdnd(
|
||||
const boost::container::small_vector_base<boost::filesystem::path>&
|
||||
file_paths);
|
||||
|
||||
/**
|
||||
* Release ownership of the selection stop listening for X11 events.
|
||||
@@ -147,11 +147,11 @@ class WineXdndProxy {
|
||||
|
||||
private:
|
||||
/**
|
||||
* From another thread, constantly poll the mouse position until
|
||||
* `tracker_window` disappears, and then perform the drop if the mouse
|
||||
* cursor was last positioned over an XDND aware window. This is a
|
||||
* workaround for us not being able to grab the mouse cursor since Wine is
|
||||
* already doing that.
|
||||
* From another thread, constantly poll the mouse position until the left
|
||||
* mouse button gets released, and then perform the drop if the mouse cursor
|
||||
* was last positioned over an XDND aware window. This is a workaround for
|
||||
* us not being able to grab the mouse cursor since Wine is already doing
|
||||
* that.
|
||||
*/
|
||||
void run_xdnd_loop();
|
||||
|
||||
@@ -240,18 +240,6 @@ class WineXdndProxy {
|
||||
*/
|
||||
std::string dragged_files_uri_list;
|
||||
|
||||
/**
|
||||
* Wine's tracker window for tracking the drag-and-drop operation. Normally
|
||||
* you would grab the mouse pointer when the drag-and-drop operation starts
|
||||
* so you can track what windows you are hovering over, but we cannot do
|
||||
* that because Wine is already doing just that. So instead we will
|
||||
* periodically poll the mouse position from another thread, and we'll
|
||||
* consider the disappearance of this window to mean that the drop has
|
||||
* either succeeded or cancelled (depending on whether or not Escape is
|
||||
* pressed).
|
||||
*/
|
||||
HWND tracker_window;
|
||||
|
||||
/**
|
||||
* We need to poll for mouse position changes from another thread, because
|
||||
* when the drag-and-drop operation starts Wine will be blocking the GUI
|
||||
|
||||
Reference in New Issue
Block a user