From 2fc6e95ec32ec262dab8932775cd2896fcff0789 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 10 Jan 2025 01:00:38 +0200 Subject: [PATCH] _wayland drag wip --- Source/Editor/GUI/Docking/DockHintWindow.cs | 11 + Source/Editor/GUI/Docking/DockWindow.cs | 4 +- Source/Engine/Core/Types/String.cpp | 5 + Source/Engine/Platform/SDL/SDLInput.cpp | 8 + .../Engine/Platform/SDL/SDLPlatform.Linux.cpp | 626 +++++++++++++++++- Source/Engine/Platform/SDL/SDLPlatform.cpp | 13 +- Source/Engine/Platform/SDL/SDLWindow.cpp | 402 ++++------- Source/Engine/Platform/SDL/SDLWindow.h | 2 - .../Tools/Flax.Build/Deps/Dependencies/SDL.cs | 6 +- 9 files changed, 757 insertions(+), 320 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockHintWindow.cs b/Source/Editor/GUI/Docking/DockHintWindow.cs index 37d4650e6..2a4cab73e 100644 --- a/Source/Editor/GUI/Docking/DockHintWindow.cs +++ b/Source/Editor/GUI/Docking/DockHintWindow.cs @@ -103,6 +103,15 @@ namespace FlaxEditor.GUI.Docking else window.StartDragging(_dragOffset); + //window.Show(); + //window.BringToFront(); + //window.Focus(); + //toMove.OnShow(); + + // Perform layout again + //windowGUI.PerformLayout(); + + // Start tracking mouse //Proxy.Window.StartTrackingMouse(false); } @@ -509,6 +518,7 @@ namespace FlaxEditor.GUI.Docking private void OnUpdate() { + //Editor.Log("OnUpdate"); if (_lateDragStartTimer > 0) { _lateDragStartTimer -= Time.UnscaledDeltaTime; @@ -524,6 +534,7 @@ namespace FlaxEditor.GUI.Docking private void OnMouseMove(Float2 mousePos) { + //Editor.Log("OnMouseMove"); // Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window if (_lateDragOffsetUpdate) { diff --git a/Source/Editor/GUI/Docking/DockWindow.cs b/Source/Editor/GUI/Docking/DockWindow.cs index f9df8218c..ae009c3e2 100644 --- a/Source/Editor/GUI/Docking/DockWindow.cs +++ b/Source/Editor/GUI/Docking/DockWindow.cs @@ -200,7 +200,7 @@ namespace FlaxEditor.GUI.Docking windowGUI.PerformLayout(); // Show - FlaxEngine.Scripting.InvokeOnUpdate(() => + /*FlaxEngine.Scripting.InvokeOnUpdate(() => { window.Show(); window.BringToFront(); @@ -209,7 +209,7 @@ namespace FlaxEditor.GUI.Docking // Perform layout again windowGUI.PerformLayout(); - }); + });*/ } /// diff --git a/Source/Engine/Core/Types/String.cpp b/Source/Engine/Core/Types/String.cpp index 1eb17f8a5..6ac9c2324 100644 --- a/Source/Engine/Core/Types/String.cpp +++ b/Source/Engine/Core/Types/String.cpp @@ -420,6 +420,11 @@ void StringAnsi::Append(const char* chars, int32 count) _length = oldLength + count; _data = (char*)Platform::Allocate((_length + 1) * sizeof(char), 16); + for (int i = 0; i < _length + 1; i++) + { + _data[i] = '0' + i % 10; + } + Platform::MemoryCopy(_data, oldData, oldLength * sizeof(char)); Platform::MemoryCopy(_data + oldLength, chars, count * sizeof(char)); _data[_length] = 0; diff --git a/Source/Engine/Platform/SDL/SDLInput.cpp b/Source/Engine/Platform/SDL/SDLInput.cpp index 59de38ffd..5a87fdee6 100644 --- a/Source/Engine/Platform/SDL/SDLInput.cpp +++ b/Source/Engine/Platform/SDL/SDLInput.cpp @@ -496,6 +496,14 @@ bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event) } return true; } + case SDL_EVENT_DROP_POSITION: + { + // We are not receiving mouse motion events during drag-and-drop + auto dpiScale = window->GetDpiScale(); + const Float2 mousePos = window->ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); + Input::Mouse->OnMouseMove(mousePos, window); + return true; + } case SDL_EVENT_WINDOW_MOUSE_LEAVE: { Input::Mouse->OnMouseLeave(window); diff --git a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp index 3173ec1c1..6958faa81 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp @@ -1,5 +1,11 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +#include + +#include "Engine/Platform/Base/DragDropHelper.h" +#include "Engine/Platform/Unix/UnixFile.h" + +#include "wayland/xdg-toplevel-drag-v1.h" #include #include #if PLATFORM_SDL && PLATFORM_LINUX @@ -16,6 +22,8 @@ #include "Engine/Engine/CommandLine.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/Linux/IncludeX11.h" +#include "Engine/Input/Input.h" +#include "Engine/Input/Mouse.h" #include #include @@ -28,13 +36,15 @@ void WaylandRegistryGlobal(void* data, wl_registry *registry, uint32 id, const c void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id); Dictionary SurfaceToWindowMap; -uint32 LastPointerSerial = 0; +uint32 ImplicitGrabSerial = 0; wl_display* WaylandDisplay = nullptr; wl_registry_listener WaylandRegistryListener = { WaylandRegistryGlobal, WaylandRegistryGlobalRemove }; xdg_toplevel_drag_manager_v1* DragManager = nullptr; wl_seat* WaylandSeat = nullptr; wl_data_device_manager* WaylandDataDeviceManager = nullptr; xdg_wm_base* WaylandXdgWmBase = nullptr; +wl_data_device* dataDevice; +bool waylandDraggingActive = false; // X11 Delegate LinuxPlatform::xEventReceived; @@ -86,6 +96,7 @@ class LinuxDropFilesData : public IGuiData { public: Array Files; + SDLWindow* Window; Type GetType() const override { @@ -105,6 +116,8 @@ class LinuxDropTextData : public IGuiData { public: StringView Text; + SDLWindow* Window; + int64* dragOver; Type GetType() const override { @@ -267,10 +280,578 @@ DragDropEffect Window::DoDragDrop(const StringView& data) return DoDragDropX11(data); } +wl_data_source* dataSource; +xdg_toplevel_drag_v1* toplevelDrag = nullptr; + +wl_data_offer* WaylandDataOffer = nullptr; // The last accepted offer +uint32 WaylandDataOfferSerial = 0; // The last accepted serial for offer +StringAnsi WaylandDataOfferMimeType; +SDLWindow* DragTargetWindow = nullptr; +Float2 DragTargetPosition; +wl_data_offer* WaylandDataSelectionOffer = nullptr; + +void WaylandDataOffer_Offer(void* data, wl_data_offer* offer, const char *mime_type) +{ + // We are being offered these types of data + //LOG(Info, "WaylandDataOffer_Offer: {}", String(mime_type)); + + //if (WaylandDataOffer == nullptr) + // return; + + //if (StringAnsi(mime_type) == "x.flaxengine.window.snap") + // WaylandDataOfferMimeType = StringAnsi(mime_type); + // wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, mime_type); +} + +void WaylandDataOffer_SourceActions(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t source_actions) +{ + // + //LOG(Info, "WaylandDataOffer_SourceActions: {}", source_actions); +} + +void WaylandDataOffer_Action(void *data, + struct wl_data_offer *wl_data_offer, + uint32_t dnd_action) +{ + // DnD: This action will be performed if dropped + //LOG(Info, "WaylandDataOffer_Action: {}", dnd_action); +} + +wl_data_offer_listener WaylandDataOfferListener = { WaylandDataOffer_Offer, WaylandDataOffer_SourceActions, WaylandDataOffer_Action}; + +void WaylandDataDevice_DataOffer(void *data, wl_data_device *wl_data_device, wl_data_offer *id) +{ + LOG(Info, "WaylandDataDevice_DataOffer: {}", (uint64)id); + /*int ret = wl_data_offer_add_listener(id, &WaylandDataOfferListener, nullptr); + if (ret != 0) + LOG(Error, "wl_data_offer_add_listener failed");*/ +} + +void WaylandDataDevice_Enter(void *data, wl_data_device *wl_data_device, uint32 serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) +{ + // DnD: The cursor entered a target surface + LOG(Info, "WaylandDataDevice_Enter serial: {}, surface: {}, pos: {}x{}, id: {}", serial, (uint64)surface, wl_fixed_to_double(x), wl_fixed_to_double(y), (uint64)id); + WaylandDataOffer = id; + WaylandDataOfferSerial = serial; + + DragTargetPosition = Float2(MAX_float, MAX_float); + SDLWindow* sourceWindow = (SDLWindow*)data; + if (!SurfaceToWindowMap.TryGet(surface, DragTargetWindow)) + DragTargetWindow = nullptr; + if (DragTargetWindow != nullptr) + DragTargetWindow = DragTargetWindow; + if (/*SurfaceToWindowMap.TryGet(surface, DragTargetWindow) && */DragTargetWindow != sourceWindow) + { + // Inform that we support the following action at this given point + wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + + if (WaylandDataOfferMimeType == "x.flaxengine.window.snap") + wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, "x.flaxengine.window.snap"); + } + else + { + wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); + } +} + +void WaylandDataDevice_Leave(void *data, wl_data_device *wl_data_device) +{ + // DnD: The cursor left the surface area + // id from enter must be destroyed here + LOG(Info, "WaylandDataDevice_Leave"); + + if (WaylandDataOffer != nullptr) + wl_data_offer_destroy(WaylandDataOffer); + WaylandDataOffer = nullptr; + WaylandDataOfferSerial = 0; + WaylandDataOfferMimeType = StringAnsi::Empty; +} + +void WaylandDataDevice_Motion(void *data, wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) +{ + // DnD: The cursor moves along the surface + Float2 dragPosition(wl_fixed_to_double(x), wl_fixed_to_double(y)); + LOG(Info, "WaylandDataDevice_Motion {},{}", (int)dragPosition.X, (int)dragPosition.Y); + + + if (DragTargetWindow != nullptr) + { + Float2 mousePos = dragPosition * DragTargetWindow->GetDpiScale(); + mousePos = Float2::Floor(mousePos); + if (DragTargetPosition != mousePos) + { + //LOG(Info, "{}: {}", time, mousePos); + Input::Mouse->OnMouseMove(mousePos, DragTargetWindow); + DragTargetPosition = mousePos; + } + } + //SDLWindow* targetWindow; + //if (SurfaceToWindowMap.TryGet(surface, targetWindow) && targetWindow == surfaceWindow) +} + +void WaylandDataDevice_Drop(void *data, wl_data_device *wl_data_device) +{ + // DnD: The drop is accepted + LOG(Info, "WaylandDataDevice_Drop"); + + /*int fds[2]; + pipe(fds); + wl_data_offer_receive(offer, "text/plain", fds[1]); + close(fds[1]); + + // TODO: do something with fds[0] + close(fds[0]);*/ + + if (WaylandDataOffer != nullptr) + { + wl_data_offer_finish(WaylandDataOffer); + wl_data_offer_destroy(WaylandDataOffer); + WaylandDataOffer = nullptr; + } +} + +void WaylandDataDevice_Selection(void *data, wl_data_device *wl_data_device, wl_data_offer *id) +{ + // Clipboard: We can read the clipboard content + /* + int fds[2]; + pipe(fds); + wl_data_offer_receive(offer, "text/plain", fds[1]); + close(fds[1]); + + wl_display_roundtrip(display); + + while (true) + { + char buf[1024]; + ssize_t n = read(fds[0], buf, sizeof(buf)); + if (n <= 0) + break; + //fwrite(buf, 1, n, stdout); + } + close(fds[0]); + wl_data_offer_destroy(offer); + */ + + + LOG(Info, "WaylandDataDevice_Selection: {}", (uint64)id); + + if (WaylandDataSelectionOffer != nullptr) + wl_data_offer_destroy(WaylandDataSelectionOffer); + WaylandDataSelectionOffer = id; +} + + + +wl_data_device_listener WaylandDataDeviceListener = +{ + WaylandDataDevice_DataOffer, + WaylandDataDevice_Enter, + WaylandDataDevice_Leave, + WaylandDataDevice_Motion, + WaylandDataDevice_Drop, + WaylandDataDevice_Selection +}; + +void WaylandDataSource_Target(void *data, + struct wl_data_source *wl_data_source, + const char *mime_type) +{ + // The destination accepts the following types, or null if nothing + //SDLWindow* window = static_cast(data); + LOG(Info, "WaylandDataSource_Target mime: {}", String(mime_type)); +} + +void WaylandDataSource_Send(void *data, + struct wl_data_source *wl_data_source, + const char *mime_type, + int32_t fd) +{ + // Clipboard: The other end has accepted the data? + IGuiData* inputData = static_cast(data); + //LOG(Info, "WaylandDataSource_Send mime: {}", String(mime_type)); + + if (inputData->GetType() == IGuiData::Type::Text) + { + UnixFile file(fd); + StringAnsi text = StringAnsi(inputData->GetAsText()); + file.Write(text.Get(), text.Length() * sizeof(StringAnsi::CharType)); + file.Close(); + + //Platform::AtomicStore(((LinuxDropTextData*)inputData)->dragOver, 1); + } + +} + +void WaylandDataSource_Cancelled(void* data, wl_data_source *source) +{ + // Clipboard: other application has replaced the content in clipboard + //SDLWindow* window = static_cast(data); + //LOG(Info, "WaylandDataSource_Cancelled"); + + IGuiData* inputData = static_cast(data); + Platform::AtomicStore(((LinuxDropTextData*)inputData)->dragOver, 1); + + wl_data_source_destroy(source); + + // The mouse up event was ignored earlier, release the button now + SDLWindow* window = ((LinuxDropTextData*)inputData)->Window; + Input::Mouse->OnMouseUp(Platform::GetMousePosition(), MouseButton::Left, window); + + /*if (DragTargetWindow != nullptr) + { + Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow); + } + else*/ /*if (window != nullptr) + { + Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window); + }*/ +} + +void WaylandDataSource_DnDDropPerformed(void *data, + struct wl_data_source *wl_data_source) +{ + // The destination is being asked to begin DnD, asking confirmation with ASK actionh + //SDLWindow* window = static_cast(data); + //LOG(Info, "WaylandDataSource_DnDDropPerformed"); +} + +void WaylandDataSource_DnDFinished(void *data, + struct wl_data_source *wl_data_source) +{ + // The destination has finally accepted the last given dnd_action + //SDLWindow* window = static_cast(data); + //LOG(Info, "WaylandDataSource_DnDFinished"); + + IGuiData* inputData = static_cast(data); + Platform::AtomicStore(((LinuxDropTextData*)inputData)->dragOver, 1); + + wl_data_source_destroy(wl_data_source); + + // The mouse up event was ignored earlier, release the button now + SDLWindow* window = ((LinuxDropTextData*)inputData)->Window; + Input::Mouse->OnMouseUp(Platform::GetMousePosition(), MouseButton::Left, window); + + /*if (DragTargetWindow != nullptr) + { + Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow); + } + else*/ /*if (window != nullptr) + { + Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window); + }*/ +} + +void WaylandDataSource_Action(void *data, + struct wl_data_source *wl_data_source, + uint32_t dnd_action) +{ + // DnD: The destination may accept the given action if confirmed + //SDLWindow* window = static_cast(data); + LOG(Info, "WaylandDataSource_Action: {}", String(dnd_action == 0 ? "NONE" : dnd_action == 1 ? "COPY" : dnd_action == 2 ? "MOVE" : dnd_action == 4 ? "ASK" : "")); + +} + +wl_data_source_listener WaylandDataSourceListener = +{ + WaylandDataSource_Target, + WaylandDataSource_Send, + WaylandDataSource_Cancelled, + WaylandDataSource_DnDDropPerformed, + WaylandDataSource_DnDFinished, + WaylandDataSource_Action +}; + +wl_event_queue* WaylandQueue = nullptr; +wl_data_device_manager* wrappedManager = nullptr; +wl_data_source* wrappedDataSource = nullptr; +wl_data_device* wrappedDataDevice = nullptr; + +class WaylandDragDropJob : public ThreadPoolTask +{ +public: + int64 StartFlag = 0; + int64 ExitFlag = 0; + StringView data; + SDLWindow* window; + int64 dragOver = 0; + int64 waitFlag = 0; + + // [ThreadPoolTask] + bool Run() override + { + Scripting::GetScriptsDomain()->Dispatch(); + + wl_display* wrappedDisplay = WaylandDisplay;//(wl_display*)wl_proxy_create_wrapper(WaylandDisplay); + //wl_proxy_set_queue((wl_proxy*)wrappedDisplay, queue); + + if (WaylandQueue == nullptr) + { + if (wrappedDataDevice != nullptr) + wl_proxy_wrapper_destroy(wrappedDataDevice); + if (wrappedDataSource != nullptr) + wl_proxy_wrapper_destroy(wrappedDataSource); + if (wrappedManager != nullptr) + wl_proxy_wrapper_destroy(wrappedManager); + if (dataDevice != nullptr) + wl_data_device_destroy(dataDevice); + + // This seems to throw bogus warnings about wl_data_source still being attached to the queue + if (WaylandQueue != nullptr) + wl_event_queue_destroy(WaylandQueue); + WaylandQueue = wl_display_create_queue(WaylandDisplay); + + wrappedManager = (wl_data_device_manager*)wl_proxy_create_wrapper(WaylandDataDeviceManager); + wl_proxy_set_queue((wl_proxy*)wrappedManager, WaylandQueue); + + // + //dataDevice = wl_data_device_manager_get_data_device(WaylandDataDeviceManager, WaylandSeat); + //wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr); + + //wl_display_roundtrip(WaylandDisplay); + /*auto */dataDevice = wl_data_device_manager_get_data_device(wrappedManager, WaylandSeat); + wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr); + wl_display_roundtrip(wrappedDisplay); + wl_data_device_set_user_data(dataDevice, window); + wrappedDataDevice = (wl_data_device*)wl_proxy_create_wrapper(dataDevice); + wl_proxy_set_queue((wl_proxy*)wrappedDataDevice, WaylandQueue); + } + + // We offer the following types of things for consumption: + dataSource = wl_data_device_manager_create_data_source(wrappedManager); + wrappedDataSource = (wl_data_source*)wl_proxy_create_wrapper(dataSource); + wl_proxy_set_queue((wl_proxy*)wrappedDataSource, WaylandQueue); + if (data == String("awindow")) + { + wl_data_source_offer(dataSource, "flaxengine/window"); + wl_data_source_offer(dataSource, "text/plain;charset=utf-8"); // TODO: needs support for custom mime-types in SDL + wl_data_source_set_actions(dataSource, wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); + } + else + { + wl_data_source_offer(dataSource, "text/plain"); + wl_data_source_offer(dataSource, "text/plain;charset=utf-8"); + wl_data_source_set_actions(dataSource, wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + } + + LinuxDropTextData textData; + textData.Text = *data; + textData.Window = window; + textData.dragOver = &dragOver; + auto _window = window->GetSDLWindow(); + //wl_data_source_set_user_data(wrappedDataSource, &textData); + + wl_data_source_add_listener(dataSource, &WaylandDataSourceListener, &textData); + + xdg_toplevel* toplevel = nullptr;//(xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr); + if (toplevel == nullptr) + { + //Platform::AtomicStore(&StartFlag, 1); + + /*while (Platform::AtomicRead(&waitFlag) == 0) + { + + }*/ + //toplevel = (xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr); + } + + xdg_toplevel* wrappedToplevel = nullptr; + + { + wl_surface* origin = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + wl_surface* icon = nullptr; + uint32 id = ImplicitGrabSerial; + //id = (uint32)SDL_GetNumberProperty(SDL_GetGlobalProperties(), "wayland.serial", 0); + wl_data_device_start_drag((wl_data_device*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), "wayland.data_device", wrappedDataDevice), dataSource, origin, icon, id); + + if (data == String("awindow")) + { + if (toplevel != nullptr) + { + wrappedToplevel = (xdg_toplevel*)wl_proxy_create_wrapper(toplevel); + wl_proxy_set_queue((wl_proxy*)wrappedToplevel, WaylandQueue); + toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); + + Float2 offset(100, 240); + Float2 scaledOffset = offset / window->GetDpiScale(); + //xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + + xdg_toplevel_drag_v1_attach(toplevelDrag, wrappedToplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + } + } + } + + /*wl_display_dispatch_queue(wrappedDisplay, queue); + wl_display_roundtrip_queue(wrappedDisplay, queue); + wl_display_flush(wrappedDisplay); + wl_display_dispatch_queue(wrappedDisplay, queue); + wl_display_dispatch(wrappedDisplay);*/ + + //wl_display_dispatch_queue_pending(wrappedDisplay, queue); + + /*int ret; + while (ret = wl_display_prepare_read_queue(wrappedDisplay, queue), ret != 0) + { + if (ret == -1) + LOG(Info, "err wl_display_prepare_read_queue: {}", errno); + + if (wl_display_dispatch_queue_pending(wrappedDisplay, queue) == -1) + LOG(Warning, "err wl_display_dispatch_queue_pending: {}", errno); + //else + // LOG(Info, "OK wl_display_dispatch_queue_pending"); + }*/ + //if (wl_display_flush(wrappedDisplay) == -1) + // LOG(Warning, "err wl_display_flush: {}", errno); + + Platform::AtomicStore(&StartFlag, 1); + + while (Platform::AtomicRead(&ExitFlag) == 0) + { + //SDLPlatform::Tick(); + //Engine::OnDraw(); + + //wl_display_dispatch_queue(displayWrapped, queue); + //wl_display_roundtrip_queue(displayWrapped, queue); + //wl_display_flush(displayWrapped); + //wl_display_dispatch_queue(displayWrapped, queue); + //wl_display_dispatch(displayWrapped); + //wl_display_dispatch_queue(wrappedDisplay, queue); + + //if (wl_display_flush(wrappedDisplay) == -1) + // LOG(Warning, "err wl_display_flush: {}", errno); + //if (wl_display_dispatch_pending(wrappedDisplay) == -1) + // LOG(Warning, "err wl_display_dispatch_pending: {}", errno); + + if (wl_display_dispatch_queue(wrappedDisplay, WaylandQueue) == -1) + LOG(Warning, "err wl_display_dispatch_queue: {}", errno); + if (wl_display_roundtrip_queue(wrappedDisplay, WaylandQueue) == -1) + LOG(Warning, "err wl_display_roundtrip_queue: {}", errno); + + if (toplevel == nullptr && data == String("awindow")) + { + if (Platform::AtomicRead(&waitFlag) != 0) + { + toplevel = (xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr); + if (toplevel != nullptr) + { + wrappedToplevel = (xdg_toplevel*)wl_proxy_create_wrapper(toplevel); + wl_proxy_set_queue((wl_proxy*)wrappedToplevel, WaylandQueue); + toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); + + Float2 offset(100, 240); + Float2 scaledOffset = offset / window->GetDpiScale(); + //xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + + xdg_toplevel_drag_v1_attach(toplevelDrag, wrappedToplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + } + } + } + + //if (wl_display_dispatch_queue(wrappedDisplay, queue) == -1) + // LOG(Warning, "err wl_display_dispatch_queue: {}", errno); + //else + // LOG(Info, "OK wl_display_dispatch_queue"); + + //if (wl_display_dispatch_queue_pending(wrappedDisplay, queue) == -1) + // LOG(Warning, "err wl_display_dispatch_queue_pending: {}", errno); + //else + // LOG(Info, "OK wl_display_dispatch_queue_pending"); + //wl_display_dispatch_pending(WaylandDisplay + + //Platform::Sleep(1); + } + + if (wl_display_roundtrip_queue(wrappedDisplay, WaylandQueue) == -1) + LOG(Warning, "err wl_display_roundtrip_queue: {}", errno); + //if (wl_display_dispatch_queue(wrappedDisplay, queue) == -1) + // LOG(Warning, "err wl_display_dispatch_queue: {}", errno); + + + if (toplevelDrag != nullptr) + { + wl_proxy_wrapper_destroy(wrappedToplevel); + xdg_toplevel_drag_v1_destroy(toplevelDrag); + toplevelDrag = nullptr; + } + + /* + wl_proxy_wrapper_destroy(wrappedDataDevice); + wl_proxy_wrapper_destroy(wrappedDataSource); + wl_proxy_wrapper_destroy(wrappedManager); + wl_data_device_destroy(dataDevice);*/ + + if (wrappedDataSource != nullptr) + wl_proxy_wrapper_destroy(wrappedDataSource); + + + //if (dataSource != nullptr) + // wl_proxy_wrapper_destroy(dataSource); + + if (WaylandDataSelectionOffer != nullptr) + { + wl_data_offer_destroy(WaylandDataSelectionOffer); + WaylandDataSelectionOffer = nullptr; + } + + // This seems to throw bogus warnings about wl_data_source still being attached to the queue + /*wl_event_queue_destroy(WaylandQueue); + */ + + + //dataDevice = nullptr; + + return false; + } +}; + DragDropEffect Window::DoDragDropWayland(const StringView& data) { - // TODO: Wayland - LOG(Warning, "Wayland Drag and drop is not implemented yet."); + // For drag-and-drop, we need to setup the event queue in separate thread to avoid racing issues + // while SDL is dispatching the main Wayland event queue when receiving the data offer from us. + + waylandDraggingActive = true; + + auto task = New(); + task->data = data; + task->window = this; + task->dragOver = 0; + Task::StartNew(task); + while (task->GetState() == TaskState::Queued) + Platform::Sleep(1); + + while (Platform::AtomicRead(&task->StartFlag) == 0) + { + Platform::Sleep(1); + } + + Show(); + //Focus(); + int counter = 100; + + while (Platform::AtomicRead(&task->dragOver) == 0) + { + SDLPlatform::Tick(); + Engine::OnUpdate(); // For docking updates + Engine::OnDraw(); + + Platform::Sleep(1); + + if (IsVisible() && Platform::AtomicRead(&task->waitFlag) == 0) + { + /*if (counter > 0) + counter--; + else*/ + Platform::AtomicStore(&task->waitFlag, 1); + } + } + + Platform::AtomicStore(&task->ExitFlag, 1); + task->Wait(); + + waylandDraggingActive = false; + return DragDropEffect::None; } @@ -857,13 +1438,10 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event) return 0; } -extern wl_data_device_listener WaylandDataDeviceListener; -wl_data_device* dataDevice; - bool SDLPlatform::InitPlatform() { - if (LinuxPlatform::Init()) - return true; + //if (LinuxPlatform::Init()) + // return true; #if false if (!CommandLine::Options.Headless && strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0) @@ -874,32 +1452,42 @@ bool SDLPlatform::InitPlatform() wl_registry_add_listener(registry, &WaylandRegistryListener, nullptr); wl_display_roundtrip(WaylandDisplay); + + dataDevice = wl_data_device_manager_get_data_device(WaylandDataDeviceManager, WaylandSeat); + wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr); + + wl_display_roundtrip(WaylandDisplay); } #else - bool waylandRequested = !CommandLine::Options.X11 || CommandLine::Options.Wayland; + bool waylandRequested = (!CommandLine::Options.X11 || CommandLine::Options.Wayland) && StringAnsi(SDL_GetHint(SDL_HINT_VIDEO_DRIVER)) == "wayland"; if (!CommandLine::Options.Headless && waylandRequested) { // Ignore in X11 session String waylandDisplayEnv; if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv)) { - WaylandDisplay = wl_display_connect(nullptr); + WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr); + if (WaylandDisplay == nullptr) + { + WaylandDisplay = wl_display_connect(nullptr); + SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, WaylandDisplay); + } if (WaylandDisplay != nullptr) { // We need to manage the wl_display and create the wl_data_device // before SDL so we can receive drag-and-drop related events from compositor. - SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, WaylandDisplay); + //SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, WaylandDisplay); wl_registry* registry = wl_display_get_registry(WaylandDisplay); wl_registry_add_listener(registry, &WaylandRegistryListener, nullptr); wl_display_roundtrip(WaylandDisplay); - dataDevice = wl_data_device_manager_get_data_device(WaylandDataDeviceManager, WaylandSeat); + /*dataDevice = wl_data_device_manager_get_data_device(WaylandDataDeviceManager, WaylandSeat); wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr); - wl_display_roundtrip(WaylandDisplay); + wl_display_roundtrip(WaylandDisplay);*/ } } } @@ -1007,7 +1595,7 @@ void WaylandPointer_Enter(void *data, LastPointerWindow = window; LastPointerPosition = Int2(surface_x, surface_y);*/ //LOG(Info, "WaylandPointerEnter serial:{}", serial); - LastPointerSerial = serial; + //ImplicitGrabSerial = serial; } void WaylandPointer_Leave(void *data, @@ -1017,7 +1605,6 @@ void WaylandPointer_Leave(void *data, { //LastPointerWindow = nullptr; //LOG(Info, "WaylandPointerLeave serial:{}", serial); - LastPointerSerial = serial; } void WaylandPointer_Motion(void *data, @@ -1032,12 +1619,13 @@ void WaylandPointer_Motion(void *data, void WaylandPointer_Button(void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - //LOG(Info, "WaylandPointerButton serial:{}", serial); + LOG(Info, "WaylandPointerButton serial:{}, button:{}, state:{}", serial, button, state); // HACK: We store the serial for upcoming drag-and-drop action even though we are // not really performing the action during this specific button press event. - // SDL receives the same event which actually starts the DnD process. - LastPointerSerial = serial; + // SDL receives the same event which actually starts the drag process. + if (state == 1) + ImplicitGrabSerial = serial; } void WaylandPointer_Axis(void *data, @@ -1130,7 +1718,7 @@ wl_seat_listener SeatListener = { SeatCapabilities, SeatName }; void WaylandRegistryGlobal(void* data, wl_registry *registry, uint32 id, const char* interface, uint32 version) { StringAnsi interfaceStr(interface); - LOG(Info, "WaylandRegistryGlobal id: {}, interface: {}", id, String(interface)); + //LOG(Info, "WaylandRegistryGlobal id: {}, interface: {}", id, String(interface)); if (interfaceStr == "xdg_toplevel_drag_manager_v1") DragManager = (xdg_toplevel_drag_manager_v1*)wl_registry_bind(registry, id, &xdg_toplevel_drag_manager_v1_interface, 1U); else if (interfaceStr == "wl_seat") diff --git a/Source/Engine/Platform/SDL/SDLPlatform.cpp b/Source/Engine/Platform/SDL/SDLPlatform.cpp index 460547c2b..86d5c744e 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.cpp @@ -45,6 +45,7 @@ bool SDLPlatform::Init() SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE); else SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE); + //SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE); #endif #if PLATFORM_LINUX @@ -76,17 +77,17 @@ bool SDLPlatform::Init() SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1"); // Disable SDL clipboard support - SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false); + /*SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false); // Disable SDL drag and drop support SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, false); SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, false); SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false); SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false); - SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false); + SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);*/ - if (InitPlatform()) - return true; + //if (InitPlatform()) + // return true; if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError()))); @@ -107,7 +108,9 @@ bool SDLPlatform::Init() } } SDL_free(locales); - + + if (InitPlatform()) + return true; SDLInput::Init(); diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp index ab6a9d23b..e9e006495 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.cpp +++ b/Source/Engine/Platform/SDL/SDLWindow.cpp @@ -40,11 +40,12 @@ extern Dictionary SurfaceToWindowMap; extern SDLWindow* LastPointerWindow; extern Int2 LastPointerPosition; -extern uint32 LastPointerSerial; +extern uint32 ImplicitGrabSerial; extern xdg_toplevel_drag_manager_v1* DragManager; extern wl_seat* WaylandSeat; extern wl_data_device_manager* WaylandDataDeviceManager; extern xdg_wm_base* WaylandXdgWmBase; +extern bool waylandDraggingActive; #endif #define DefaultDPI 96 @@ -452,6 +453,7 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_MOUSE_MOTION: { + //LOG(Info, "SDL_EVENT_MOUSE_MOTION"); if (_isTrackingMouse && _isUsingMouseOffset) { Float2 delta(event.motion.xrel, event.motion.yrel); @@ -588,6 +590,7 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_FOCUS_GAINED: { + LOG(Info, "focus {}", GetTitle()); #if PLATFORM_LINUX _forcedFocus = false; #endif @@ -603,6 +606,7 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_FOCUS_LOST: { + LOG(Info, "focus lost {}", GetTitle()); #if PLATFORM_LINUX _forcedFocus = false; #endif @@ -629,10 +633,24 @@ void SDLWindow::HandleEvent(SDL_Event& event) } return; } -#if false +#if true + case SDL_EVENT_CLIPBOARD_UPDATE: + { + LOG(Info, "SDL_EVENT_CLIPBOARD_UPDATE"); + return; + } case SDL_EVENT_DROP_BEGIN: { - Focus(); + LOG(Info, "SDL_EVENT_DROP_BEGIN"); + + auto dpiScale = GetDpiScale(); + const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); + DragDropEffect effect = DragDropEffect::None; + auto daata = event.drop.data; + + SDLDropTextData dropData; + OnDragEnter(&dropData, mousePos, effect); + /*Focus(); Float2 mousePosition; SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); mousePosition = ScreenToClient(mousePosition); @@ -640,20 +658,37 @@ void SDLWindow::HandleEvent(SDL_Event& event) DragDropEffect effect; SDLDropTextData dropData; OnDragEnter(&dropData, mousePosition, effect); - OnDragOver(&dropData, mousePosition, effect); + OnDragOver(&dropData, mousePosition, effect);*/ return; } case SDL_EVENT_DROP_POSITION: { + auto dpiScale = GetDpiScale(); + const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); DragDropEffect effect = DragDropEffect::None; + auto daata = event.drop.data; SDLDropTextData dropData; - OnDragOver(&dropData, Float2(static_cast(event.drop.x), static_cast(event.drop.y)), effect); - return; + OnDragOver(&dropData, mousePos, effect); + /*DragDropEffect effect = DragDropEffect::None; + + SDLDropTextData dropData; + OnDragOver(&dropData, Float2(static_cast(event.drop.x), static_cast(event.drop.y)), effect);*/ + break; } case SDL_EVENT_DROP_FILE: { - SDLDropFilesData dropData; + LOG(Info, "SDL_EVENT_DROP_FILE"); + auto dpiScale = GetDpiScale(); + const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); + DragDropEffect effect = DragDropEffect::None; + auto daata = event.drop.data; + + SDLDropTextData dropData; + OnDragDrop(&dropData, mousePos, effect); + + + /*SDLDropFilesData dropData; dropData.Files.Add(StringAnsi(event.drop.data).ToString()); // TODO: collect multiple files at once? Focus(); @@ -662,12 +697,23 @@ void SDLWindow::HandleEvent(SDL_Event& event) SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); mousePosition = ScreenToClient(mousePosition); DragDropEffect effect = DragDropEffect::None; - OnDragDrop(&dropData, mousePosition, effect); + OnDragDrop(&dropData, mousePosition, effect);*/ return; } case SDL_EVENT_DROP_TEXT: { + auto dpiScale = GetDpiScale(); + const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); + DragDropEffect effect = DragDropEffect::None; + auto daata = event.drop.data; + auto str = String(event.drop.data); + + LOG(Info, "SDL_EVENT_DROP_TEXT: {}", str); + SDLDropTextData dropData; + OnDragDrop(&dropData, mousePos, effect); + + /*SDLDropTextData dropData; String str = StringAnsi(event.drop.data).ToString(); dropData.Text = StringView(str); @@ -676,11 +722,23 @@ void SDLWindow::HandleEvent(SDL_Event& event) SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); mousePosition = ScreenToClient(mousePosition); DragDropEffect effect = DragDropEffect::None; - OnDragDrop(&dropData, mousePosition, effect); + OnDragDrop(&dropData, mousePosition, effect);*/ return; } case SDL_EVENT_DROP_COMPLETE: { + LOG(Info, "SDL_EVENT_DROP_COMPLETE"); + auto dpiScale = GetDpiScale(); + const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); + DragDropEffect effect = DragDropEffect::None; + auto daata = event.drop.data; + + OnDragLeave(); + + if (SDLPlatform::UsesWayland()) + { + //_dragOver = false; + } return; } #endif @@ -694,11 +752,14 @@ void SDLWindow::HandleEvent(SDL_Event& event) #if PLATFORM_LINUX case SDL_EVENT_MOUSE_BUTTON_UP: { - if (SDLPlatform::UsesWayland() && _dragOver) + if (SDLPlatform::UsesWayland() && waylandDraggingActive) { + LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP, dragging"); // We are dragging a window, keep the button held down return; } + else + LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP"); break; } #endif @@ -1207,272 +1268,45 @@ void SDLWindow::UpdateCursor() SDL_SetCursor(Cursors[index]); } -wl_data_offer* WaylandDataOffer = nullptr; // The last accepted offer -uint32 WaylandDataOfferSerial = 0; // The last accepted serial for offer -StringAnsi WaylandDataOfferMimeType; -SDLWindow* DragTargetWindow = nullptr; -Float2 DragTargetPosition; -void WaylandDataOffer_Offer(void* data, wl_data_offer* offer, const char *mime_type) + +//extern wl_data_device* dataDevice; +//wl_data_source* dataSource; + +/*void OnBeforeSurfaceCommit(void* data) { - // We are being offered these types of data - //LOG(Info, "WaylandDataOffer_Offer: {}", String(mime_type)); - - //if (WaylandDataOffer == nullptr) - // return; + wl_display_flush((wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr)); - if (StringAnsi(mime_type) == "x.flaxengine.window.snap") - WaylandDataOfferMimeType = StringAnsi(mime_type); - // wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, mime_type); -} - -void WaylandDataOffer_SourceActions(void *data, - struct wl_data_offer *wl_data_offer, - uint32_t source_actions) -{ - // - //LOG(Info, "WaylandDataOffer_SourceActions: {}", source_actions); -} - -void WaylandDataOffer_Action(void *data, - struct wl_data_offer *wl_data_offer, - uint32_t dnd_action) -{ - // DnD: This action will be performed if dropped - //LOG(Info, "WaylandDataOffer_Action: {}", dnd_action); -} - -wl_data_offer* WaylandDataSelectionOffer = nullptr; -wl_data_offer_listener WaylandDataOfferListener = { WaylandDataOffer_Offer, WaylandDataOffer_SourceActions, WaylandDataOffer_Action}; - -void WaylandDataDevice_DataOffer(void *data, wl_data_device *wl_data_device, wl_data_offer *id) -{ - //LOG(Info, "WaylandDataDevice_DataOffer: {}", (uint64)id); - int ret = wl_data_offer_add_listener(id, &WaylandDataOfferListener, nullptr); - if (ret != 0) - LOG(Error, "wl_data_offer_add_listener failed"); -} - -void WaylandDataDevice_Enter(void *data, wl_data_device *wl_data_device, uint32 serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id) -{ - // DnD: The cursor entered a target surface - LOG(Info, "WaylandDataDevice_Enter serial: {}, surface: {}, pos: {}x{}, id: {}", serial, (uint64)surface, wl_fixed_to_double(x), wl_fixed_to_double(y), (uint64)id); - WaylandDataOffer = id; - WaylandDataOfferSerial = serial; - - DragTargetPosition = Float2(MAX_float, MAX_float); - SDLWindow* sourceWindow = (SDLWindow*)data; - if (!SurfaceToWindowMap.TryGet(surface, DragTargetWindow)) - DragTargetWindow = nullptr; - if (DragTargetWindow != nullptr) - DragTargetWindow = DragTargetWindow; - if (/*SurfaceToWindowMap.TryGet(surface, DragTargetWindow) && */DragTargetWindow != sourceWindow) - { - // Inform that we support the following action at this given point - wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE); - - if (WaylandDataOfferMimeType == "x.flaxengine.window.snap") - wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, "x.flaxengine.window.snap"); - } - else - { - wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); - } -} - -void WaylandDataDevice_Leave(void *data, wl_data_device *wl_data_device) -{ - // DnD: The cursor left the surface area - // id from enter must be destroyed here - LOG(Info, "WaylandDataDevice_Leave"); - - if (WaylandDataOffer != nullptr) - wl_data_offer_destroy(WaylandDataOffer); - WaylandDataOffer = nullptr; - WaylandDataOfferSerial = 0; - WaylandDataOfferMimeType = StringAnsi::Empty; -} - -void WaylandDataDevice_Motion(void *data, wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) -{ - // DnD: The cursor moves along the surface - Float2 dragPosition(wl_fixed_to_double(x), wl_fixed_to_double(y)); - //LOG(Info, "WaylandDataDevice_Motion {},{}", (int)dragPosition.X, (int)dragPosition.Y); - - - if (DragTargetWindow != nullptr) - { - Float2 mousePos = dragPosition * DragTargetWindow->GetDpiScale(); - mousePos = Float2::Floor(mousePos); - if (DragTargetPosition != mousePos) - { - //LOG(Info, "{}: {}", time, mousePos); - Input::Mouse->OnMouseMove(mousePos, DragTargetWindow); - DragTargetPosition = mousePos; - } - } - //SDLWindow* targetWindow; - //if (SurfaceToWindowMap.TryGet(surface, targetWindow) && targetWindow == surfaceWindow) -} - -void WaylandDataDevice_Drop(void *data, wl_data_device *wl_data_device) -{ - // DnD: The drop is accepted - LOG(Info, "WaylandDataDevice_Drop"); - - /*int fds[2]; - pipe(fds); - wl_data_offer_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - // TODO: do something with fds[0] - close(fds[0]);*/ - - wl_data_offer_finish(WaylandDataOffer); - wl_data_offer_destroy(WaylandDataOffer); - WaylandDataOffer = nullptr; -} - -void WaylandDataDevice_Selection(void *data, wl_data_device *wl_data_device, wl_data_offer *id) -{ - // Clipboard: We can read the clipboard content - /* - int fds[2]; - pipe(fds); - wl_data_offer_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - wl_display_roundtrip(display); - - while (true) - { - char buf[1024]; - ssize_t n = read(fds[0], buf, sizeof(buf)); - if (n <= 0) - break; - //fwrite(buf, 1, n, stdout); - } - close(fds[0]); - wl_data_offer_destroy(offer); - */ - - - LOG(Info, "WaylandDataDevice_Selection: {}", (uint64)id); - - if (WaylandDataSelectionOffer != nullptr) - wl_data_offer_destroy(WaylandDataSelectionOffer); - WaylandDataSelectionOffer = id; -} - -wl_data_device_listener WaylandDataDeviceListener = -{ - WaylandDataDevice_DataOffer, - WaylandDataDevice_Enter, - WaylandDataDevice_Leave, - WaylandDataDevice_Motion, - WaylandDataDevice_Drop, - WaylandDataDevice_Selection -}; - -xdg_toplevel_drag_v1* toplevelDrag = nullptr; - -void WaylandDataSource_Target(void *data, - struct wl_data_source *wl_data_source, - const char *mime_type) -{ - // The destination accepts the following types, or null if nothing SDLWindow* window = static_cast(data); - //LOG(Info, "WaylandDataSource_Target mime: {}", String(mime_type)); -} + LOG(Info, "OnBeforeSurfaceCommit"); -void WaylandDataSource_Send(void *data, - struct wl_data_source *wl_data_source, - const char *mime_type, - int32_t fd) -{ - // Clipboard: The other end has accepted the data? - SDLWindow* window = static_cast(data); - //LOG(Info, "WaylandDataSource_Send mime: {}", String(mime_type)); + auto _window = window->GetSDLWindow(); + SDL_SetPointerProperty(SDL_GetWindowProperties(_window), "SDL.window.wayland.callback", nullptr); + SDL_SetPointerProperty(SDL_GetWindowProperties(_window), "SDL.window.wayland.callback.data", nullptr); - // write stuff to the file descriptor - //write(fd, text, strlen(text)); - //close(fd); -} + xdg_toplevel* toplevel = (xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr); + toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); -void WaylandDataSource_Cancelled(void* data, wl_data_source *source) -{ - // Clipboard: other application has replaced the content in clipboard - SDLWindow* window = static_cast(data); - LOG(Info, "WaylandDataSource_Cancelled"); - //wl_data_source_destroy(source); + wl_surface* origin = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + wl_surface* icon = nullptr; + uint32 id = LastPointerSerial; + wl_data_device_start_drag(dataDevice, dataSource, origin, icon, id); - /*if (DragTargetWindow != nullptr) - { - Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow); - } - else*/ if (window != nullptr) - { - Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window); - } -} + Float2 offset = Float2::Zero; + Float2 scaledOffset = offset / window->GetDpiScale(); + xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); -void WaylandDataSource_DnDDropPerformed(void *data, - struct wl_data_source *wl_data_source) -{ - // The destination is being asked to begin DnD, asking confirmation with ASK actionh - SDLWindow* window = static_cast(data); - LOG(Info, "WaylandDataSource_DnDDropPerformed"); -} - -void WaylandDataSource_DnDFinished(void *data, - struct wl_data_source *wl_data_source) -{ - // The destination has finally accepted the last given dnd_action - SDLWindow* window = static_cast(data); - LOG(Info, "WaylandDataSource_DnDFinished"); - - /*if (DragTargetWindow != nullptr) - { - Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow); - } - else*/ if (window != nullptr) - { - Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window); - } -} - -void WaylandDataSource_Action(void *data, - struct wl_data_source *wl_data_source, - uint32_t dnd_action) -{ - // DnD: The destination may accept the given action if confirmed - SDLWindow* window = static_cast(data); - //LOG(Info, "WaylandDataSource_Action: {}", String(dnd_action == 0 ? "NONE" : dnd_action == 1 ? "COPY" : dnd_action == 2 ? "MOVE" : dnd_action == 4 ? "ASK" : "")); - -} - -wl_data_source_listener WaylandDataSourceListener = -{ - WaylandDataSource_Target, - WaylandDataSource_Send, - WaylandDataSource_Cancelled, - WaylandDataSource_DnDDropPerformed, - WaylandDataSource_DnDFinished, - WaylandDataSource_Action -}; - -extern wl_data_device* dataDevice; -wl_data_source* dataSource; + wl_display_flush((wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr)); +}*/ void SDLWindow::StartDragging(const Float2& offset) { LOG(Info, "StartDragging {}", offset); + DoDragDropWayland(String("awindow")); +/* _dragOver = true; - //wl_display_flush((wl_display*)GetWaylandDisplay()); - SDL_PumpEvents(); - wl_data_device_set_user_data(dataDevice, this); // We offer the following types of things for consumption: @@ -1481,27 +1315,31 @@ void SDLWindow::StartDragging(const Float2& offset) wl_data_source_set_actions(dataSource, wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); wl_data_source_add_listener(dataSource, &WaylandDataSourceListener, this); - toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); - - wl_surface* origin = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); - wl_surface* icon = nullptr; - uint32 id = LastPointerSerial; - wl_data_device_start_drag(dataDevice, dataSource, origin, icon, id); - xdg_toplevel* toplevel = (xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr); - /*if (toplevel == nullptr) + if (toplevel == nullptr) { - auto asdf = xdg_wm_base_get_xdg_surface(WaylandXdgWmBase, origin); - toplevel = xdg_surface_get_toplevel(asdf); - }*/ - Float2 scaledOffset = offset / _dpiScale; - xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + SDL_SetPointerProperty(SDL_GetWindowProperties(_window), "SDL.window.wayland.callback", (void*)OnBeforeSurfaceCommit); + SDL_SetPointerProperty(SDL_GetWindowProperties(_window), "SDL.window.wayland.callback.data", this); + } + else + { + toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); + + wl_surface* origin = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + wl_surface* icon = nullptr; + uint32 id = LastPointerSerial; + wl_data_device_start_drag(dataDevice, dataSource, origin, icon, id); + + Float2 scaledOffset = offset / _dpiScale; + xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + } + */ } void SDLWindow::StopDragging() { LOG(Info, "StopDragging"); - +/* wl_data_device_set_user_data(dataDevice, nullptr); _dragOver = false; @@ -1516,22 +1354,8 @@ void SDLWindow::StopDragging() wl_data_source_destroy(dataSource); dataSource = nullptr; } + */ } -void SDLWindow::CreateDockHints() -{ - LOG(Info, "CreateDockHints"); - - -} - -void SDLWindow::RemoveDockHints() -{ - LOG(Info, "RemoveDockHints"); - - -} - - #endif diff --git a/Source/Engine/Platform/SDL/SDLWindow.h b/Source/Engine/Platform/SDL/SDLWindow.h index 2ed251db2..ed49e474a 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.h +++ b/Source/Engine/Platform/SDL/SDLWindow.h @@ -110,8 +110,6 @@ public: void SetCursor(CursorType type) override; void StartDragging(const Float2& offset) override; void StopDragging() override; - void CreateDockHints() override; - void RemoveDockHints() override; #if USE_EDITOR && PLATFORM_WINDOWS // [IUnknown] diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs index e6a39cbac..9b7eb5a80 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/SDL.cs @@ -88,8 +88,8 @@ namespace Flax.Deps.Dependencies Path.Combine(root, "include", "SDL3"), }; - CloneGitRepoFastSince(root, "https://github.com/libsdl-org/SDL", new DateTime(2025, 01, 06)); - GitResetToCommit(root, "8ec576ddabdc7edfd68e7a8a3214e84e4026328d"); + //CloneGitRepoFastSince(root, "https://github.com/libsdl-org/SDL", new DateTime(2025, 01, 06)); + //GitResetToCommit(root, "8ec576ddabdc7edfd68e7a8a3214e84e4026328d"); foreach (var platform in options.Platforms) { @@ -135,7 +135,7 @@ namespace Flax.Deps.Dependencies directoriesToCopy.Add(Path.Combine(buildDir, "include", "SDL3")); int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency); - RunCmake(root, platform, architecture, $"-B\"{buildDir}\" -DCMAKE_INSTALL_PREFIX=\"{buildDir}\" -DSDL_SHARED={(!buildStatic ? "ON" : "OFF")} -DSDL_STATIC={(buildStatic ? "ON" : "OFF")} -DCMAKE_POSITION_INDEPENDENT_CODE=ON " + string.Join(" ", configs)); + RunCmake(root, platform, architecture, $"-B\"{buildDir}\" -DCMAKE_BUILD_TYPE={configuration} -DCMAKE_INSTALL_PREFIX=\"{buildDir}\" -DSDL_SHARED={(!buildStatic ? "ON" : "OFF")} -DSDL_STATIC={(buildStatic ? "ON" : "OFF")} -DCMAKE_POSITION_INDEPENDENT_CODE=ON " + string.Join(" ", configs)); BuildCmake(buildDir, configuration, new Dictionary() { {"CMAKE_BUILD_PARALLEL_LEVEL", concurrency.ToString()} }); Utilities.Run("cmake", $"--build . --target install --config {configuration}", null, buildDir, Utilities.RunOptions.DefaultTool);