Implement new window dragging system

This commit is contained in:
2025-01-21 20:14:44 +02:00
parent 14a90d32c2
commit 3646c5a06a
26 changed files with 6081 additions and 1226 deletions

View File

@@ -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>

View File

@@ -1427,11 +1427,7 @@ DragDropEffect Window::DoDragDrop(const StringView& data)
StringAnsi dataAnsi(data);
LinuxDropTextData dropData;
dropData.Text = data;
#if !PLATFORM_SDL
unsigned long mainWindow = _window;
#else
unsigned long mainWindow = (unsigned long)_handle;
#endif
// Begin dragging
auto screen = X11::XDefaultScreen(xDisplay);

View File

@@ -405,19 +405,24 @@ public:
auto windowHandle = static_cast<SDLWindow*>(window)->_window;
if (relativeMode)
{
oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
relativeModeWindow = window;
SDL_GetMouseState(&oldPosition.X, &oldPosition.Y);
if (!SDL_CursorVisible())
{
// Trap the cursor in current location
SDL_Rect clipRect = { (int)oldPosition.X, (int)oldPosition.Y, 1, 1 };
oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
SDL_SetWindowMouseRect(windowHandle, &clipRect);
}
}
else
{
SDL_SetWindowMouseRect(windowHandle, oldScreenRect);
if (relativeModeWindow != window)
{
// FIXME: When floating game window is focused and editor viewport activated, the relative mode gets stuck
return;
}
SDL_SetWindowMouseRect(windowHandle, nullptr);//oldScreenRect);
SDL_WarpMouseInWindow(windowHandle, oldPosition.X, oldPosition.Y);
oldScreenRect = nullptr;
relativeModeWindow = nullptr;
@@ -519,17 +524,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);

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,36 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if PLATFORM_SDL && PLATFORM_WINDOWS
#define BORDERLESS_MAXIMIZE_WORKAROUND 2
#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 "Engine/Engine/Engine.h"
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_system.h>
#include <SDL3/SDL_timer.h>
#if USE_EDITOR
#include <oleidl.h>
#endif
#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
namespace WinImpl
{
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)
@@ -35,18 +55,59 @@ bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg)
{
Window* window;
GET_WINDOW_WITH_HWND(window, msg->hwnd);
auto hit = static_cast<WindowHitCodes>(msg->wParam);
if (SDLPlatform::CheckWindowDragging(window, hit))
return false;
WinImpl::DraggedWindow = window;
WinImpl::DraggedWindowStartPosition = WinImpl::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))));
WinImpl::DraggedWindowMousePosition = mousePos;
WinImpl::DraggedWindowMousePosition -= WinImpl::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 = WinImpl::DraggedWindowMousePosition.X;
event.button.y = WinImpl::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()
bool SDLPlatform::InitInternal()
{
// 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,10 +116,177 @@ bool SDLPlatform::InitPlatform()
return false;
}
bool SDLPlatform::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 = WinImpl::DraggedWindowStartPosition;
Float2 newPos = Float2(static_cast<float>(event->window.data1), static_cast<float>(event->window.data2));
Float2 offset = newPos - start;
Float2 mousePos = WinImpl::DraggedWindowMousePosition;
SDL_Event mouseMovedEvent { 0 };
mouseMovedEvent.motion.type = SDL_EVENT_MOUSE_MOTION;
mouseMovedEvent.motion.windowID = SDL_GetWindowID(WinImpl::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, &WinImpl::DraggedWindow);
}
void SDLPlatform::PostHandleEvents()
{
SDL_RemoveEventWatch(EventFilterCallback, &WinImpl::DraggedWindow);
// Handle window dragging release here
if (WinImpl::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(WinImpl::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;
WinImpl::DraggedWindow->HandleEvent(buttonUpEvent);
WinImpl::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_MOUSE_BUTTON_UP:
{
if (WinImpl::DraggedWindow != nullptr && WinImpl::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 = WinImpl::DraggedWindow->ScreenToClient(mousePos);
SDL_Event event2 = event;
event2.button.windowID = WinImpl::DraggedWindow->_windowId;
event2.button.x = clientPos.X;
event2.button.y = clientPos.Y;
SDLInput::HandleEvent(WinImpl::DraggedWindow, event2);
}
break;
}
default:
break;
}
return false;
}
bool SDLPlatform::UsesWindows()
{
return true;
}
bool SDLPlatform::UsesWayland()
{
return false;
}
bool SDLPlatform::UsesX11()
{
return false;
}
void SDLWindow::Focus()
{
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "1");
// Forcing the window to focus causes issues with opening context menus while window is maximized
//auto forceRaiseWindow = SDL_GetHint(SDL_HINT_FORCE_RAISEWINDOW);
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, "1");
SDL_RaiseWindow(_window);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, forceRaiseWindow);
}
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
{
// Other supported values: "permonitor", "permonitorv2"
SDL_SetHint("SDL_WINDOWS_DPI_AWARENESS", enable ? "system" : "unaware");
}
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
{
Show();
return DragDropEffect::None;
}
#endif

View File

@@ -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>
@@ -22,14 +23,10 @@
#if PLATFORM_LINUX
#include "Engine/Engine/CommandLine.h"
#include "Engine/Platform/MessageBox.h"
#include <SDL3/SDL_messagebox.h>
#endif
#define DefaultDPI 96
uint32 SDLPlatform::DraggedWindowId = 0;
namespace
{
int32 SystemDpi = 96;
@@ -44,7 +41,12 @@ bool SDLPlatform::Init()
else if (CommandLine::Options.Wayland)
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
else
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
{
// Override the X11 preference when running in Wayland session
String waylandDisplayEnv;
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv))
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
}
#endif
#if PLATFORM_LINUX
@@ -69,21 +71,15 @@ 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");
// Disable SDL clipboard support
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false);
// Disable SDL drag and drop support
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
//if (InitInternal())
// 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,10 +100,23 @@ bool SDLPlatform::Init()
}
}
SDL_free(locales);
if (InitPlatform())
if (InitInternal())
return true;
if (UsesWindows() || UsesX11())
{
// Disable SDL clipboard support
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, false);
// Disable SDL drag and drop support
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
}
SDLInput::Init();
SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
@@ -129,74 +138,12 @@ 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 (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<float>(left), static_cast<float>(-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<float>(left), static_cast<float>(-top));
Input::Mouse->OnMouseMove(mousePos, window);
}
#endif
}
}
PreHandleEvents();
SDL_PumpEvents();
SDL_Event events[32];
int count = SDL_PeepEvents(events, SDL_arraysize(events), SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST);
@@ -208,8 +155,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]);
}
PostHandleEvents();
}
bool SDLPlatform::HandleEvent(SDL_Event& event)
@@ -264,7 +213,18 @@ 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();
//if (!SDL_GetGlobalMouseState(&pos.X, &pos.Y))
// LOG(Error, "SDL_GetGlobalMouseState() failed");
}
else if (UsesX11())
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
else
pos = Input::GetMouseScreenPosition();
return pos;
}
@@ -313,176 +273,4 @@ 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)
{
StringAnsi textAnsi(text);
StringAnsi captionAnsi(caption);
SDL_MessageBoxData data;
SDL_MessageBoxButtonData dataButtons[3];
data.window = parent ? static_cast<SDLWindow*>(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

View File

@@ -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
/// <summary>
/// The Windows platform implementation and application management utilities.
/// The SDL platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API SDLPlatform
#if PLATFORM_WINDOWS
@@ -38,25 +35,25 @@ 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);
static bool SDLPlatform::EventFilterCallback(void* userdata, SDL_Event* event);
#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
static bool UsesWindows();
static bool UsesWayland();
static bool UsesX11();

View File

@@ -14,7 +14,6 @@
#include "Engine/Input/Input.h"
#include "Engine/Input/Keyboard.h"
#include "Engine/Input/Mouse.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Platform/WindowsManager.h"
#define NOGDI
@@ -26,8 +25,6 @@
#if PLATFORM_WINDOWS
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#define STYLE_RESIZABLE (WS_THICKFRAME | WS_MAXIMIZEBOX)
#define BORDERLESS_MAXIMIZE_WORKAROUND 2
#if USE_EDITOR
#include <oleidl.h>
#endif
@@ -37,57 +34,44 @@
#define DefaultDPI 96
namespace
namespace WindowImpl
{
SDLWindow* LastEventWindow = nullptr;
static SDL_Cursor* Cursors[SDL_SYSTEM_CURSOR_COUNT] = { nullptr };
#if BORDERLESS_MAXIMIZE_WORKAROUND == 2
int SkipMaximizeEventsCount = 0;
#endif
}
using namespace WindowImpl;
void* GetNativeWindowPointer(SDL_Window* window);
SDL_HitTestResult OnWindowHitTest(SDL_Window* win, const SDL_Point* area, void* data);
void GetRelativeWindowOffset(WindowType type, SDLWindow* parentWindow, Int2& positionOffset);
Int2 GetSDLWindowScreenPosition(const SDLWindow* window);
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y);
void SetSDLWindowScreenPosition(const SDLWindow* window, const Int2 position);
class SDLDropFilesData : public IGuiData
bool IsPopupWindow(WindowType type)
{
public:
Array<String> Files;
return type == WindowType::Popup || type == WindowType::Tooltip;
}
Type GetType() const override
{
return Type::Files;
}
String GetAsText() const override
{
return String::Empty;
}
void GetAsFiles(Array<String>* files) const override
{
files->Add(Files);
}
};
class SDLDropTextData : public IGuiData
void* GetNativeWindowPointer(SDL_Window* window)
{
public:
StringView Text;
Type GetType() const override
{
return Type::Text;
}
String GetAsText() const override
{
return String(Text);
}
void GetAsFiles(Array<String>* files) const override
{
}
};
void* windowPtr;
auto props = SDL_GetWindowProperties(window);
#if PLATFORM_WINDOWS
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
#elif PLATFORM_LINUX
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
if (windowPtr == nullptr)
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
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr);
#elif PLATFORM_IOS
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
#else
static_assert(false, "unsupported platform");
#endif
return windowPtr;
}
SDLWindow::SDLWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
@@ -130,8 +114,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));
@@ -190,10 +174,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
@@ -203,32 +183,10 @@ 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
}
void* GetNativeWindowPointer(SDL_Window* window)
{
void* windowPtr;
auto props = SDL_GetWindowProperties(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);
if (windowPtr == nullptr)
windowPtr = (void*)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
#elif PLATFORM_MAC
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
#elif PLATFORM_ANDROID
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr);
#elif PLATFORM_IOS
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
#else
static_assert(false, "unsupported platform");
#endif
return windowPtr;
}
SDL_Window* SDLWindow::GetSDLWindow() const
{
return _window;
@@ -265,7 +223,7 @@ SDLWindow::~SDLWindow()
if (_window == nullptr)
return;
SDL_StopTextInput(_window);
SDL_DestroyWindow(_window);
@@ -366,6 +324,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:
@@ -375,16 +337,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);
@@ -413,16 +365,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
case SDL_EVENT_WINDOW_MOVED:
{
_cachedClientRectangle.Location = Float2(static_cast<float>(event.window.data1), static_cast<float>(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)
SDLPlatform::CheckWindowDragging(this, WindowHitCodes::Caption);
}
#endif
return;
}
case SDL_EVENT_WINDOW_HIT_TEST:
@@ -440,46 +382,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;
@@ -543,68 +450,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
}
return;
}
#if false
case SDL_EVENT_DROP_BEGIN:
{
Focus();
Float2 mousePosition;
SDL_GetGlobalMouseState(&mousePosition.X, &mousePosition.Y);
mousePosition = ScreenToClient(mousePosition);
DragDropEffect effect;
SDLDropTextData dropData;
OnDragEnter(&dropData, mousePosition, effect);
OnDragOver(&dropData, mousePosition, effect);
return;
}
case SDL_EVENT_DROP_POSITION:
{
DragDropEffect effect = DragDropEffect::None;
SDLDropTextData dropData;
OnDragOver(&dropData, Float2(static_cast<float>(event.drop.x), static_cast<float>(event.drop.y)), effect);
return;
}
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);
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&dropData, mousePosition, 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);
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&dropData, mousePosition, effect);
return;
}
case SDL_EVENT_DROP_COMPLETE:
{
return;
}
#endif
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
{
#if !PLATFORM_WINDOWS
OnDragLeave(); // Check for release of mouse button too?
#endif
break;
}
default:
break;
}
@@ -639,9 +484,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)
{
@@ -678,19 +523,7 @@ void SDLWindow::Maximize()
if (!_settings.AllowMaximize)
return;
#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 1
// Workaround for "SDL_BORDERLESS_RESIZABLE_STYLE" hint not working as expected when maximizing windows
auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE);
style &= ~STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
SDL_MaximizeWindow(_window);
style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
#else
SDL_MaximizeWindow(_window);
#endif
}
void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
@@ -706,45 +539,18 @@ void SDLWindow::SetBorderless(bool isBorderless, bool maximized)
BringToFront();
if (isBorderless)
{
SDL_SetWindowBordered(_window, !isBorderless ? true : false);
if (maximized)
{
Maximize();
}
else
Focus();
}
SDL_SetWindowBordered(_window, !isBorderless ? true : false);
if (maximized)
Maximize();
else
{
SDL_SetWindowBordered(_window, !isBorderless ? true : false);
if (maximized)
{
Maximize();
}
else
Focus();
}
Focus();
CheckForWindowResize();
}
void SDLWindow::Restore()
{
#if PLATFORM_WINDOWS && BORDERLESS_MAXIMIZE_WORKAROUND == 1
// Workaround for "SDL_BORDERLESS_RESIZABLE_STYLE" hint not working as expected when maximizing windows
auto style = ::GetWindowLong((HWND)_handle, GWL_STYLE);
style &= ~STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
SDL_RestoreWindow(_window);
style = ::GetWindowLong((HWND)_handle, GWL_STYLE) | STYLE_RESIZABLE;
::SetWindowLong((HWND)_handle, GWL_STYLE, style);
#else
SDL_RestoreWindow(_window);
#endif
}
bool SDLWindow::IsClosed() const
@@ -760,28 +566,19 @@ bool SDLWindow::IsForegroundWindow() const
void SDLWindow::BringToFront(bool force)
{
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);
}
void SDLWindow::SetClientBounds(const Rectangle& clientArea)
{
int newX = static_cast<int>(clientArea.GetLeft());
int newY = static_cast<int>(clientArea.GetTop());
Int2 newPos = Int2(clientArea.GetTopLeft());
int newW = static_cast<int>(clientArea.GetWidth());
int newH = static_cast<int>(clientArea.GetHeight());
SetSDLWindowScreenPosition(this, newX, newY);
SetSDLWindowScreenPosition(this, newPos);
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))
@@ -812,9 +609,9 @@ Int2 GetSDLWindowScreenPosition(const SDLWindow* window)
return position - relativeOffset;
}
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y)
void SetSDLWindowScreenPosition(const SDLWindow* window, const Int2 position)
{
Int2 relativePosition(x, y);
Int2 relativePosition = position;
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativePosition);
SDL_SetWindowPosition(window->GetSDLWindow(), relativePosition.X, relativePosition.Y);
}
@@ -834,12 +631,12 @@ void SDLWindow::SetPosition(const Float2& position)
screenPosition += Int2(monitorBounds.GetTopLeft());
}
SetSDLWindowScreenPosition(this, screenPosition.X, screenPosition.Y);
SetSDLWindowScreenPosition(this, screenPosition);
}
void SDLWindow::SetClientPosition(const Float2& position)
{
SetSDLWindowScreenPosition(this, static_cast<int>(position.X), static_cast<int>(position.Y));
SetSDLWindowScreenPosition(this, Int2(position));
}
void SDLWindow::SetIsFullscreen(bool isFullscreen)
@@ -848,7 +645,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);
@@ -930,25 +727,15 @@ void SDLWindow::SetOpacity(const float opacity)
LOG(Warning, "SDL_SetWindowOpacity failed: {0}", String(SDL_GetError()));
}
#if !PLATFORM_WINDOWS
void SDLWindow::Focus()
{
#if PLATFORM_WINDOWS
auto activateWhenRaised = SDL_GetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "1");
// Forcing the window to focus causes issues with opening context menus while window is maximized
//auto forceRaiseWindow = SDL_GetHint(SDL_HINT_FORCE_RAISEWINDOW);
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, "1");
SDL_RaiseWindow(_window);
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, activateWhenRaised);
//SDL_SetHint(SDL_HINT_FORCE_RAISEWINDOW, forceRaiseWindow);
#else
SDL_RaiseWindow(_window);
#endif
}
#endif
String SDLWindow::GetTitle() const
{
return String(SDL_GetWindowTitle(_window));

View File

@@ -57,11 +57,12 @@ 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();
#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 +105,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;