From 72828a9e5853cd7fcd47bcb57b9d636e110c7f69 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 28 Jan 2025 01:16:59 +0200 Subject: [PATCH] _wayland big refactor --- Source/Engine/Platform/SDL/SDLInput.cpp | 11 - .../Engine/Platform/SDL/SDLPlatform.Linux.cpp | 1462 ++++++++--------- .../Platform/SDL/SDLPlatform.Windows.cpp | 175 +- Source/Engine/Platform/SDL/SDLPlatform.cpp | 330 +--- Source/Engine/Platform/SDL/SDLPlatform.h | 23 +- Source/Engine/Platform/SDL/SDLWindow.cpp | 319 +--- Source/Engine/Platform/SDL/SDLWindow.h | 1 + 7 files changed, 851 insertions(+), 1470 deletions(-) diff --git a/Source/Engine/Platform/SDL/SDLInput.cpp b/Source/Engine/Platform/SDL/SDLInput.cpp index 6f4b445dd..c166f6ae5 100644 --- a/Source/Engine/Platform/SDL/SDLInput.cpp +++ b/Source/Engine/Platform/SDL/SDLInput.cpp @@ -519,17 +519,6 @@ 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(event.drop.x * dpiScale, event.drop.y * dpiScale);// = window->ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); - Float2 mousePos = window->ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); - if (window != Engine::MainWindow) - mousePos = window->GetPosition() - mousePos; - 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 dfcd9a386..5ee152988 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp @@ -20,77 +20,24 @@ #include "Engine/Graphics/RenderTask.h" #include "Engine/Platform/Base/DragDropHelper.h" #include "Engine/Platform/Unix/UnixFile.h" +#include "Engine/Platform/MessageBox.h" +#include #include #include #include +#include +#include +#include #include #include #include -// Wayland -void WaylandRegistryGlobal(void* data, wl_registry *registry, uint32 id, const char* interface, uint32 version); -void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id); - -Dictionary SurfaceToWindowMap; -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; -bool waylandDraggingWindow = false; -StringView waylandDraggingData = nullptr; - -// X11 -Delegate LinuxPlatform::xEventReceived; - // Missing Wayland features: // - Application icon (xdg-toplevel-icon-v1) https://github.com/libsdl-org/SDL/pull/9584 // - Color picker (xdg-desktop-portal?) https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html -// - - -namespace -{ - X11::Display* xDisplay = nullptr; - X11::XIM IM = nullptr; - X11::XIC IC = nullptr; - X11::Atom xAtomDeleteWindow; - X11::Atom xAtomXdndEnter; - X11::Atom xAtomXdndPosition; - X11::Atom xAtomXdndLeave; - X11::Atom xAtomXdndDrop; - X11::Atom xAtomXdndActionCopy; - X11::Atom xAtomXdndStatus; - X11::Atom xAtomXdndSelection; - X11::Atom xAtomXdndFinished; - X11::Atom xAtomXdndAware; - X11::Atom xAtomWmState; - X11::Atom xAtomWmStateHidden; - X11::Atom xAtomWmStateMaxVert; - X11::Atom xAtomWmStateMaxHorz; - X11::Atom xAtomWmWindowOpacity; - X11::Atom xAtomWmName; - X11::Atom xAtomAtom; - X11::Atom xAtomClipboard; - X11::Atom xAtomPrimary; - X11::Atom xAtomTargets; - X11::Atom xAtomText; - X11::Atom xAtomString; - X11::Atom xAtomUTF8String; - X11::Atom xAtomXselData; - - X11::Atom xDnDRequested = 0; - X11::Window xDndSourceWindow = 0; - DragDropEffect xDndResult; - Float2 xDndPos; - int32 xDnDVersion = 0; - int32 XFixesSelectionNotifyEvent = 0; -} +// - class LinuxDropFilesData : public IGuiData { @@ -132,15 +79,342 @@ public: } }; -struct Property +namespace WaylandImpl { - unsigned char* data; - int format, nitems; - X11::Atom type; -}; + wl_display* WaylandDisplay = nullptr; + + uint32 GrabSerial = 0; + wl_pointer_listener PointerListener = + { + [](void* data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { }, // Enter event + [](void* data, wl_pointer* wl_pointer, uint32_t serial, wl_surface* surface) { }, // Leave event + [](void* data, wl_pointer* wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { }, // Motion event + [](void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) // Button event + { + // Store the serial for upcoming drag-and-drop action + if (state == 1) + GrabSerial = serial; + }, + [](void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { }, // Axis event + [](void* data, wl_pointer* wl_pointer) { }, // Frame event + [](void* data, wl_pointer* wl_pointer, uint32_t axis_source) { }, // Axis source event + [](void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis) { }, // Axis stop event + [](void* data, wl_pointer* wl_pointer, uint32_t axis, int32_t discrete) { }, // axis discrete + [](void* data, wl_pointer* wl_pointer, uint32_t axis, int32_t value120) { }, // Scroll wheel event + [](void* data, wl_pointer* wl_pointer, uint32_t axis, uint32_t direction) { }, // Relative direction event + }; + + wl_pointer* WaylandPointer = nullptr; + wl_seat_listener SeatListener = + { + [](void* data, wl_seat* seat, uint32 capabilities) // Seat capabilities changed event + { + if ((capabilities & wl_seat_capability::WL_SEAT_CAPABILITY_POINTER) != 0) + { + WaylandPointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(WaylandPointer, &PointerListener, nullptr); + } + }, + [](void* data, wl_seat* seat, const char* name) { } // Seat name event + }; + + xdg_toplevel_drag_manager_v1* DragManager = nullptr; + wl_seat* Seat = nullptr; + wl_data_device_manager* DataDeviceManager = nullptr; + wl_registry_listener RegistryListener = + { + [](void* data, wl_registry* registry, uint32 id, const char* interface, uint32 version) // Announce global object event + { + StringAnsi interfaceStr(interface); + if (interfaceStr == "xdg_toplevel_drag_manager_v1") + DragManager = static_cast(wl_registry_bind(registry, id, &xdg_toplevel_drag_manager_v1_interface, Math::Min(1U, version))); + else if (interfaceStr == "wl_seat") + { + Seat = static_cast(wl_registry_bind(registry, id, &wl_seat_interface, Math::Min(9U, version))); + wl_seat_add_listener(Seat, &SeatListener, nullptr); + } + else if (interfaceStr == "wl_data_device_manager") + DataDeviceManager = static_cast(wl_registry_bind(registry, id, &wl_data_device_manager_interface, Math::Min(3U, version))); + }, + [] (void* data, wl_registry* registry, uint32 id) { }, // Announce global remove event + }; + + wl_data_offer* DataOffer = nullptr; // The last accepted offer + wl_data_offer* SelectionOffer = nullptr; + wl_data_device_listener DataDeviceListener = + { + [](void* data, wl_data_device* data_device, wl_data_offer* id) { }, // Data offer event + [](void* data, wl_data_device* data_device, uint32 serial, wl_surface* surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer* id) // Enter event + { + DataOffer = id; + + SDLWindow* sourceWindow = (SDLWindow*)data; + if (sourceWindow != nullptr) + { + // Let them know 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); + } + else + { + wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE); + } + }, + [](void* data, wl_data_device* data_device) // Leave event + { + // The cursor left the surface area + + if (DataOffer != nullptr) + wl_data_offer_destroy(DataOffer); + DataOffer = nullptr; + }, + [](void* data, wl_data_device* data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) { }, // Motion event + [](void* data, wl_data_device* data_device) // Drop event + { + // The drop is accepted + if (DataOffer != nullptr) + { + wl_data_offer_finish(DataOffer); + wl_data_offer_destroy(DataOffer); + DataOffer = nullptr; + } + }, + [](void* data, wl_data_device* data_device, wl_data_offer* id) // Selection event + { + // Clipboard: We can read the clipboard content + if (SelectionOffer != nullptr) + wl_data_offer_destroy(SelectionOffer); + SelectionOffer = id; + }, + }; + + int64 DragOverFlag = 0; + wl_data_source_listener DataSourceListener = + { + [](void* data, wl_data_source* source, const char* mime_type) { }, // Target event + [](void* data, wl_data_source* source, const char* mime_type, int32_t fd) // Send event + { + // Clipboard: The other end has accepted and is requesting the data + IGuiData* inputData = static_cast(data); + 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(); + } + }, + [](void* data, wl_data_source* source) // Cancelled event + { + // Clipboard: other application has replaced the content in clipboard + IGuiData* inputData = static_cast(data); + Platform::AtomicStore(&WaylandImpl::DragOverFlag, 1); + + wl_data_source_destroy(source); + }, + [](void* data, wl_data_source* source) { }, // DnD drop performed event + [](void* data, wl_data_source* source) // DnD Finished event + { + // The destination has finally accepted the last given dnd_action + IGuiData* inputData = static_cast(data); + Platform::AtomicStore(&WaylandImpl::DragOverFlag, 1); + + wl_data_source_destroy(source); + }, + [](void* data, wl_data_source* source, uint32_t dnd_action) { }, // Action event + }; + + wl_data_device* DataDevice = nullptr; + wl_event_queue* EventQueue = nullptr; + wl_data_device_manager* WrappedDataDeviceManager = nullptr; + wl_data_device* WrappedDataDevice = nullptr; + bool DraggingActive = false; + bool DraggingWindow = false; + StringView DraggingData = nullptr; + class DragDropJob : public ThreadPoolTask + { + public: + int64 StartFlag = 0; + int64 ExitFlag = 0; + StringView data = nullptr; + SDLWindow* window = nullptr; + SDLWindow* dragSourceWindow = nullptr; + Float2 dragOffset = Float2::Zero; + int64 waitFlag = 0; + + // [ThreadPoolTask] + bool Run() override + { + bool dragWindow = data == String("notawindow"); + uint32 grabSerial = GrabSerial; + + if (EventQueue == nullptr) + { + if (WrappedDataDevice != nullptr) + wl_proxy_wrapper_destroy(WrappedDataDevice); + if (WrappedDataDeviceManager != nullptr) + wl_proxy_wrapper_destroy(WrappedDataDeviceManager); + 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 (EventQueue != nullptr) + wl_event_queue_destroy(EventQueue); + EventQueue = wl_display_create_queue(WaylandDisplay); + + WrappedDataDeviceManager = (wl_data_device_manager*)wl_proxy_create_wrapper(DataDeviceManager); + wl_proxy_set_queue((wl_proxy*)WrappedDataDeviceManager, EventQueue); + + DataDevice = wl_data_device_manager_get_data_device(WrappedDataDeviceManager, Seat); + wl_data_device_add_listener(DataDevice, &DataDeviceListener, nullptr); + wl_display_roundtrip(WaylandDisplay); + wl_data_device_set_user_data(DataDevice, dragWindow ? dragSourceWindow : window); + WrappedDataDevice = (wl_data_device*)wl_proxy_create_wrapper(DataDevice); + wl_proxy_set_queue((wl_proxy*)WrappedDataDevice, EventQueue); + } + + // Offer data for consumption, the data source is destroyed elsewhere + wl_data_source* dataSource = wl_data_device_manager_create_data_source(WrappedDataDeviceManager); + wl_data_source* wrappedDataSource = (wl_data_source*)wl_proxy_create_wrapper(dataSource); + wl_proxy_set_queue((wl_proxy*)wrappedDataSource, EventQueue); + if (dragWindow) + { + 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_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_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + } + + LinuxDropTextData textData; + textData.Text = *data; + textData.Window = window; + textData.dragOver = &DragOverFlag; + wl_data_source_add_listener(dataSource, &DataSourceListener, &textData); + + auto _window = window->GetSDLWindow(); + auto _mainwindow = dragSourceWindow != nullptr ? dragSourceWindow->GetSDLWindow() : _window; + wl_surface* originSurface = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_mainwindow), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr); + wl_surface* iconSurface = nullptr; + wl_data_device_start_drag(WrappedDataDevice, dataSource, originSurface, iconSurface, grabSerial); + + Platform::AtomicStore(&StartFlag, 1); + + xdg_toplevel_drag_v1* toplevelDrag = nullptr; + xdg_toplevel* wrappedToplevel = nullptr; + + while (Platform::AtomicRead(&ExitFlag) == 0) + { + if (wl_display_dispatch_queue(WaylandDisplay, EventQueue) == -1) + LOG(Warning, "wl_display_dispatch_queue failed, errno: {}", errno); + if (wl_display_roundtrip_queue(WaylandDisplay, EventQueue) == -1) + LOG(Warning, "wl_display_roundtrip_queue failed, errno: {}", errno); + + if (wrappedToplevel == nullptr && dragWindow) + { + if (Platform::AtomicRead(&waitFlag) != 0) + { + auto 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, EventQueue); + toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource); + + Float2 scaledOffset = dragOffset / window->GetDpiScale(); + xdg_toplevel_drag_v1_attach(toplevelDrag, wrappedToplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y); + } + } + } + } + + if (wl_display_roundtrip_queue(WaylandDisplay, EventQueue) == -1) + LOG(Warning, "wl_display_roundtrip_queue failed, errno: {}", errno); + + if (toplevelDrag != nullptr) + { + wl_proxy_wrapper_destroy(wrappedToplevel); + xdg_toplevel_drag_v1_destroy(toplevelDrag); + toplevelDrag = nullptr; + } + + if (wrappedDataSource != nullptr) + wl_proxy_wrapper_destroy(wrappedDataSource); + + if (SelectionOffer != nullptr) + { + wl_data_offer_destroy(SelectionOffer); + SelectionOffer = nullptr; + } + + // We can't release the queue immediately due to some resources being still used for a while + /*if (WaylandQueue != nullptr) + { + wl_event_queue_destroy(WaylandQueue); + WaylandQueue = nullptr; + }*/ + + return false; + } + }; +} namespace Impl { + Window* draggedWindow = nullptr; +} +using namespace Impl; + +// X11 +Delegate LinuxPlatform::xEventReceived; + +namespace X11Impl +{ + struct Property + { + unsigned char* data; + int format, nitems; + X11::Atom type; + }; + + X11::Display* xDisplay = nullptr; + X11::XIM IM = nullptr; + X11::XIC IC = nullptr; + X11::Atom xAtomDeleteWindow; + X11::Atom xAtomXdndEnter; + X11::Atom xAtomXdndPosition; + X11::Atom xAtomXdndLeave; + X11::Atom xAtomXdndDrop; + X11::Atom xAtomXdndActionCopy; + X11::Atom xAtomXdndStatus; + X11::Atom xAtomXdndSelection; + X11::Atom xAtomXdndFinished; + X11::Atom xAtomXdndAware; + X11::Atom xAtomWmState; + X11::Atom xAtomWmStateHidden; + X11::Atom xAtomWmStateMaxVert; + X11::Atom xAtomWmStateMaxHorz; + X11::Atom xAtomWmWindowOpacity; + X11::Atom xAtomWmName; + X11::Atom xAtomAtom; + X11::Atom xAtomClipboard; + X11::Atom xAtomPrimary; + X11::Atom xAtomTargets; + X11::Atom xAtomText; + X11::Atom xAtomString; + X11::Atom xAtomUTF8String; + X11::Atom xAtomXselData; + + X11::Atom xDnDRequested = 0; + X11::Window xDndSourceWindow = 0; + DragDropEffect xDndResult; + Float2 xDndPos; + int32 xDnDVersion = 0; + int32 XFixesSelectionNotifyEvent = 0; + StringAnsi ClipboardText; void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window) @@ -268,6 +542,7 @@ namespace Impl return Float2((float)x, (float)y); } } +//using namespace X11Impl; DragDropEffect Window::DoDragDrop(const StringView& data) { @@ -280,537 +555,9 @@ 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; - SDLWindow* dragSourceWindow; - Float2 dragOffset = Float2::Zero; - int64 dragOver = 0; - int64 waitFlag = 0; - - // [ThreadPoolTask] - bool Run() override - { - bool dragWindow = data == String("notawindow"); - 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, dragWindow ? dragSourceWindow : 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 (dragWindow) - { - 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(); - auto _mainwindow = dragSourceWindow != nullptr ? dragSourceWindow->GetSDLWindow() : _window; - //if (!window->IsVisible()) - // _window = mainwindow->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(_mainwindow), 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 (dragWindow) - { - 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 scaledOffset = dragOffset / 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 && dragWindow) - { - 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 scaledOffset = dragOffset / 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, Window* dragSourceWindow, Float2 dragOffset) { - // For drag-and-drop, we need to setup the event queue in separate thread to avoid racing issues + // For drag-and-drop, we need to run another event queue in a separate thread to avoid racing issues // while SDL is dispatching the main Wayland event queue when receiving the data offer from us. // Show()? @@ -846,16 +593,17 @@ DragDropEffect Window::DoDragDropWayland(const StringView& data, Window* dragSou Platform::Sleep(1); } - waylandDraggingActive = true; - waylandDraggingWindow = data == String("notawindow"); - waylandDraggingData = StringView(data.Get(), data.Length()); - - auto task = New(); + WaylandImpl::DraggingActive = true; + WaylandImpl::DraggingWindow = data == String("notawindow"); + WaylandImpl::DraggingData = StringView(data.Get(), data.Length()); + WaylandImpl::DragOverFlag = 0; + + auto task = New(); task->data = data; task->window = this; task->dragSourceWindow = dragSourceWindow; // Needs to be the parent window when dragging a tab to window - task->dragOver = 0; task->dragOffset = dragOffset; + task->grabSerial = WaylandImpl::GrabSerial; Task::StartNew(task); while (task->GetState() == TaskState::Queued) Platform::Sleep(1); @@ -865,11 +613,7 @@ DragDropEffect Window::DoDragDropWayland(const StringView& data, Window* dragSou Platform::Sleep(1); } - //Show(); - //Focus(); - int counter = 100; - - while (Platform::AtomicRead(&task->dragOver) == 0) + while (Platform::AtomicRead(&WaylandImpl::DragOverFlag) == 0) { SDLPlatform::Tick(); Engine::OnUpdate();//Scripting::Update(); // For docking updates @@ -879,10 +623,7 @@ DragDropEffect Window::DoDragDropWayland(const StringView& data, Window* dragSou if (IsVisible() && Platform::AtomicRead(&task->waitFlag) == 0) { - /*if (counter > 0) - counter--; - else*/ - Platform::AtomicStore(&task->waitFlag, 1); + Platform::AtomicStore(&task->waitFlag, 1); } } @@ -892,15 +633,16 @@ DragDropEffect Window::DoDragDropWayland(const StringView& data, Window* dragSou Platform::AtomicStore(&task->ExitFlag, 1); task->Wait(); - waylandDraggingActive = false; - waylandDraggingWindow = false; - waylandDraggingData = nullptr; + WaylandImpl::DraggingActive = false; + WaylandImpl::DraggingWindow = false; + WaylandImpl::DraggingData = nullptr; return DragDropEffect::None; } DragDropEffect Window::DoDragDropX11(const StringView& data) { + using namespace X11Impl; auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54); auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24); auto cursorGood = X11::XCreateFontCursor(xDisplay, 4); @@ -988,7 +730,7 @@ DragDropEffect Window::DoDragDropX11(const StringView& data) else if (event.type == MotionNotify) { // Find window under mouse - auto window = Impl::FindAppWindow(xDisplay, rootWindow); + auto window = X11Impl::FindAppWindow(xDisplay, rootWindow); int fmt, version = -1; X11::Atom atmp; unsigned long nitems, bytesLeft; @@ -1055,7 +797,7 @@ DragDropEffect Window::DoDragDropX11(const StringView& data) auto ww = WindowsManager::GetByNativePtr((void*)window); if (ww) { - xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition()); + xDndPos = ww->ScreenToClient(X11Impl::GetX11MousePosition()); xDndResult = DragDropEffect::None; ww->OnDragEnter(&dropData, xDndPos, xDndResult); } @@ -1084,7 +826,7 @@ DragDropEffect Window::DoDragDropX11(const StringView& data) auto ww = WindowsManager::GetByNativePtr((void*)window); if (ww) { - xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition()); + xDndPos = ww->ScreenToClient(X11Impl::GetX11MousePosition()); ww->_dragOver = true; xDndResult = DragDropEffect::None; ww->OnDragOver(&dropData, xDndPos, xDndResult); @@ -1131,7 +873,7 @@ DragDropEffect Window::DoDragDropX11(const StringView& data) auto ww = WindowsManager::GetByNativePtr((void*)previousWindow); if (ww) { - xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition()); + xDndPos = ww->ScreenToClient(X11Impl::GetX11MousePosition()); xDndResult = DragDropEffect::None; ww->OnDragDrop(&dropData, xDndPos, xDndResult); ww->Focus(); @@ -1215,6 +957,124 @@ DragDropEffect Window::DoDragDropX11(const StringView& data) return result; } +void SDLPlatform::PreHandleEvents() +{ +} + +void SDLPlatform::PostHandleEvents() +{ + // Handle window dragging release here + if (draggedWindow != nullptr) + { + Float2 mousePosition; + auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); + bool buttonReleased = (buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) == 0; + if (buttonReleased) + { + // Send simulated mouse up event + SDL_Event buttonUpEvent { 0 }; + buttonUpEvent.motion.type = SDL_EVENT_MOUSE_BUTTON_UP; + buttonUpEvent.button.down = false; + buttonUpEvent.motion.windowID = SDL_GetWindowID(draggedWindow->GetSDLWindow()); + buttonUpEvent.motion.timestamp = SDL_GetTicksNS(); + buttonUpEvent.motion.state = SDL_BUTTON_LEFT; + buttonUpEvent.button.clicks = 1; + buttonUpEvent.motion.x = mousePosition.X; + buttonUpEvent.motion.y = mousePosition.Y; + draggedWindow->HandleEvent(buttonUpEvent); + draggedWindow = nullptr; + } + } +} + +bool SDLWindow::HandleEventInternal(SDL_Event& event) +{ + switch (event.type) + { + case SDL_EVENT_WINDOW_MOVED: + { + if (SDLPlatform::UsesX11()) + { + // X11 doesn't report any mouse events when mouse is over the caption area, send a simulated event instead... + Float2 mousePosition; + auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); + if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0 && draggedWindow == nullptr) + { + // TODO: verify mouse position, window focus + bool result = false; + OnLeftButtonHit(WindowHitCodes::Caption, result); + if (result) + draggedWindow = this; + } + } + break; + } + case SDL_EVENT_MOUSE_BUTTON_UP: + { + if (SDLPlatform::UsesWayland() && WaylandImpl::DraggingActive) + { + // We are dragging a window, keep the button held down + return true; + } + break; + } + case SDL_EVENT_WINDOW_MOUSE_LEAVE: + { + OnDragLeave(); // Check for release of mouse button too? + break; + } + case SDL_EVENT_DROP_BEGIN: + case SDL_EVENT_DROP_POSITION: + case SDL_EVENT_DROP_FILE: + case SDL_EVENT_DROP_TEXT: + case SDL_EVENT_DROP_COMPLETE: + { + if (SDLPlatform::UsesWayland()) + { + // HACK: We can't use Wayland listeners due to SDL also using them at the same time causes + // some of the events to drop and make it impossible to implement dragging on application side. + // We can get enough information through SDL_EVENT_DROP_* events to fill in the blanks for the + // drag and drop implementation. + + auto dpiScale = GetDpiScale(); + const Float2 mousePos = Float2(event.drop.x * dpiScale, event.drop.y * dpiScale); + DragDropEffect effect = DragDropEffect::None; + String text(event.drop.data); + + LinuxDropTextData dropData; + dropData.Text = text; + + if (event.type == SDL_EVENT_DROP_BEGIN || event.type == SDL_EVENT_DROP_POSITION) + { + // We don't have the drag data during these events... + dropData.Text = WaylandImpl::DraggingData; + } + + if (event.type == SDL_EVENT_DROP_BEGIN) + { + OnDragEnter(&dropData, mousePos, effect); + } + else if (event.type == SDL_EVENT_DROP_POSITION) + { + Input::Mouse->OnMouseMove(ClientToScreen(mousePos), this); + OnDragOver(&dropData, mousePos, effect); + } + else if (event.type == SDL_EVENT_DROP_FILE) + OnDragDrop(&dropData, mousePos, effect); + else if (event.type == SDL_EVENT_DROP_TEXT) + OnDragDrop(&dropData, mousePos, effect); + else if (event.type == SDL_EVENT_DROP_COMPLETE) + OnDragLeave(); + } + break; + } + default: + break; + } + + return false; +} + void SDLClipboard::Clear() { SetText(StringView::Empty); @@ -1228,14 +1088,14 @@ void SDLClipboard::SetText(const StringView& text) if (!mainWindow) return; - if (xDisplay) + if (X11Impl::xDisplay) { X11::Window window = (X11::Window)(mainWindow->GetX11WindowHandle()); - Impl::ClipboardText.Set(text.Get(), text.Length()); - X11::XSetSelectionOwner(xDisplay, xAtomClipboard, window, CurrentTime); // CLIPBOARD + X11Impl::ClipboardText.Set(text.Get(), text.Length()); + X11::XSetSelectionOwner(X11Impl::xDisplay, X11Impl::xAtomClipboard, window, CurrentTime); // CLIPBOARD //X11::XSetSelectionOwner(xDisplay, xAtomPrimary, window, CurrentTime); // XA_PRIMARY - X11::XFlush(xDisplay); - X11::XGetSelectionOwner(xDisplay, xAtomClipboard); + X11::XFlush(X11Impl::xDisplay); + X11::XGetSelectionOwner(X11Impl::xDisplay, X11Impl::xAtomClipboard); //X11::XGetSelectionOwner(xDisplay, xAtomPrimary); } else @@ -1260,20 +1120,20 @@ String SDLClipboard::GetText() auto mainWindow = Engine::MainWindow; if (!mainWindow) return result; - if (xDisplay) + if (X11Impl::xDisplay) { - X11::Window window = (X11::Window)mainWindow->GetX11WindowHandle(); + X11::Window window = static_cast(mainWindow->GetX11WindowHandle()); - Impl::ClipboardGetText(result, xAtomClipboard, xAtomUTF8String, window); + X11Impl::ClipboardGetText(result, X11Impl::xAtomClipboard, X11Impl::xAtomUTF8String, window); if (result.HasChars()) return result; - Impl::ClipboardGetText(result, xAtomClipboard, xAtomString, window); + X11Impl::ClipboardGetText(result, X11Impl::xAtomClipboard, X11Impl::xAtomString, window); if (result.HasChars()) return result; - Impl::ClipboardGetText(result, xAtomPrimary, xAtomUTF8String, window); + X11Impl::ClipboardGetText(result, X11Impl::xAtomPrimary, X11Impl::xAtomUTF8String, window); if (result.HasChars()) return result; - Impl::ClipboardGetText(result, xAtomPrimary, xAtomString, window); + X11Impl::ClipboardGetText(result, X11Impl::xAtomPrimary, X11Impl::xAtomString, window); return result; } else @@ -1293,8 +1153,9 @@ Array SDLClipboard::GetFiles() return Array(); } -bool SDLCALL SDLPlatform::X11EventHook(void *userdata, _XEvent *xevent) +bool SDLCALL SDLPlatform::X11EventHook(void* userdata, _XEvent* xevent) { + using namespace X11Impl; const X11::XEvent& event = *(X11::XEvent*)xevent; Window* window; @@ -1311,13 +1172,13 @@ bool SDLCALL SDLPlatform::X11EventHook(void *userdata, _XEvent *xevent) const char* targetTypeFiles = "text/uri-list"; if (event.xclient.data.l[1] & 1) { - Property p = Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0)); - xDnDRequested = Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems); + Property p = X11Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0)); + xDnDRequested = X11Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems); X11::XFree(p.data); } else { - xDnDRequested = Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); + xDnDRequested = X11Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); } return false; } @@ -1402,7 +1263,7 @@ bool SDLCALL SDLPlatform::X11EventHook(void *userdata, _XEvent *xevent) window = WindowsManager::GetByNativePtr((void*)event.xany.window); if (window) { - Property p = Impl::ReadProperty(xDisplay, event.xany.window, xAtomPrimary); + Property p = X11Impl::ReadProperty(xDisplay, event.xany.window, xAtomPrimary); if (xDndResult != DragDropEffect::None) { LinuxDropFilesData dropData; @@ -1457,9 +1318,9 @@ bool SDLCALL SDLPlatform::X11EventHook(void *userdata, _XEvent *xevent) result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomAtom, 32, PropModeReplace, (unsigned char*)types.Get(), types.Count()); } else if (ev.target == xAtomString || ev.target == xAtomText) - result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomString, 8, PropModeReplace, (unsigned char*)Impl::ClipboardText.Get(), Impl::ClipboardText.Length()); + result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomString, 8, PropModeReplace, (unsigned char*)X11Impl::ClipboardText.Get(), X11Impl::ClipboardText.Length()); else if (ev.target == xAtomUTF8String) - result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomUTF8String, 8, PropModeReplace, (unsigned char*)Impl::ClipboardText.Get(), Impl::ClipboardText.Length()); + result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomUTF8String, 8, PropModeReplace, (unsigned char*)X11Impl::ClipboardText.Get(), X11Impl::ClipboardText.Length()); else ev.property = 0; if ((result & 2) == 0) @@ -1483,7 +1344,7 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event) return 0; } -bool SDLPlatform::InitPlatform() +bool SDLPlatform::InitInternal() { //if (LinuxPlatform::Init()) // return true; @@ -1511,28 +1372,13 @@ bool SDLPlatform::InitPlatform() String waylandDisplayEnv; if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv)) { - WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr); - if (WaylandDisplay == nullptr) + WaylandImpl::WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr); + if (WaylandImpl::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); - - 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); - wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr); - - wl_display_roundtrip(WaylandDisplay);*/ + // Tap into Wayland registry so we can start listening for events + wl_registry* registry = wl_display_get_registry(WaylandImpl::WaylandDisplay); + wl_registry_add_listener(registry, &WaylandImpl::RegistryListener, nullptr); + wl_display_roundtrip(WaylandImpl::WaylandDisplay); } } } @@ -1541,9 +1387,10 @@ bool SDLPlatform::InitPlatform() return false; } -bool SDLPlatform::InitPlatformX11(void* display) +bool SDLPlatform::InitX11(void* display) { - if (xDisplay || WaylandDisplay) + using namespace X11Impl; + if (xDisplay || WaylandImpl::WaylandDisplay) return false; // The Display instance must be the same one SDL uses internally @@ -1594,7 +1441,7 @@ bool SDLPlatform::InitPlatformX11(void* display) void* SDLPlatform::GetXDisplay() { - return xDisplay; + return X11Impl::xDisplay; } void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable) @@ -1609,179 +1456,178 @@ bool SDLPlatform::UsesWindows() bool SDLPlatform::UsesWayland() { - if (xDisplay == nullptr && WaylandDisplay == nullptr) + if (X11Impl::xDisplay == nullptr && WaylandImpl::WaylandDisplay == nullptr) { // In case the X11 display pointer has not been updated yet return strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; } - return WaylandDisplay != nullptr; + return WaylandImpl::WaylandDisplay != nullptr; } bool SDLPlatform::UsesX11() { - if (xDisplay == nullptr && WaylandDisplay == nullptr) + if (X11Impl::xDisplay == nullptr && WaylandImpl::WaylandDisplay == nullptr) { // In case the X11 display pointer has not been updated yet return strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0; } - return xDisplay != nullptr; + return X11Impl::xDisplay != nullptr; } -void WaylandPointer_Enter(void *data, - struct wl_pointer *wl_pointer, - uint32_t serial, - struct wl_surface *surface, - wl_fixed_t surface_x, - wl_fixed_t surface_y) +DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) { - /*SDLWindow* window; - if (!SurfaceToWindowMap.TryGet(surface, window)) - return; + StringAnsi textAnsi(text); + StringAnsi captionAnsi(caption); - LastPointerWindow = window; - LastPointerPosition = Int2(surface_x, surface_y);*/ - //LOG(Info, "WaylandPointerEnter serial:{}", serial); - //ImplicitGrabSerial = serial; -} + SDL_MessageBoxData data; + SDL_MessageBoxButtonData dataButtons[3]; + data.window = parent ? static_cast(parent)->_window : nullptr; + data.title = captionAnsi.GetText(); + data.message = textAnsi.GetText(); + data.colorScheme = nullptr; -void WaylandPointer_Leave(void *data, - struct wl_pointer *wl_pointer, - uint32_t serial, - struct wl_surface *surface) -{ - //LastPointerWindow = nullptr; - //LOG(Info, "WaylandPointerLeave serial:{}", serial); -} - -void WaylandPointer_Motion(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - wl_fixed_t surface_x, - wl_fixed_t surface_y) -{ - //LOG(Info, "WaylandPointerMotion time:{}", time); - //LastPointerPosition = Int2(surface_x, surface_y); -} - -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:{}, 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 drag process. - if (state == 1) - ImplicitGrabSerial = serial; -} - -void WaylandPointer_Axis(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - uint32_t axis, - wl_fixed_t value) -{ - //LOG(Info, "WaylandPointerAxis time:{}", time); -} - -void WaylandPointer_Frame(void *data, - struct wl_pointer *wl_pointer) -{ - //LOG(Info, "WaylandPointerFrame"); -} - -void WaylandPointer_AxisSource(void *data, - struct wl_pointer *wl_pointer, - uint32_t axis_source) -{ - //LOG(Info, "WaylandPointerAxisSource"); -} - -void WaylandPointer_AxisStop(void *data, - struct wl_pointer *wl_pointer, - uint32_t time, - uint32_t axis) -{ - //LOG(Info, "WaylandPointerAxisStop time:{}", time); -} - -void WaylandPointer_AxisDiscrete(void *data, - struct wl_pointer *wl_pointer, - uint32_t axis, - int32_t discrete) -{ - //LOG(Info, "WaylandPointerAxisDiscrete"); -} - -void WaylandPointer_AxisValue120(void *data, - struct wl_pointer *wl_pointer, - uint32_t axis, - int32_t value120) -{ - //LOG(Info, "WaylandPointerAxisValue120"); -} - -void WaylandPointer_AxisRelativeDirection(void *data, - struct wl_pointer *wl_pointer, - uint32_t axis, - uint32_t direction) -{ - //LOG(Info, "WaylandPointerAxisRelativeDirection"); -} - - -wl_pointer_listener WaylandPointerListener = -{ - WaylandPointer_Enter, - WaylandPointer_Leave, - WaylandPointer_Motion, - WaylandPointer_Button, - WaylandPointer_Axis, - WaylandPointer_Frame, - WaylandPointer_AxisSource, - WaylandPointer_AxisStop, - WaylandPointer_AxisDiscrete, - WaylandPointer_AxisValue120, - WaylandPointer_AxisRelativeDirection -}; - -wl_pointer* WaylandPointer = nullptr; - -void SeatCapabilities(void* data, wl_seat* seat, uint32 capabilities) -{ - if ((capabilities & wl_seat_capability::WL_SEAT_CAPABILITY_POINTER) != 0) + switch (icon) { - WaylandPointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(WaylandPointer, &WaylandPointerListener, nullptr); + case MessageBoxIcon::Error: + case MessageBoxIcon::Hand: + case MessageBoxIcon::Stop: + data.flags |= SDL_MESSAGEBOX_ERROR; + break; + case MessageBoxIcon::Asterisk: + case MessageBoxIcon::Information: + case MessageBoxIcon::Question: + data.flags |= SDL_MESSAGEBOX_INFORMATION; + break; + case MessageBoxIcon::Exclamation: + case MessageBoxIcon::Warning: + data.flags |= SDL_MESSAGEBOX_WARNING; + break; + default: + break; } -} -void SeatName(void* data, wl_seat* seat, const char* name) -{ -} - -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)); - if (interfaceStr == "xdg_toplevel_drag_manager_v1") - DragManager = (xdg_toplevel_drag_manager_v1*)wl_registry_bind(registry, id, &xdg_toplevel_drag_manager_v1_interface, Math::Min(1U, version)); - else if (interfaceStr == "wl_seat") + switch (buttons) { - WaylandSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, Math::Min(9U, version)); - wl_seat_add_listener(WaylandSeat, &SeatListener, nullptr); + case MessageBoxButtons::AbortRetryIgnore: + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::Abort, + "Abort" + }; + dataButtons[1] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + (int)DialogResult::Retry, + "Retry" + }; + dataButtons[2] = + { + 0, + (int)DialogResult::Ignore, + "Ignore" + }; + data.numbuttons = 3; + break; + case MessageBoxButtons::OK: + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::OK, + "OK" + }; + data.numbuttons = 1; + break; + case MessageBoxButtons::OKCancel: + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + (int)DialogResult::OK, + "OK" + }; + dataButtons[1] = + { + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::Cancel, + "Cancel" + }; + data.numbuttons = 2; + break; + case MessageBoxButtons::RetryCancel: + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + (int)DialogResult::Retry, + "Retry" + }; + dataButtons[1] = + { + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::Cancel, + "Cancel" + }; + data.numbuttons = 2; + break; + case MessageBoxButtons::YesNo: + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + (int)DialogResult::Yes, + "Yes" + }; + dataButtons[1] = + { + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::No, + "No" + }; + data.numbuttons = 2; + break; + case MessageBoxButtons::YesNoCancel: + { + dataButtons[0] = + { + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, + (int)DialogResult::Yes, + "Yes" + }; + dataButtons[1] = + { + 0, + (int)DialogResult::No, + "No" + }; + dataButtons[2] = + { + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, + (int)DialogResult::Cancel, + "Cancel" + }; + data.numbuttons = 3; + break; } - else if (interfaceStr == "wl_data_device_manager") - WaylandDataDeviceManager = (wl_data_device_manager*)wl_registry_bind(registry, id, &wl_data_device_manager_interface, Math::Min(3U, version)); - else if (interfaceStr == "xdg_wm_base") - WaylandXdgWmBase = (xdg_wm_base*)wl_registry_bind(registry, id, &xdg_wm_base_interface, Math::Min(6U, version)); - -} + default: + break; + } + data.buttons = dataButtons; -void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id) -{ - LOG(Info, "WaylandRegistryGlobalRemove id:{}", id); + int result = -1; + if (!SDL_ShowMessageBox(&data, &result)) + { +#if PLATFORM_LINUX + // Fallback to native messagebox implementation in case some system fonts are missing + if (SDLPlatform::UsesX11()) + { + LOG(Warning, "Failed to show SDL message box: {0}", String(SDL_GetError())); + return ShowFallback(parent, text, caption, buttons, icon); + } +#endif + LOG(Error, "Failed to show SDL message box: {0}", String(SDL_GetError())); + return DialogResult::Abort; + } + if (result < 0) + return DialogResult::None; + return (DialogResult)result; } #endif diff --git a/Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp b/Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp index e7b3e358e..10bdea912 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp @@ -98,7 +98,7 @@ bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg) #undef GET_WINDOW_WITH_HWND } -bool SDLPlatform::InitPlatform() +bool SDLPlatform::InitInternal() { // Workaround required for handling window dragging events properly SDL_SetWindowsMessageHook(&EventMessageHook, nullptr); @@ -109,6 +109,179 @@ bool SDLPlatform::InitPlatform() return false; } +bool EventFilterCallback(void* userdata, SDL_Event* event) +{ + Window* draggedWindow = *(Window**)userdata; + if (draggedWindow == nullptr) + return true; + + // When the window is being dragged on Windows, the internal message loop is blocking + // the SDL event queue. We need to handle all relevant events in this event watch callback + // to ensure dragging related functionality doesn't break due to engine not getting updated. + // This also happens to fix the engine freezing during the dragging operation. + + SDLWindow* window = SDLWindow::GetWindowFromEvent(*event); + if (event->type == SDL_EVENT_WINDOW_EXPOSED) + { + // The internal timer is sending exposed events every ~16ms + Engine::OnUpdate();//Scripting::Update(); // For docking updates + Engine::OnDraw(); + return false; + } + else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) + { + SDLWindow* window = SDLWindow::GetWindowFromEvent(*event); + if (window) + { + bool result = false; + window->OnLeftButtonHit(WindowHitCodes::Caption, result); + //if (result) + // return false; + window->HandleEvent(*event); + } + + return false; + } + else if (event->type == SDL_EVENT_WINDOW_MOVED) + { + Float2 start = draggedWindowStartPosition; + Float2 newPos = Float2(static_cast(event->window.data1), static_cast(event->window.data2)); + Float2 offset = newPos - start; + Float2 mousePos = draggedWindowMousePosition; + + SDL_Event mouseMovedEvent { 0 }; + mouseMovedEvent.motion.type = SDL_EVENT_MOUSE_MOTION; + mouseMovedEvent.motion.windowID = SDL_GetWindowID(draggedWindow->GetSDLWindow()); + mouseMovedEvent.motion.timestamp = SDL_GetTicksNS(); + mouseMovedEvent.motion.state = SDL_BUTTON_LEFT; + mouseMovedEvent.motion.x = mousePos.X; + mouseMovedEvent.motion.y = mousePos.Y; + if (window) + window->HandleEvent(mouseMovedEvent); + if (window) + window->HandleEvent(*event); + + return false; + } + if (window) + window->HandleEvent(*event); + + return true; +} + +void SDLPlatform::PreHandleEvents() +{ + SDL_AddEventWatch(EventFilterCallback, &draggedWindow); +} + +void SDLPlatform::PostHandleEvents() +{ + SDL_RemoveEventWatch(watch, &draggedWindow); + + // Handle window dragging release here + if (draggedWindow != nullptr) + { + Float2 mousePosition; + auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); + + // Send simulated mouse up event + SDL_Event buttonUpEvent { 0 }; + buttonUpEvent.motion.type = SDL_EVENT_MOUSE_BUTTON_UP; + buttonUpEvent.button.down = false; + buttonUpEvent.motion.windowID = SDL_GetWindowID(draggedWindow->GetSDLWindow()); + buttonUpEvent.motion.timestamp = SDL_GetTicksNS(); + buttonUpEvent.motion.state = SDL_BUTTON_LEFT; + buttonUpEvent.button.clicks = 1; + buttonUpEvent.motion.x = mousePosition.X; + buttonUpEvent.motion.y = mousePosition.Y; + draggedWindow->HandleEvent(buttonUpEvent); + draggedWindow = nullptr; + } +} + +bool SDLWindow::HandleEventInternal(SDL_Event& event) +{ + switch (event.type) + { + case SDL_EVENT_WINDOW_DESTROYED: + { +#if USE_EDITOR + // Disable file dropping + if (_settings.AllowDragAndDrop) + { + const auto result = RevokeDragDrop((HWND)_handle); + if (result != S_OK) + LOG(Warning, "Window drag and drop service error: 0x{0:x}:{1}", result, 2); + } +#endif + break; + } + case SDL_EVENT_WINDOW_MAXIMIZED: + { +#if BORDERLESS_MAXIMIZE_WORKAROUND == 2 + if (SkipMaximizeEventsCount > 0) + { + SkipMaximizeEventsCount--; + return; + } + + if (!_settings.HasBorder && _settings.HasSizingFrame) + { + // Restore the window back to previous state + SDL_RestoreWindow(_window); + + // Remove the resizable flags from borderless windows and maximize the window again + auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE); + style &= ~STYLE_RESIZABLE; + ::SetWindowLong((HWND)_handle, GWL_STYLE, style); + + SDL_MaximizeWindow(_window); + + // Re-enable the resizable borderless flags + style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE; + ::SetWindowLong((HWND)_handle, GWL_STYLE, style); + + // The next SDL_EVENT_WINDOW_RESTORED and SDL_EVENT_WINDOW_MAXIMIZED events should be ignored + SkipMaximizeEventsCount = 2; + } +#endif + break; + } + case SDL_EVENT_WINDOW_RESTORED: + { +#if BORDERLESS_MAXIMIZE_WORKAROUND == 2 + if (SkipMaximizeEventsCount > 0) + { + SkipMaximizeEventsCount--; + return true; + } +#endif + break; + } + case SDL_EVENT_MOUSE_BUTTON_UP: + { + if (draggedWindow != nullptr && draggedWindow->_windowId != event.button.windowID) + { + // Send the button event to dragged window as well + Float2 mousePos = ClientToScreen({ event.button.x, event.button.y }); + Float2 clientPos = draggedWindow->ScreenToClient(mousePos); + + SDL_Event event2 = event; + event2.button.windowID = draggedWindow->_windowId; + event2.button.x = clientPos.X; + event2.button.y = clientPos.Y; + + SDLInput::HandleEvent(draggedWindow, event2); + } + break; + } + default: + break; + } + + return false; +} + bool SDLPlatform::UsesWindows() { return true; diff --git a/Source/Engine/Platform/SDL/SDLPlatform.cpp b/Source/Engine/Platform/SDL/SDLPlatform.cpp index 49f57e5e9..f23b10fcd 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.cpp +++ b/Source/Engine/Platform/SDL/SDLPlatform.cpp @@ -20,23 +20,13 @@ #include #include #include -#include #if PLATFORM_LINUX #include "Engine/Engine/CommandLine.h" -#include "Engine/Platform/MessageBox.h" -#include #endif #define DefaultDPI 96 -Window* draggedWindow = nullptr; -#if PLATFORM_WINDOWS -extern Float2 draggedWindowStartPosition; -extern Float2 draggedWindowMousePosition; -#endif -uint32 SDLPlatform::DraggedWindowId = 0; - namespace { int32 SystemDpi = 96; @@ -83,7 +73,7 @@ bool SDLPlatform::Init() SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1"); - //if (InitPlatform()) + //if (InitInternal()) // return true; if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) @@ -106,7 +96,7 @@ bool SDLPlatform::Init() } SDL_free(locales); - if (InitPlatform()) + if (InitInternal()) return true; if (UsesWindows() || UsesX11()) @@ -143,139 +133,11 @@ void SDLPlatform::LogInfo() LOG(Info, "SDL video driver: {}", String(SDL_GetCurrentVideoDriver())); } -bool SDLPlatform::CheckWindowDragging(Window* window, WindowHitCodes hit) -{ - bool handled = false; - window->OnLeftButtonHit(hit, handled); - if (handled) - DraggedWindowId = window->_windowId; - return handled; -} - void SDLPlatform::Tick() { SDLInput::Update(); -#if false - if (DraggedWindowId != 0) - { - Float2 mousePos; - auto buttons = SDL_GetGlobalMouseState(&mousePos.X, &mousePos.Y); - if (!(buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT))) - { - Window* window = nullptr; - WindowsManager::WindowsLocker.Lock(); - for (int32 i = 0; i < WindowsManager::Windows.Count(); i++) - { - if (WindowsManager::Windows[i]->_windowId == DraggedWindowId) - { - window = WindowsManager::Windows[i]; - break; - } - } - WindowsManager::WindowsLocker.Unlock(); - - if (window != nullptr) - { - int top, left, bottom, right; - SDL_GetWindowBordersSize(window->_window, &top, &left, &bottom, &right); - mousePos += Float2(static_cast(left), static_cast(-top)); - Input::Mouse->OnMouseUp(mousePos, MouseButton::Left, window); - } - DraggedWindowId = 0; - } - else - { -#if PLATFORM_LINUX - String dockHintWindow("DockHint.Window"); - Window* window = nullptr; - WindowsManager::WindowsLocker.Lock(); - for (int32 i = 0; i < WindowsManager::Windows.Count(); i++) - { - if (WindowsManager::Windows[i]->_title.Compare(dockHintWindow) == 0) - //if (WindowsManager::Windows[i]->_windowId == DraggedWindowId) - { - window = WindowsManager::Windows[i]; - break; - } - } - WindowsManager::WindowsLocker.Unlock(); - - if (window != nullptr) - { - int top, left, bottom, right; - SDL_GetWindowBordersSize(window->_window, &top, &left, &bottom, &right); - mousePos += Float2(static_cast(left), static_cast(-top)); - Input::Mouse->OnMouseMove(mousePos, window); - } -#endif - } - } -#endif - -#if PLATFORM_WINDOWS - auto watch = [](void* userdata, SDL_Event* event) -> bool - { - Window* draggedWindow = *(Window**)userdata; - if (draggedWindow == nullptr) - return true; - - // When the window is being dragged on Windows, the internal message loop is blocking - // the SDL event queue. We need to handle all relevant events in this event watch callback - // to ensure dragging related functionality doesn't break due to engine not getting updated. - // This also happens to fix the engine freezing during the dragging operation. - - SDLWindow* window = SDLWindow::GetWindowFromEvent(*event); - if (event->type == SDL_EVENT_WINDOW_EXPOSED) - { - // The internal timer is sending exposed events every ~16ms - Engine::OnUpdate();//Scripting::Update(); // For docking updates - Engine::OnDraw(); - return false; - } - else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) - { - SDLWindow* window = SDLWindow::GetWindowFromEvent(*event); - if (window) - { - bool result = false; - window->OnLeftButtonHit(WindowHitCodes::Caption, result); - //if (result) - // return false; - window->HandleEvent(*event); - } - - return false; - } - else if (event->type == SDL_EVENT_WINDOW_MOVED) - { - Float2 start = draggedWindowStartPosition; - Float2 newPos = Float2(static_cast(event->window.data1), static_cast(event->window.data2)); - Float2 offset = newPos - start; - Float2 mousePos = draggedWindowMousePosition; - - SDL_Event mouseMovedEvent { 0 }; - mouseMovedEvent.motion.type = SDL_EVENT_MOUSE_MOTION; - mouseMovedEvent.motion.windowID = SDL_GetWindowID(draggedWindow->GetSDLWindow()); - mouseMovedEvent.motion.timestamp = SDL_GetTicksNS(); - mouseMovedEvent.motion.state = SDL_BUTTON_LEFT; - mouseMovedEvent.motion.x = mousePos.X; - mouseMovedEvent.motion.y = mousePos.Y; - if (window) - window->HandleEvent(mouseMovedEvent); - if (window) - window->HandleEvent(*event); - - return false; - } - if (window) - window->HandleEvent(*event); - - return true; - }; - - SDL_AddEventWatch(watch, &draggedWindow); -#endif + PreHandleEvents(); SDL_PumpEvents(); SDL_Event events[32]; @@ -288,34 +150,10 @@ void SDLPlatform::Tick() else if (events[i].type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && events[i].type <= SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED) SDLInput::HandleEvent(nullptr, events[i]); else - SDLPlatform::HandleEvent(events[i]); + HandleEvent(events[i]); } -#if PLATFORM_WINDOWS - SDL_RemoveEventWatch(watch, &draggedWindow); -#endif - // Handle Windows and X11 window dragging release - if (draggedWindow != nullptr) - { - Float2 mousePosition; - auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); - bool buttonReleased = (buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) == 0; - if (buttonReleased || UsesWindows()) - { - // Send simulated mouse up event - SDL_Event buttonUpEvent { 0 }; - buttonUpEvent.motion.type = SDL_EVENT_MOUSE_BUTTON_UP; - buttonUpEvent.button.down = false; - buttonUpEvent.motion.windowID = SDL_GetWindowID(draggedWindow->GetSDLWindow()); - buttonUpEvent.motion.timestamp = SDL_GetTicksNS(); - buttonUpEvent.motion.state = SDL_BUTTON_LEFT; - buttonUpEvent.button.clicks = 1; - buttonUpEvent.motion.x = mousePosition.X; - buttonUpEvent.motion.y = mousePosition.Y; - draggedWindow->HandleEvent(buttonUpEvent); - draggedWindow = nullptr; - } - } + PostHandleEvents(); } bool SDLPlatform::HandleEvent(SDL_Event& event) @@ -428,162 +266,4 @@ Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings) return New(settings); } -#if PLATFORM_LINUX -DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) -{ - StringAnsi textAnsi(text); - StringAnsi captionAnsi(caption); - - SDL_MessageBoxData data; - SDL_MessageBoxButtonData dataButtons[3]; - data.window = parent ? static_cast(parent)->_window : nullptr; - data.title = captionAnsi.GetText(); - data.message = textAnsi.GetText(); - data.colorScheme = nullptr; - - switch (icon) - { - case MessageBoxIcon::Error: - case MessageBoxIcon::Hand: - case MessageBoxIcon::Stop: - data.flags |= SDL_MESSAGEBOX_ERROR; - break; - case MessageBoxIcon::Asterisk: - case MessageBoxIcon::Information: - case MessageBoxIcon::Question: - data.flags |= SDL_MESSAGEBOX_INFORMATION; - break; - case MessageBoxIcon::Exclamation: - case MessageBoxIcon::Warning: - data.flags |= SDL_MESSAGEBOX_WARNING; - break; - default: - break; - } - - switch (buttons) - { - case MessageBoxButtons::AbortRetryIgnore: - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::Abort, - "Abort" - }; - dataButtons[1] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, - (int)DialogResult::Retry, - "Retry" - }; - dataButtons[2] = - { - 0, - (int)DialogResult::Ignore, - "Ignore" - }; - data.numbuttons = 3; - break; - case MessageBoxButtons::OK: - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::OK, - "OK" - }; - data.numbuttons = 1; - break; - case MessageBoxButtons::OKCancel: - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, - (int)DialogResult::OK, - "OK" - }; - dataButtons[1] = - { - SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::Cancel, - "Cancel" - }; - data.numbuttons = 2; - break; - case MessageBoxButtons::RetryCancel: - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, - (int)DialogResult::Retry, - "Retry" - }; - dataButtons[1] = - { - SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::Cancel, - "Cancel" - }; - data.numbuttons = 2; - break; - case MessageBoxButtons::YesNo: - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, - (int)DialogResult::Yes, - "Yes" - }; - dataButtons[1] = - { - SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::No, - "No" - }; - data.numbuttons = 2; - break; - case MessageBoxButtons::YesNoCancel: - { - dataButtons[0] = - { - SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, - (int)DialogResult::Yes, - "Yes" - }; - dataButtons[1] = - { - 0, - (int)DialogResult::No, - "No" - }; - dataButtons[2] = - { - SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, - (int)DialogResult::Cancel, - "Cancel" - }; - data.numbuttons = 3; - break; - } - default: - break; - } - data.buttons = dataButtons; - - int result = -1; - if (!SDL_ShowMessageBox(&data, &result)) - { -#if PLATFORM_LINUX - // Fallback to native messagebox implementation in case some system fonts are missing - if (SDLPlatform::UsesX11()) - { - LOG(Warning, "Failed to show SDL message box: {0}", String(SDL_GetError())); - return ShowFallback(parent, text, caption, buttons, icon); - } -#endif - LOG(Error, "Failed to show SDL message box: {0}", String(SDL_GetError())); - return DialogResult::Abort; - } - if (result < 0) - return DialogResult::None; - return (DialogResult)result; -} -#endif - #endif diff --git a/Source/Engine/Platform/SDL/SDLPlatform.h b/Source/Engine/Platform/SDL/SDLPlatform.h index 968258635..ce0207b1a 100644 --- a/Source/Engine/Platform/SDL/SDLPlatform.h +++ b/Source/Engine/Platform/SDL/SDLPlatform.h @@ -7,21 +7,18 @@ #include "Engine/Platform/Base/Enums.h" #if PLATFORM_WINDOWS #include "Engine/Platform/Windows/WindowsPlatform.h" +typedef struct tagMSG MSG; #elif PLATFORM_LINUX #include "Engine/Platform/Linux/LinuxPlatform.h" +union _XEvent; #else #endif class SDLWindow; union SDL_Event; -#if PLATFORM_WINDOWS -typedef struct tagMSG MSG; -#elif PLATFORM_LINUX -union _XEvent; -#endif /// -/// The Windows platform implementation and application management utilities. +/// The SDL platform implementation and application management utilities. /// class FLAXENGINE_API SDLPlatform #if PLATFORM_WINDOWS @@ -38,22 +35,20 @@ class FLAXENGINE_API SDLPlatform friend SDLWindow; private: - static uint32 DraggedWindowId; - -private: - static bool InitPlatform(); + static bool InitInternal(); #if PLATFORM_LINUX - static bool InitPlatformX11(void* display); + static bool InitX11(void* display); #endif static bool HandleEvent(SDL_Event& event); #if PLATFORM_WINDOWS - static bool __cdecl EventMessageHook(void* userdata, MSG* msg); + static bool EventMessageHook(void* userdata, MSG* msg); #elif PLATFORM_LINUX - static bool __cdecl X11EventHook(void *userdata, _XEvent *xevent); + static bool X11EventHook(void* userdata, _XEvent* xevent); #endif + static void PreHandleEvents(); + static void PostHandleEvents(); public: - static bool CheckWindowDragging(Window* window, WindowHitCodes hit); #if PLATFORM_LINUX static void* GetXDisplay(); #endif diff --git a/Source/Engine/Platform/SDL/SDLWindow.cpp b/Source/Engine/Platform/SDL/SDLWindow.cpp index 5a9b075c7..1fc5a939e 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.cpp +++ b/Source/Engine/Platform/SDL/SDLWindow.cpp @@ -37,14 +37,12 @@ #include #include -extern Dictionary SurfaceToWindowMap; extern SDLWindow* LastPointerWindow; extern Int2 LastPointerPosition; 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; extern bool waylandDraggingWindow; extern StringView waylandDraggingData; @@ -74,43 +72,6 @@ bool IsPopupWindow(WindowType type) return type == WindowType::Popup || type == WindowType::Tooltip; } -class SDLDropFilesData : public IGuiData -{ -public: - Array Files; - - Type GetType() const override - { - return Type::Files; - } - String GetAsText() const override - { - return String::Empty; - } - void GetAsFiles(Array* files) const override - { - files->Add(Files); - } -}; - -class SDLDropTextData : public IGuiData -{ -public: - StringView Text; - - Type GetType() const override - { - return Type::Text; - } - String GetAsText() const override - { - return String(Text); - } - void GetAsFiles(Array* files) const override - { - } -}; - SDLWindow::SDLWindow(const CreateWindowSettings& settings) : WindowBase(settings) , _handle(nullptr) @@ -152,8 +113,8 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) flags |= SDL_WINDOW_TRANSPARENT; // Disable parenting of child windows as those are always on top of the parent window and never show up in taskbar - //if (_settings.Parent != nullptr && (_settings.Type != WindowType::Tooltip && _settings.Type != WindowType::Popup)) - // _settings.Parent = nullptr; + if (_settings.Parent != nullptr && (_settings.Type != WindowType::Tooltip && _settings.Type != WindowType::Popup)) + _settings.Parent = nullptr; // The window position needs to be relative to the parent window Int2 relativePosition(Math::TruncToInt(settings.Position.X), Math::TruncToInt(settings.Position.Y)); @@ -212,10 +173,6 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) if (xdndAware != 0) X11::XChangeProperty(xDisplay, (X11::Window)_handle, xdndAware, (X11::Atom)4, 32, PropModeReplace, (unsigned char*)&xdndVersion, 1); } - else - { - // TODO: Wayland - } #endif } #endif @@ -225,7 +182,7 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings) #if PLATFORM_LINUX // Initialize using the shared Display instance from SDL if (SDLPlatform::UsesX11() && SDLPlatform::GetXDisplay() == nullptr) - SDLPlatform::InitPlatformX11(GetX11Display()); + SDLPlatform::InitX11(GetX11Display()); #endif } @@ -287,10 +244,7 @@ SDLWindow::~SDLWindow() if (_window == nullptr) return; - -#if PLATFORM_LINUX - SurfaceToWindowMap.RemoveValue(this); -#endif + SDL_StopTextInput(_window); SDL_DestroyWindow(_window); @@ -391,6 +345,10 @@ void SDLWindow::HandleEvent(SDL_Event& event) if (_isClosing) return; + // Platform specific event handling + if (HandleEventInternal(event)) + return; + switch (event.type) { case SDL_EVENT_WINDOW_CLOSE_REQUESTED: @@ -400,16 +358,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) } case SDL_EVENT_WINDOW_DESTROYED: { -#if USE_EDITOR && PLATFORM_WINDOWS - // Disable file dropping - if (_settings.AllowDragAndDrop) - { - const auto result = RevokeDragDrop((HWND)_handle); - if (result != S_OK) - LOG(Warning, "Window drag and drop service error: 0x{0:x}:{1}", result, 2); - } -#endif - // Quit #if PLATFORM_WINDOWS PostQuitMessage(0); @@ -438,22 +386,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) case SDL_EVENT_WINDOW_MOVED: { _cachedClientRectangle.Location = Float2(static_cast(event.window.data1), static_cast(event.window.data2)); -#if PLATFORM_LINUX - if (SDLPlatform::UsesX11()) - { - // X11 doesn't report any mouse events when mouse is over the caption area, send a simulated event instead... - Float2 mousePosition; - auto buttons = SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); - if ((buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)) != 0 && draggedWindow == nullptr) - { - // TODO: verify mouse position, window focus - bool result = false; - OnLeftButtonHit(WindowHitCodes::Caption, result); - if (result) - draggedWindow = this; - } - } -#endif return; } case SDL_EVENT_WINDOW_HIT_TEST: @@ -471,46 +403,11 @@ void SDLWindow::HandleEvent(SDL_Event& event) _minimized = false; _maximized = true; -#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 2 - if (SkipMaximizeEventsCount > 0) - { - SkipMaximizeEventsCount--; - return; - } - - if (!_settings.HasBorder && _settings.HasSizingFrame) - { - // Restore the window back to previous state - SDL_RestoreWindow(_window); - - // Remove the resizable flags from borderless windows and maximize the window again - auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE); - style &= ~STYLE_RESIZABLE; - ::SetWindowLong((HWND)_handle, GWL_STYLE, style); - - SDL_MaximizeWindow(_window); - - // Re-enable the resizable borderless flags - style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE; - ::SetWindowLong((HWND)_handle, GWL_STYLE, style); - - // The next SDL_EVENT_WINDOW_RESTORED and SDL_EVENT_WINDOW_MAXIMIZED events should be ignored - SkipMaximizeEventsCount = 2; - } -#endif CheckForWindowResize(); return; } case SDL_EVENT_WINDOW_RESTORED: { -#if BORDERLESS_MAXIMIZE_WORKAROUND == 2 - if (SkipMaximizeEventsCount > 0) - { - SkipMaximizeEventsCount--; - return; - } -#endif - if (_maximized) { _maximized = false; @@ -574,173 +471,6 @@ void SDLWindow::HandleEvent(SDL_Event& event) } return; } -#if PLATFORM_LINUX - case SDL_EVENT_CLIPBOARD_UPDATE: - { - LOG(Info, "SDL_EVENT_CLIPBOARD_UPDATE"); - return; - } - case SDL_EVENT_DROP_BEGIN: - case SDL_EVENT_DROP_POSITION: - case SDL_EVENT_DROP_FILE: - case SDL_EVENT_DROP_TEXT: - case SDL_EVENT_DROP_COMPLETE: - { - if (SDLPlatform::UsesWayland()) - { - static Float2 dragStartPosition = Float2::Zero; - - auto dpiScale = GetDpiScale(); - //const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); - const Float2 mousePos = dragStartPosition + Float2(event.drop.x * dpiScale, event.drop.y * dpiScale); - DragDropEffect effect = DragDropEffect::None; - String text(event.drop.data); - - SDLDropTextData dropData; - dropData.Text = text; - - if (event.type == SDL_EVENT_DROP_BEGIN || event.type == SDL_EVENT_DROP_POSITION) - { - // We don't have the drag data during these events... - dropData.Text = waylandDraggingData; - } - - - if (event.type == SDL_EVENT_DROP_BEGIN) - { - dragStartPosition = waylandDraggingWindow ? GetPosition() : Float2::Zero;//Platform::GetMousePosition(); - LOG(Info, "SDL_EVENT_DROP_BEGIN: {}, mousepos: {}", dragStartPosition, mousePos); - OnDragEnter(&dropData, mousePos, effect); - } - else if (event.type == SDL_EVENT_DROP_POSITION) - { - LOG(Info, "SDL_EVENT_DROP_POSITION: {}", mousePos); - Input::Mouse->OnMouseMove(mousePos, this); - OnDragOver(&dropData, mousePos, effect); - } - else if (event.type == SDL_EVENT_DROP_FILE) - OnDragDrop(&dropData, mousePos, effect); - else if (event.type == SDL_EVENT_DROP_TEXT) - OnDragDrop(&dropData, mousePos, effect); - else if (event.type == SDL_EVENT_DROP_COMPLETE) - OnDragLeave(); - /*Focus(); - Float2 mousePosition; - SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y); - mousePosition = ScreenToClient(mousePosition); - - DragDropEffect effect; - SDLDropTextData dropData; - OnDragEnter(&dropData, mousePosition, effect); - OnDragOver(&dropData, mousePosition, effect);*/ - } - break; - } - /*case SDL_EVENT_DROP_POSITION: - { - auto dpiScale = GetDpiScale(); - //const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale}); - const Float2 mousePos(event.drop.x * dpiScale, event.drop.y * dpiScale); - DragDropEffect effect = DragDropEffect::None; - auto daata = event.drop.data; - - SDLDropTextData dropData; - OnDragOver(&dropData, mousePos, effect); - - break; - } - case SDL_EVENT_DROP_FILE: - { - 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); - - - - 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); - - 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 - case SDL_EVENT_WINDOW_MOUSE_LEAVE: - { -#if !PLATFORM_WINDOWS - OnDragLeave(); // Check for release of mouse button too? -#endif - break; - } -//#if PLATFORM_LINUX - case SDL_EVENT_MOUSE_BUTTON_DOWN: - { - LOG(Info, "SDL_EVENT_MOUSE_BUTTON_DOWN"); - break; - } - case SDL_EVENT_MOUSE_BUTTON_UP: - { -#if PLATFORM_LINUX - if (SDLPlatform::UsesWayland() && waylandDraggingActive) - { - LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP, dragging"); - // We are dragging a window, keep the button held down - return; - } - else -#endif - { - LOG(Info, "SDL_EVENT_MOUSE_BUTTON_UP: {}", GetTitle()); -#if PLATFORM_WINDOWS - if (draggedWindow != nullptr && draggedWindow->_windowId != event.button.windowID) - { - // Send the button event to dragged window as well - Float2 mousePos = ClientToScreen({ event.button.x, event.button.y }); - Float2 clientPos = draggedWindow->ScreenToClient(mousePos); - - SDL_Event event2 = event; - event2.button.windowID = draggedWindow->_windowId; - event2.button.x = clientPos.X; - event2.button.y = clientPos.Y; - - SDLInput::HandleEvent(draggedWindow, event2); - } -#endif - } - break; - } -//#endif default: break; } @@ -1266,7 +996,6 @@ void SDLWindow::UpdateCursor() DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow) { // TODO: this needs to be non-blocking in all platforms - LOG(Info, "StartDragging {}", offset); Float2 dragOffset = offset; if (_settings.HasBorder) @@ -1289,38 +1018,6 @@ DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offse #endif { Show(); - - //draggingActive = true; - - /*auto watch = [](void* userdata, SDL_Event* event) -> bool - { - if (event->window.type == SDL_EVENT_WINDOW_EXPOSED) - { - LOG(Info, "exposedo"); - } - else - LOG(Info, "eventy");*/ - /*SDLPlatform::Tick(); - Engine::OnUpdate();//Scripting::Update(); // For docking updates - Engine::OnDraw();*//* - return true; - }; - - SDL_AddEventWatch(watch, nullptr);*/ - - /*while (draggingActive) - { - SDLPlatform::Tick(); - Engine::OnUpdate();//Scripting::Update(); // For docking updates - Engine::OnDraw(); - - Platform::Sleep(1); - }*/ - - //SDL_RemoveEventWatch(watch, nullptr); - - // The mouse up event was ignored earlier, release the button now - //Input::Mouse->OnMouseUp(Platform::GetMousePosition(), MouseButton::Left, this); } return DragDropEffect::None; } diff --git a/Source/Engine/Platform/SDL/SDLWindow.h b/Source/Engine/Platform/SDL/SDLWindow.h index 93785d989..331f99266 100644 --- a/Source/Engine/Platform/SDL/SDLWindow.h +++ b/Source/Engine/Platform/SDL/SDLWindow.h @@ -57,6 +57,7 @@ private: static SDLWindow* GetWindowFromEvent(const SDL_Event& event); static SDLWindow* GetWindowWithSDLWindow(SDL_Window* window); void HandleEvent(SDL_Event& event); + bool HandleEventInternal(SDL_Event& event); void CheckForWindowResize(); void UpdateCursor();