Implement new window dragging system
This commit is contained in:
@@ -429,7 +429,7 @@ public:
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Starts drag and drop operation
|
||||
/// Starts a drag and drop operation.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <returns>The result.</returns>
|
||||
@@ -438,6 +438,18 @@ public:
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts a window drag and drop operation.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
/// <param name="offset">The offset for positioning the window from cursor.</param>
|
||||
/// <param name="dragSourceWindow">The window where dragging started.</param>
|
||||
/// <returns>The result.</returns>
|
||||
API_FUNCTION() virtual DragDropEffect DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
|
||||
{
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the mouse tracking.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,24 +10,45 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#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 "Engine/Engine/Time.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Platform/Base/DragDropHelper.h"
|
||||
#include "Engine/Platform/Unix/UnixFile.h"
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <wayland/xdg-toplevel-drag-v1.h>
|
||||
#include <wayland/xdg-shell.h>
|
||||
|
||||
// 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<wl_surface*, SDLWindow*> 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;
|
||||
|
||||
// X11
|
||||
Delegate<void*> LinuxPlatform::xEventReceived;
|
||||
|
||||
// Missing Wayland features:
|
||||
// - Application icon (xdg-toplevel-icon-v1) https://github.com/libsdl-org/SDL/pull/9584
|
||||
// - Window positioning and position tracking
|
||||
// - Color picker (xdg-desktop-portal?) https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html
|
||||
// -
|
||||
|
||||
@@ -73,6 +94,7 @@ class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
Array<String> Files;
|
||||
SDLWindow* Window;
|
||||
|
||||
Type GetType() const override
|
||||
{
|
||||
@@ -92,6 +114,8 @@ class LinuxDropTextData : public IGuiData
|
||||
{
|
||||
public:
|
||||
StringView Text;
|
||||
SDLWindow* Window;
|
||||
int64* dragOver;
|
||||
|
||||
Type GetType() const override
|
||||
{
|
||||
@@ -254,10 +278,618 @@ DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
return DoDragDropX11(data);
|
||||
}
|
||||
|
||||
DragDropEffect Window::DoDragDropWayland(const StringView& 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)
|
||||
{
|
||||
// TODO: Wayland
|
||||
LOG(Warning, "Wayland Drag and drop is not implemented yet.");
|
||||
// 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<SDLWindow*>(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<IGuiData*>(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<SDLWindow*>(data);
|
||||
//LOG(Info, "WaylandDataSource_Cancelled");
|
||||
|
||||
IGuiData* inputData = static_cast<IGuiData*>(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<SDLWindow*>(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<SDLWindow*>(data);
|
||||
//LOG(Info, "WaylandDataSource_DnDFinished");
|
||||
|
||||
IGuiData* inputData = static_cast<IGuiData*>(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<SDLWindow*>(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->GetSDLWindow();
|
||||
//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
|
||||
// while SDL is dispatching the main Wayland event queue when receiving the data offer from us.
|
||||
|
||||
// Show()?
|
||||
{
|
||||
if (!_visible)
|
||||
{
|
||||
if (_showAfterFirstPaint)
|
||||
{
|
||||
if (RenderTask)
|
||||
RenderTask->Enabled = true;
|
||||
}
|
||||
else
|
||||
SDL_ShowWindow(_window);
|
||||
}
|
||||
WindowBase::Show();
|
||||
}
|
||||
|
||||
//while (true)
|
||||
{
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
|
||||
// Update game logic
|
||||
if (Time::OnBeginUpdate(time))
|
||||
{
|
||||
Engine::OnUpdate();
|
||||
Engine::OnLateUpdate();
|
||||
Time::OnEndUpdate();
|
||||
}
|
||||
|
||||
SDLPlatform::Tick();
|
||||
Engine::OnDraw();
|
||||
|
||||
Platform::Sleep(1);
|
||||
}
|
||||
|
||||
waylandDraggingActive = true;
|
||||
|
||||
auto task = New<WaylandDragDropJob>();
|
||||
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::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();//Scripting::Update(); // 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);
|
||||
}
|
||||
}
|
||||
|
||||
// The mouse up event was ignored earlier, release the button now
|
||||
Input::Mouse->OnMouseUp(Platform::GetMousePosition(), MouseButton::Left, this);
|
||||
|
||||
Platform::AtomicStore(&task->ExitFlag, 1);
|
||||
task->Wait();
|
||||
|
||||
waylandDraggingActive = false;
|
||||
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
@@ -846,8 +1478,58 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
|
||||
bool SDLPlatform::InitPlatform()
|
||||
{
|
||||
if (LinuxPlatform::Init())
|
||||
return true;
|
||||
//if (LinuxPlatform::Init())
|
||||
// return true;
|
||||
|
||||
#if false
|
||||
if (!CommandLine::Options.Headless && strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0)
|
||||
{
|
||||
WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr);
|
||||
|
||||
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);
|
||||
}
|
||||
#else
|
||||
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*)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);
|
||||
|
||||
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);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -913,14 +1595,186 @@ void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
base::SetHighDpiAwarenessEnabled(enable);
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWindows()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
if (xDisplay == nullptr && WaylandDisplay == nullptr)
|
||||
{
|
||||
// In case the X11 display pointer has not been updated yet
|
||||
return strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
|
||||
}
|
||||
return WaylandDisplay != nullptr;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
if (xDisplay == nullptr && WaylandDisplay == nullptr)
|
||||
{
|
||||
// In case the X11 display pointer has not been updated yet
|
||||
return strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0;
|
||||
}
|
||||
return 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)
|
||||
{
|
||||
/*SDLWindow* window;
|
||||
if (!SurfaceToWindowMap.TryGet(surface, window))
|
||||
return;
|
||||
|
||||
LastPointerWindow = window;
|
||||
LastPointerPosition = Int2(surface_x, surface_y);*/
|
||||
//LOG(Info, "WaylandPointerEnter serial:{}", serial);
|
||||
//ImplicitGrabSerial = serial;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
WaylandPointer = wl_seat_get_pointer(seat);
|
||||
wl_pointer_add_listener(WaylandPointer, &WaylandPointerListener, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
WaylandSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, Math::Min(9U, version));
|
||||
wl_seat_add_listener(WaylandSeat, &SeatListener, nullptr);
|
||||
}
|
||||
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));
|
||||
|
||||
}
|
||||
|
||||
void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id)
|
||||
{
|
||||
LOG(Info, "WaylandRegistryGlobalRemove id:{}", id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WINDOWS
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "SDLInput.h"
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
extern Window* draggedWindow;
|
||||
Float2 draggedWindowStartPosition = Float2::Zero;
|
||||
Float2 draggedWindowMousePosition = Float2::Zero;
|
||||
|
||||
// The events for releasing the mouse during window dragging are missing, handle the mouse release event here
|
||||
bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg)
|
||||
@@ -31,22 +39,68 @@ bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg)
|
||||
ASSERT((window) != nullptr); \
|
||||
} while (false)
|
||||
|
||||
if (draggedWindow != nullptr)
|
||||
{
|
||||
LOG(Info, "event hook message: {}", msg->message);
|
||||
}
|
||||
|
||||
if (msg->message == WM_NCLBUTTONDOWN)
|
||||
{
|
||||
Window* window;
|
||||
GET_WINDOW_WITH_HWND(window, msg->hwnd);
|
||||
|
||||
auto hit = static_cast<WindowHitCodes>(msg->wParam);
|
||||
if (SDLPlatform::CheckWindowDragging(window, hit))
|
||||
return false;
|
||||
draggedWindow = window;
|
||||
|
||||
draggedWindowStartPosition = draggedWindow->GetClientPosition();
|
||||
Float2 mousePos(static_cast<float>(static_cast<LONG>(WINDOWS_GET_X_LPARAM(msg->lParam))), static_cast<float>(static_cast<LONG>(WINDOWS_GET_Y_LPARAM(msg->lParam))));
|
||||
draggedWindowMousePosition = mousePos;
|
||||
draggedWindowMousePosition -= draggedWindowStartPosition;
|
||||
|
||||
bool result = false;
|
||||
WindowHitCodes hit = static_cast<WindowHitCodes>(msg->wParam);
|
||||
window->OnHitTest(mousePos, hit, result);
|
||||
//if (result && hit != WindowHitCodes::Caption)
|
||||
// return false;
|
||||
|
||||
if (hit == WindowHitCodes::Caption)
|
||||
{
|
||||
SDL_Event event{ 0 };
|
||||
event.button.type = SDL_EVENT_MOUSE_BUTTON_DOWN;
|
||||
event.button.down = true;
|
||||
event.button.timestamp = SDL_GetTicksNS();
|
||||
event.button.windowID = SDL_GetWindowID(window->GetSDLWindow());
|
||||
event.button.button = SDL_BUTTON_LEFT;
|
||||
event.button.clicks = 1;
|
||||
event.button.x = draggedWindowMousePosition.X;
|
||||
event.button.y = draggedWindowMousePosition.Y;
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
/*else if (msg->message == WM_NCLBUTTONUP || msg->message == WM_CAPTURECHANGED)
|
||||
{
|
||||
windowDragging = false;
|
||||
Window* window;
|
||||
GET_WINDOW_WITH_HWND(window, msg->hwnd);
|
||||
|
||||
SDL_Event event{ 0 };
|
||||
event.button.type = SDL_EVENT_MOUSE_BUTTON_UP;
|
||||
event.button.down = false;
|
||||
event.button.timestamp = SDL_GetTicksNS();
|
||||
event.button.windowID = SDL_GetWindowID(window->GetSDLWindow());
|
||||
event.button.button = SDL_BUTTON_LEFT;
|
||||
event.button.clicks = 1;
|
||||
event.button.x = static_cast<float>(static_cast<LONG>(WINDOWS_GET_X_LPARAM(msg->lParam)));
|
||||
event.button.y = static_cast<float>(static_cast<LONG>(WINDOWS_GET_Y_LPARAM(msg->lParam)));
|
||||
|
||||
SDL_PushEvent(&event);
|
||||
}*/
|
||||
return true;
|
||||
#undef GET_WINDOW_WITH_HWND
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitPlatform()
|
||||
{
|
||||
// Workaround required for handling window dragging events properly for DockHintWindow
|
||||
// Workaround required for handling window dragging events properly
|
||||
SDL_SetWindowsMessageHook(&EventMessageHook, nullptr);
|
||||
|
||||
if (WindowsPlatform::Init())
|
||||
@@ -55,6 +109,21 @@ bool SDLPlatform::InitPlatform()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWindows()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// Other supported values: "permonitor", "permonitorv2"
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Engine/Platform/BatteryInfo.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/SDL/SDLInput.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
@@ -19,6 +20,7 @@
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_version.h>
|
||||
#include <SDL3/SDL_locale.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
@@ -28,6 +30,11 @@
|
||||
|
||||
#define DefaultDPI 96
|
||||
|
||||
Window* draggedWindow = nullptr;
|
||||
#if PLATFORM_WINDOWS
|
||||
extern Float2 draggedWindowStartPosition;
|
||||
extern Float2 draggedWindowMousePosition;
|
||||
#endif
|
||||
uint32 SDLPlatform::DraggedWindowId = 0;
|
||||
|
||||
namespace
|
||||
@@ -69,12 +76,14 @@ bool SDLPlatform::Init()
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_WARP_MOTION, "0");
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // Needed for tracking mode
|
||||
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, "0"); //
|
||||
SDL_SetHint(SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS, "8"); // Reduce the default mouse double-click radius
|
||||
|
||||
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_SCALE_TO_DISPLAY, "1");
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
// Disable SDL clipboard support
|
||||
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false);
|
||||
|
||||
@@ -84,6 +93,10 @@ bool SDLPlatform::Init()
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
|
||||
#endif
|
||||
|
||||
//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())));
|
||||
@@ -104,7 +117,7 @@ bool SDLPlatform::Init()
|
||||
}
|
||||
}
|
||||
SDL_free(locales);
|
||||
|
||||
|
||||
if (InitPlatform())
|
||||
return true;
|
||||
|
||||
@@ -142,6 +155,7 @@ void SDLPlatform::Tick()
|
||||
{
|
||||
SDLInput::Update();
|
||||
|
||||
#if false
|
||||
if (DraggedWindowId != 0)
|
||||
{
|
||||
Float2 mousePos;
|
||||
@@ -196,6 +210,71 @@ void SDLPlatform::Tick()
|
||||
#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<float>(event->window.data1), static_cast<float>(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
|
||||
|
||||
SDL_PumpEvents();
|
||||
SDL_Event events[32];
|
||||
@@ -210,6 +289,32 @@ void SDLPlatform::Tick()
|
||||
else
|
||||
SDLPlatform::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLPlatform::HandleEvent(SDL_Event& event)
|
||||
@@ -264,7 +369,16 @@ void SDLPlatform::OpenUrl(const StringView& url)
|
||||
Float2 SDLPlatform::GetMousePosition()
|
||||
{
|
||||
Float2 pos;
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
if (UsesWayland())
|
||||
{
|
||||
// Wayland doesn't support reporting global mouse position,
|
||||
// use the last known reported position we got from received window events.
|
||||
pos = Input::GetMouseScreenPosition();
|
||||
}
|
||||
else if (UsesX11())
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
else
|
||||
pos = Input::GetMouseScreenPosition();
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -313,20 +427,6 @@ Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
return New<SDLWindow>(settings);
|
||||
}
|
||||
|
||||
#if !PLATFORM_LINUX
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
{
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
#if PLATFORM_LINUX
|
||||
static void* GetXDisplay();
|
||||
#endif
|
||||
static bool UsesWindows();
|
||||
static bool UsesWayland();
|
||||
static bool UsesX11();
|
||||
|
||||
|
||||
@@ -33,8 +33,23 @@
|
||||
#endif
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include <wayland/xdg-toplevel-drag-v1.h>
|
||||
#include <wayland/xdg-shell.h>
|
||||
|
||||
extern Dictionary<wl_surface*, SDLWindow*> 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;
|
||||
#endif
|
||||
|
||||
extern Window* draggedWindow;
|
||||
|
||||
#define DefaultDPI 96
|
||||
|
||||
namespace
|
||||
@@ -52,6 +67,11 @@ void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& pos
|
||||
Int2 GetSDLWindowScreenPosition(const SDLWindow* window);
|
||||
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y);
|
||||
|
||||
bool IsPopupWindow(WindowType type)
|
||||
{
|
||||
return type == WindowType::Popup || type == WindowType::Tooltip;
|
||||
}
|
||||
|
||||
class SDLDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
@@ -214,9 +234,9 @@ void* GetNativeWindowPointer(SDL_Window* window)
|
||||
#if PLATFORM_WINDOWS
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif PLATFORM_LINUX
|
||||
windowPtr = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
if (windowPtr == nullptr)
|
||||
windowPtr = (void*)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
windowPtr = (void*)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
#elif PLATFORM_MAC
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_ANDROID
|
||||
@@ -266,6 +286,9 @@ SDLWindow::~SDLWindow()
|
||||
if (_window == nullptr)
|
||||
return;
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
SurfaceToWindowMap.RemoveValue(this);
|
||||
#endif
|
||||
SDL_StopTextInput(_window);
|
||||
SDL_DestroyWindow(_window);
|
||||
|
||||
@@ -419,8 +442,14 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
// 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)
|
||||
SDLPlatform::CheckWindowDragging(this, WindowHitCodes::Caption);
|
||||
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;
|
||||
@@ -543,10 +572,49 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
}
|
||||
return;
|
||||
}
|
||||
#if false
|
||||
case SDL_EVENT_DROP_BEGIN:
|
||||
#if true
|
||||
case SDL_EVENT_CLIPBOARD_UPDATE:
|
||||
{
|
||||
Focus();
|
||||
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:
|
||||
{
|
||||
//LOG(Info, "SDL_EVENT_DROP_BEGIN");
|
||||
|
||||
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;
|
||||
auto daata = event.drop.data;
|
||||
|
||||
SDLDropTextData dropData;
|
||||
|
||||
|
||||
|
||||
if (event.type == SDL_EVENT_DROP_BEGIN)
|
||||
{
|
||||
dragStartPosition = Platform::GetMousePosition();
|
||||
OnDragEnter(&dropData, mousePos, effect);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_DROP_POSITION)
|
||||
{
|
||||
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);
|
||||
@@ -554,49 +622,68 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
DragDropEffect effect;
|
||||
SDLDropTextData dropData;
|
||||
OnDragEnter(&dropData, mousePosition, effect);
|
||||
OnDragOver(&dropData, mousePosition, effect);
|
||||
return;
|
||||
OnDragOver(&dropData, mousePosition, effect);*/
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_DROP_POSITION:
|
||||
/*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, Float2(static_cast<float>(event.drop.x), static_cast<float>(event.drop.y)), effect);
|
||||
return;
|
||||
OnDragOver(&dropData, mousePos, effect);
|
||||
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_DROP_FILE:
|
||||
{
|
||||
SDLDropFilesData dropData;
|
||||
dropData.Files.Add(StringAnsi(event.drop.data).ToString()); // TODO: collect multiple files at once?
|
||||
|
||||
Focus();
|
||||
|
||||
Float2 mousePosition;
|
||||
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
|
||||
mousePosition = ScreenToClient(mousePosition);
|
||||
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;
|
||||
OnDragDrop(&dropData, mousePosition, effect);
|
||||
auto daata = event.drop.data;
|
||||
|
||||
SDLDropTextData dropData;
|
||||
OnDragDrop(&dropData, mousePos, effect);
|
||||
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_DROP_TEXT:
|
||||
{
|
||||
SDLDropTextData dropData;
|
||||
String str = StringAnsi(event.drop.data).ToString();
|
||||
dropData.Text = StringView(str);
|
||||
|
||||
Focus();
|
||||
Float2 mousePosition;
|
||||
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
|
||||
mousePosition = ScreenToClient(mousePosition);
|
||||
auto dpiScale = GetDpiScale();
|
||||
const Float2 mousePos = ClientToScreen({ event.drop.x * dpiScale, event.drop.y * dpiScale});
|
||||
DragDropEffect effect = DragDropEffect::None;
|
||||
OnDragDrop(&dropData, mousePosition, effect);
|
||||
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:
|
||||
{
|
||||
@@ -605,6 +692,44 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
#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;
|
||||
}
|
||||
@@ -639,9 +764,9 @@ void SDLWindow::Show()
|
||||
else if (_settings.Parent == nullptr)
|
||||
BringToFront();
|
||||
|
||||
// Reused top-most windows (DockHintWindow) doesn't stay on top for some reason
|
||||
if (_settings.IsTopmost && _settings.Type != WindowType::Tooltip)
|
||||
SDL_SetWindowAlwaysOnTop(_window, true);
|
||||
// Reused top-most windows doesn't stay on top for some reason
|
||||
if (_settings.IsTopmost && !IsPopupWindow(_settings.Type))
|
||||
SetIsAlwaysOnTop(true);
|
||||
|
||||
if (_isTrackingMouse)
|
||||
{
|
||||
@@ -662,6 +787,11 @@ void SDLWindow::Hide()
|
||||
|
||||
SDL_HideWindow(_window);
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
//if (SDLPlatform::UsesWayland() && _dragOver)
|
||||
// StopDragging();
|
||||
#endif
|
||||
|
||||
WindowBase::Hide();
|
||||
}
|
||||
|
||||
@@ -760,10 +890,20 @@ bool SDLWindow::IsForegroundWindow() const
|
||||
|
||||
void SDLWindow::BringToFront(bool force)
|
||||
{
|
||||
#if PLATFORM_WINDOWS // FIXME
|
||||
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
|
||||
SDL_RaiseWindow(_window);
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
|
||||
#endif
|
||||
if (SDLPlatform::UsesX11())
|
||||
{
|
||||
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
|
||||
SDL_RaiseWindow(_window);
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
|
||||
SDL_SyncWindow(_window);
|
||||
}
|
||||
}
|
||||
|
||||
void SDLWindow::SetClientBounds(const Rectangle& clientArea)
|
||||
@@ -777,11 +917,6 @@ void SDLWindow::SetClientBounds(const Rectangle& clientArea)
|
||||
SDL_SetWindowSize(_window, newW, newH);
|
||||
}
|
||||
|
||||
bool IsPopupWindow(WindowType type)
|
||||
{
|
||||
return type == WindowType::Popup || type == WindowType::Tooltip;
|
||||
}
|
||||
|
||||
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset)
|
||||
{
|
||||
if (!IsPopupWindow(type))
|
||||
@@ -848,7 +983,7 @@ void SDLWindow::SetIsFullscreen(bool isFullscreen)
|
||||
if (!isFullscreen)
|
||||
{
|
||||
// The window is set to always-on-top for some reason when leaving fullscreen
|
||||
SDL_SetWindowAlwaysOnTop(_window, false);
|
||||
SetIsAlwaysOnTop(false);
|
||||
}
|
||||
|
||||
WindowBase::SetIsFullscreen(isFullscreen);
|
||||
@@ -1116,5 +1251,68 @@ void SDLWindow::UpdateCursor()
|
||||
SDL_SetCursor(Cursors[index]);
|
||||
}
|
||||
|
||||
//bool draggingActive = false;
|
||||
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)
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
if (SDLPlatform::UsesWayland() && dragSourceWindow == this)
|
||||
{
|
||||
// Wayland includes the decorations in the client-space coordinates, adjust the offset for it.
|
||||
// Assume the title decoration is 25px thick...
|
||||
float topOffset = 25.0f;
|
||||
dragOffset += Float2(0.0f, topOffset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
if (SDLPlatform::UsesWayland())
|
||||
DoDragDropWayland(String("notawindow"), dragSourceWindow, dragOffset);
|
||||
else
|
||||
#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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
void UpdateCursor();
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
DragDropEffect DoDragDropWayland(const StringView& data);
|
||||
DragDropEffect DoDragDropWayland(const StringView& data, Window* dragSourceWindow = nullptr, Float2 dragOffset = Float2::Zero);
|
||||
DragDropEffect DoDragDropX11(const StringView& data);
|
||||
#endif
|
||||
|
||||
@@ -104,6 +104,7 @@ public:
|
||||
String GetTitle() const override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow) override;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void StartClippingCursor(const Rectangle& bounds) override;
|
||||
|
||||
Reference in New Issue
Block a user