Implement SDL platform, windowing and input handling
This commit is contained in:
@@ -95,13 +95,14 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
CommandLine::Options.Std = true;
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
|
||||
if (Platform::Init())
|
||||
{
|
||||
Platform::Fatal(TEXT("Cannot init platform."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -13,10 +15,14 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
namespace
|
||||
{
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool LastGameViewportFocus = false;
|
||||
}
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -104,6 +110,8 @@ void Screen::SetCursorVisible(const bool value)
|
||||
{
|
||||
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
|
||||
}
|
||||
else if (win)
|
||||
win->SetCursor(CursorType::Default);
|
||||
CursorVisible = value;
|
||||
}
|
||||
|
||||
@@ -190,7 +198,11 @@ void ScreenService::Update()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Sync current cursor state in Editor (eg. when viewport focus can change)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
const auto win = Editor::Managed->GetGameWindow(true);
|
||||
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
|
||||
if (gameViewportFocus != LastGameViewportFocus)
|
||||
Screen::SetCursorVisible(CursorVisible);
|
||||
LastGameViewportFocus = gameViewportFocus;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "AndroidVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -17,8 +18,10 @@ void AndroidVulkanPlatform::GetDeviceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void AndroidVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void AndroidVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
ASSERT(window);
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
ASSERT(windowHandle);
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);
|
||||
|
||||
@@ -17,7 +17,7 @@ class AndroidVulkanPlatform : public VulkanPlatformBase
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void GetDeviceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef AndroidVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -192,7 +192,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
|
||||
|
||||
// Create platform-dependent surface
|
||||
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface);
|
||||
VulkanPlatform::CreateSurface(_window, GPUDeviceVulkan::Instance, &_surface);
|
||||
if (_surface == VK_NULL_HANDLE)
|
||||
{
|
||||
LOG(Warning, "Failed to create Vulkan surface.");
|
||||
|
||||
@@ -4,62 +4,62 @@
|
||||
|
||||
#include "LinuxVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
// Contents of vulkan\vulkan_xlib.h inlined here to prevent typename collisions with engine types due to X11 types
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define VK_KHR_xlib_surface 1
|
||||
#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6
|
||||
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
|
||||
typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
|
||||
#define Display X11::Display
|
||||
#define Window X11::Window
|
||||
#define VisualID X11::VisualID
|
||||
#include "vulkan/vulkan_xlib.h"
|
||||
#undef Display
|
||||
#undef Window
|
||||
#undef VisualID
|
||||
|
||||
typedef struct VkXlibSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkXlibSurfaceCreateFlagsKHR flags;
|
||||
X11::Display* dpy;
|
||||
X11::Window window;
|
||||
} VkXlibSurfaceCreateInfoKHR;
|
||||
#include "vulkan/vulkan_wayland.h"
|
||||
|
||||
typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, X11::Display* dpy, X11::VisualID visualID);
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(
|
||||
VkInstance instance,
|
||||
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkSurfaceKHR* pSurface);
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
uint32_t queueFamilyIndex,
|
||||
Display* dpy,
|
||||
VisualID visualID);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
//
|
||||
|
||||
// Export X11 surface extension from volk
|
||||
// Export extension from volk
|
||||
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||
extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
|
||||
|
||||
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void LinuxVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void LinuxVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
surfaceCreateInfo.dpy = (X11::Display*)Platform::GetXDisplay();
|
||||
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
#else
|
||||
SDLWindow* sdlWindow = static_cast<Window*>(window);
|
||||
X11::Window x11Window = (X11::Window)sdlWindow->GetX11WindowHandle();
|
||||
wl_surface* waylandSurface = (wl_surface*)sdlWindow->GetWaylandSurfacePtr();
|
||||
if (waylandSurface != nullptr)
|
||||
{
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.display = (wl_display*)sdlWindow->GetWaylandDisplay();
|
||||
surfaceCreateInfo.surface = waylandSurface;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
else if (x11Window != 0)
|
||||
{
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)sdlWindow->GetX11Display();
|
||||
surfaceCreateInfo.window = x11Window;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ class LinuxVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef LinuxVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "MacVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -12,12 +13,13 @@ void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void MacVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void MacVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
NSWindow* window = (NSWindow*)windowHandle;
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
NSWindow* nswindow = (NSWindow*)windowHandle;
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK);
|
||||
surfaceCreateInfo.pView = (void*)window.contentView;
|
||||
surfaceCreateInfo.pView = (void*)nswindow.contentView;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class MacVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef MacVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -13,8 +14,9 @@ void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void Win32VulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void Win32VulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
||||
|
||||
@@ -17,7 +17,7 @@ class Win32VulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef Win32VulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "iOSVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -13,8 +14,9 @@ void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void iOSVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void iOSVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
// Create surface on a main UI Thread
|
||||
Function<void()> func = [&windowHandle, &instance, &surface]()
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ class iOSVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef iOSVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
#include "SDL/SDLClipboard.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsClipboard.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Linux/LinuxClipboard.h"
|
||||
|
||||
@@ -140,6 +140,9 @@ API_ENUM() enum class ArchitectureType
|
||||
#if !defined(PLATFORM_SWITCH)
|
||||
#define PLATFORM_SWITCH 0
|
||||
#endif
|
||||
#if !defined(PLATFORM_SDL)
|
||||
#define PLATFORM_SDL 0
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsDefines.h"
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace X11
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
}
|
||||
|
||||
// Helper macros
|
||||
|
||||
@@ -93,7 +93,9 @@ X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
Array<KeyboardKeys> KeyCodeMap;
|
||||
Delegate<void*> LinuxPlatform::xEventRecieved;
|
||||
#if !PLATFORM_SDL
|
||||
Delegate<void*> LinuxPlatform::xEventReceived;
|
||||
#endif
|
||||
Window* MouseTrackingWindow = nullptr;
|
||||
|
||||
// Message boxes configuration
|
||||
@@ -364,6 +366,8 @@ static int X11_MessageBoxInitPositions(MessageBoxData* data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
// Create and set up X11 dialog box window
|
||||
static int X11_MessageBoxCreateWindow(MessageBoxData* data)
|
||||
{
|
||||
@@ -847,6 +851,7 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
LOG(Error, "X11 Error: {0}", String(buffer));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32 CalculateDpi()
|
||||
{
|
||||
@@ -1203,17 +1208,18 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct Property
|
||||
/*struct Property
|
||||
{
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
};
|
||||
};*/
|
||||
|
||||
namespace Impl
|
||||
{
|
||||
LinuxKeyboard* Keyboard;
|
||||
LinuxMouse* Mouse;
|
||||
#if !PLATFORM_SDL
|
||||
StringAnsi ClipboardText;
|
||||
|
||||
void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window)
|
||||
@@ -1328,8 +1334,10 @@ namespace Impl
|
||||
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
|
||||
return FindAppWindow(display, child);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
@@ -1367,7 +1375,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return DragDropEffect::None;
|
||||
@@ -1381,13 +1389,18 @@ DragDropEffect LinuxWindow::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);
|
||||
auto rootWindow = X11::XRootWindow(xDisplay, screen);
|
||||
if (X11::XGrabPointer(xDisplay, _window, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
if (X11::XGrabPointer(xDisplay, mainWindow, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
return DragDropEffect::None;
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime);
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, mainWindow, CurrentTime);
|
||||
|
||||
// Process events
|
||||
X11::XEvent event;
|
||||
@@ -1495,7 +1508,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1524,7 +1537,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndEnter;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
|
||||
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
|
||||
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
|
||||
@@ -1559,7 +1572,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndPosition;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = (x << 16) | y;
|
||||
m.data.l[3] = CurrentTime;
|
||||
@@ -1602,7 +1615,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndDrop;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = CurrentTime;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1649,7 +1662,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = _window;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
@@ -1675,7 +1688,7 @@ void LinuxClipboard::SetText(const StringView& text)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return;
|
||||
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
|
||||
@@ -1698,7 +1711,7 @@ String LinuxClipboard::GetText()
|
||||
if (CommandLine::Options.Headless)
|
||||
return String::Empty;
|
||||
String result;
|
||||
auto mainWindow = (LinuxWindow*)Engine::MainWindow;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return result;
|
||||
X11::Window window = (X11::Window)mainWindow->GetNativePtr();
|
||||
@@ -1727,9 +1740,11 @@ Array<String> LinuxClipboard::GetFiles()
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* LinuxPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
return xDisplay;
|
||||
}
|
||||
|
||||
bool LinuxPlatform::CreateMutex(const Char* name)
|
||||
@@ -2117,10 +2132,15 @@ bool LinuxPlatform::Init()
|
||||
Platform::MemoryClear(Cursors, sizeof(Cursors));
|
||||
Platform::MemoryClear(CursorsImg, sizeof(CursorsImg));
|
||||
|
||||
|
||||
// Skip setup if running in headless mode (X11 might not be available on servers)
|
||||
if (CommandLine::Options.Headless)
|
||||
return false;
|
||||
|
||||
#if PLATFORM_SDL
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
X11::XInitThreads();
|
||||
|
||||
xDisplay = X11::XOpenDisplay(nullptr);
|
||||
@@ -2259,7 +2279,7 @@ bool LinuxPlatform::Init()
|
||||
Input::Mouse = Impl::Mouse = New<LinuxMouse>();
|
||||
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
|
||||
LinuxInput::Init();
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2269,6 +2289,7 @@ void LinuxPlatform::BeforeRun()
|
||||
|
||||
void LinuxPlatform::Tick()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
UnixPlatform::Tick();
|
||||
|
||||
LinuxInput::UpdateState();
|
||||
@@ -2285,9 +2306,9 @@ void LinuxPlatform::Tick()
|
||||
continue;
|
||||
|
||||
// External event handling
|
||||
xEventRecieved(&event);
|
||||
xEventReceived(&event);
|
||||
|
||||
LinuxWindow* window;
|
||||
Window* window;
|
||||
switch (event.type)
|
||||
{
|
||||
case ClientMessage:
|
||||
@@ -2619,6 +2640,7 @@ void LinuxPlatform::Tick()
|
||||
}
|
||||
|
||||
//X11::XFlush(xDisplay);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LinuxPlatform::BeforeExit()
|
||||
@@ -2627,6 +2649,7 @@ void LinuxPlatform::BeforeExit()
|
||||
|
||||
void LinuxPlatform::Exit()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
for (int32 i = 0; i < (int32)CursorType::MAX; i++)
|
||||
{
|
||||
if (Cursors[i])
|
||||
@@ -2652,6 +2675,7 @@ void LinuxPlatform::Exit()
|
||||
X11::XCloseDisplay(xDisplay);
|
||||
xDisplay = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 LinuxPlatform::GetDpi()
|
||||
@@ -2872,7 +2896,11 @@ bool LinuxPlatform::SetWorkingDirectory(const String& path)
|
||||
|
||||
Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
return New<SDLWindow>(settings);
|
||||
#else
|
||||
return New<LinuxWindow>(settings);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
/// <summary>
|
||||
/// An event that is fired when an XEvent is received during platform tick.
|
||||
/// </summary>
|
||||
static Delegate<void*> xEventRecieved;
|
||||
static Delegate<void*> xEventReceived;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX && !PLATFORM_SDL
|
||||
|
||||
#include "../Window.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
|
||||
@@ -89,6 +89,19 @@ public class Platform : EngineModule
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (EngineConfiguration.WithSDL(options))
|
||||
{
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Mac:
|
||||
options.PublicDependencies.Add("SDL");
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (options.Target.IsEditor)
|
||||
{
|
||||
// Include platform settings headers
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "Types.h"
|
||||
#include "Defines.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLPlatform.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsPlatform.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPPlatform.h"
|
||||
|
||||
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Base/ClipboardBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// SDL implementation of the clipboard service for Linux platform.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLClipboard : public ClipboardBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [ClipboardBase]
|
||||
static void Clear();
|
||||
static void SetText(const StringView& text);
|
||||
static void SetRawData(const Span<byte>& data);
|
||||
static void SetFiles(const Array<String>& files);
|
||||
static String GetText();
|
||||
static Array<byte> GetRawData();
|
||||
static Array<String> GetFiles();
|
||||
};
|
||||
|
||||
#endif
|
||||
763
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
763
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
@@ -0,0 +1,763 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLInput.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Input/Keyboard.h"
|
||||
#include "Engine/Input/Gamepad.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
|
||||
class SDLMouse;
|
||||
class SDLKeyboard;
|
||||
class SDLGamepad;
|
||||
|
||||
// TODO: Turn these into customizable values
|
||||
#define TRIGGER_THRESHOLD 30
|
||||
#define LEFT_STICK_THRESHOLD 7849
|
||||
#define RIGHT_STICK_THRESHOLD 8689
|
||||
|
||||
namespace SDLInputImpl
|
||||
{
|
||||
SDLMouse* Mouse = nullptr;
|
||||
SDLKeyboard* Keyboard = nullptr;
|
||||
Dictionary<SDL_JoystickID, SDLGamepad*> Gamepads;
|
||||
}
|
||||
|
||||
static const KeyboardKeys SDL_TO_FLAX_KEYS_MAP[] =
|
||||
{
|
||||
KeyboardKeys::None, // SDL_SCANCODE_UNKNOWN
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::A,
|
||||
KeyboardKeys::B,
|
||||
KeyboardKeys::C,
|
||||
KeyboardKeys::D,
|
||||
KeyboardKeys::E,
|
||||
KeyboardKeys::F,
|
||||
KeyboardKeys::G,
|
||||
KeyboardKeys::H,
|
||||
KeyboardKeys::I,
|
||||
KeyboardKeys::J,
|
||||
KeyboardKeys::K,
|
||||
KeyboardKeys::L,
|
||||
KeyboardKeys::M,
|
||||
KeyboardKeys::N,
|
||||
KeyboardKeys::O,
|
||||
KeyboardKeys::P,
|
||||
KeyboardKeys::Q,
|
||||
KeyboardKeys::R,
|
||||
KeyboardKeys::S,
|
||||
KeyboardKeys::T,
|
||||
KeyboardKeys::U,
|
||||
KeyboardKeys::V,
|
||||
KeyboardKeys::W,
|
||||
KeyboardKeys::X,
|
||||
KeyboardKeys::Y,
|
||||
KeyboardKeys::Z, // 29
|
||||
KeyboardKeys::Alpha1,
|
||||
KeyboardKeys::Alpha2,
|
||||
KeyboardKeys::Alpha3,
|
||||
KeyboardKeys::Alpha4,
|
||||
KeyboardKeys::Alpha5,
|
||||
KeyboardKeys::Alpha6,
|
||||
KeyboardKeys::Alpha7,
|
||||
KeyboardKeys::Alpha8,
|
||||
KeyboardKeys::Alpha9,
|
||||
KeyboardKeys::Alpha0, // 39
|
||||
KeyboardKeys::Return,
|
||||
KeyboardKeys::Escape,
|
||||
KeyboardKeys::Backspace,
|
||||
KeyboardKeys::Tab,
|
||||
KeyboardKeys::Spacebar,
|
||||
KeyboardKeys::Minus,
|
||||
KeyboardKeys::None, //KeyboardKeys::Equals, // ?
|
||||
KeyboardKeys::LeftBracket,
|
||||
KeyboardKeys::RightBracket,
|
||||
KeyboardKeys::Backslash, // SDL_SCANCODE_BACKSLASH ?
|
||||
KeyboardKeys::Oem102, // SDL_SCANCODE_NONUSHASH ?
|
||||
KeyboardKeys::Colon, // SDL_SCANCODE_SEMICOLON ?
|
||||
KeyboardKeys::Quote, // SDL_SCANCODE_APOSTROPHE
|
||||
KeyboardKeys::BackQuote, // SDL_SCANCODE_GRAVE
|
||||
KeyboardKeys::Comma,
|
||||
KeyboardKeys::Period,
|
||||
KeyboardKeys::Slash,
|
||||
KeyboardKeys::Capital,
|
||||
KeyboardKeys::F1,
|
||||
KeyboardKeys::F2,
|
||||
KeyboardKeys::F3,
|
||||
KeyboardKeys::F4,
|
||||
KeyboardKeys::F5,
|
||||
KeyboardKeys::F6,
|
||||
KeyboardKeys::F7,
|
||||
KeyboardKeys::F8,
|
||||
KeyboardKeys::F9,
|
||||
KeyboardKeys::F10,
|
||||
KeyboardKeys::F11,
|
||||
KeyboardKeys::F12,
|
||||
KeyboardKeys::PrintScreen,
|
||||
KeyboardKeys::Scroll,
|
||||
KeyboardKeys::Pause,
|
||||
KeyboardKeys::Insert,
|
||||
KeyboardKeys::Home,
|
||||
KeyboardKeys::PageUp,
|
||||
KeyboardKeys::Delete,
|
||||
KeyboardKeys::End,
|
||||
KeyboardKeys::PageDown,
|
||||
KeyboardKeys::ArrowRight,
|
||||
KeyboardKeys::ArrowLeft,
|
||||
KeyboardKeys::ArrowDown,
|
||||
KeyboardKeys::ArrowUp,
|
||||
KeyboardKeys::Numlock,
|
||||
KeyboardKeys::NumpadDivide,
|
||||
KeyboardKeys::NumpadMultiply,
|
||||
KeyboardKeys::NumpadSubtract,
|
||||
KeyboardKeys::NumpadAdd,
|
||||
KeyboardKeys::Return, // SDL_SCANCODE_KP_ENTER ?
|
||||
KeyboardKeys::Numpad1,
|
||||
KeyboardKeys::Numpad2,
|
||||
KeyboardKeys::Numpad3,
|
||||
KeyboardKeys::Numpad4,
|
||||
KeyboardKeys::Numpad5,
|
||||
KeyboardKeys::Numpad6,
|
||||
KeyboardKeys::Numpad7,
|
||||
KeyboardKeys::Numpad8,
|
||||
KeyboardKeys::Numpad9,
|
||||
KeyboardKeys::Numpad0, //98
|
||||
KeyboardKeys::NumpadDecimal, // SDL_SCANCODE_KP_PERIOD
|
||||
KeyboardKeys::Backslash, // SDL_SCANCODE_NONUSBACKSLASH ?
|
||||
KeyboardKeys::Applications,
|
||||
KeyboardKeys::Sleep, // SDL_SCANCODE_POWER ?
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EQUALS ?
|
||||
KeyboardKeys::F13,
|
||||
KeyboardKeys::F14,
|
||||
KeyboardKeys::F15,
|
||||
KeyboardKeys::F16,
|
||||
KeyboardKeys::F17,
|
||||
KeyboardKeys::F18,
|
||||
KeyboardKeys::F19,
|
||||
KeyboardKeys::F20,
|
||||
KeyboardKeys::F21,
|
||||
KeyboardKeys::F22,
|
||||
KeyboardKeys::F23,
|
||||
KeyboardKeys::F24,
|
||||
KeyboardKeys::Execute,
|
||||
KeyboardKeys::Help,
|
||||
KeyboardKeys::LeftMenu, // SDL_SCANCODE_MENU ?
|
||||
KeyboardKeys::Select,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_STOP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AGAIN
|
||||
KeyboardKeys::None, // SDL_SCANCODE_UNDO
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CUT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_COPY
|
||||
KeyboardKeys::None, // SDL_SCANCODE_PASTE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_FIND
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MUTE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_VOLUMEUP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_VOLUMEDOWN
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::NumpadSeparator, // SDL_SCANCODE_KP_COMMA ?
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EQUALSAS400
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL1
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL3
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL4
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL5
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL6
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL7
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL8
|
||||
KeyboardKeys::None, // SDL_SCANCODE_INTERNATIONAL9
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG1
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG3
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG4
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG5
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG6
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG7
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG8
|
||||
KeyboardKeys::None, // SDL_SCANCODE_LANG9
|
||||
KeyboardKeys::None, // SDL_SCANCODE_ALTERASE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SYSREQ
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CANCEL
|
||||
KeyboardKeys::Clear, // SDL_SCANCODE_CLEAR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_PRIOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_RETURN2
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_OUT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_OPER
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CLEARAGAIN
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CRSEL
|
||||
KeyboardKeys::None, // SDL_SCANCODE_EXSEL
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_00
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_000
|
||||
KeyboardKeys::None, // SDL_SCANCODE_THOUSANDSSEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_DECIMALSEPARATOR
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CURRENCYUNIT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CURRENCYSUBUNIT
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LEFTPAREN = 182,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_RIGHTPAREN = 183,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LEFTBRACE = 184,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_RIGHTBRACE = 185,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_TAB = 186,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_BACKSPACE = 187,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_A = 188,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_B = 189,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_C = 190,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_D = 191,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_E = 192,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_F = 193,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_XOR = 194,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_POWER = 195,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_PERCENT = 196,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_LESS = 197,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_GREATER = 198,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_AMPERSAND = 199,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DBLAMPERSAND = 200,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_VERTICALBAR = 201,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DBLVERTICALBAR = 202,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_COLON = 203,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_HASH = 204,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_SPACE = 205,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_AT = 206,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_EXCLAM = 207,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMSTORE = 208,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMRECALL = 209,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMCLEAR = 210,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMADD = 211,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMSUBTRACT = 212,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMMULTIPLY = 213,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_MEMDIVIDE = 214,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_PLUSMINUS = 215,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_CLEAR = 216,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_CLEARENTRY = 217,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_BINARY = 218,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_OCTAL = 219,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_DECIMAL = 220,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_KP_HEXADECIMAL = 221,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::Control, // SDL_SCANCODE_LCTRL = 224,
|
||||
KeyboardKeys::Shift, // SDL_SCANCODE_LSHIFT = 225,
|
||||
KeyboardKeys::Alt, // SDL_SCANCODE_LALT = 226,
|
||||
KeyboardKeys::LeftMenu, // SDL_SCANCODE_LGUI = 227,
|
||||
KeyboardKeys::Control, // SDL_SCANCODE_RCTRL = 228,
|
||||
KeyboardKeys::Shift, // SDL_SCANCODE_RSHIFT = 229,
|
||||
KeyboardKeys::Alt, // SDL_SCANCODE_RALT = 230,
|
||||
KeyboardKeys::RightMenu, // SDL_SCANCODE_RGUI = 231,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::None,
|
||||
KeyboardKeys::Modechange, // SDL_SCANCODE_MODE
|
||||
KeyboardKeys::Sleep, // SDL_SCANCODE_SLEEP
|
||||
KeyboardKeys::None, // SDL_SCANCODE_WAKE
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CHANNEL_INCREMENT = 260,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CHANNEL_DECREMENT = 261,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_PLAY = 262,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_PAUSE = 263,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_RECORD = 264,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_FAST_FORWARD = 265,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_REWIND = 266,
|
||||
KeyboardKeys::MediaNextTrack, // SDL_SCANCODE_MEDIA_NEXT_TRACK = 267,
|
||||
KeyboardKeys::MediaPrevTrack, // SDL_SCANCODE_MEDIA_PREVIOUS_TRACK = 268,
|
||||
KeyboardKeys::MediaStop, // SDL_SCANCODE_MEDIA_STOP = 269,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_EJECT = 270,
|
||||
KeyboardKeys::MediaPlayPause, // SDL_SCANCODE_MEDIA_PLAY_PAUSE = 271,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_MEDIA_SELECT = 272,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_NEW = 273,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_OPEN = 274,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_CLOSE = 275,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_EXIT = 276,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_SAVE = 277,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_PRINT = 278,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_PROPERTIES = 279,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_SEARCH = 280,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_HOME = 281,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_BACK = 282,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_FORWARD = 283,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_STOP = 284,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_REFRESH = 285,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_AC_BOOKMARKS = 286,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SOFTLEFT = 287,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_SOFTRIGHT = 288,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_CALL = 289,
|
||||
KeyboardKeys::None, // SDL_SCANCODE_ENDCALL = 290
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the keyboard device for Windows platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Keyboard" />
|
||||
class SDLKeyboard : public Keyboard
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLKeyboard"/> class.
|
||||
/// </summary>
|
||||
explicit SDLKeyboard()
|
||||
: Keyboard()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the mouse device for SDL platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Mouse" />
|
||||
class SDLMouse : public Mouse
|
||||
{
|
||||
private:
|
||||
Float2 oldPosition;
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLMouse"/> class.
|
||||
/// </summary>
|
||||
explicit SDLMouse()
|
||||
: Mouse()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Float2 GetMousePosition() const
|
||||
{
|
||||
return oldPosition;
|
||||
}
|
||||
|
||||
// [Mouse]
|
||||
void SetMousePosition(const Float2& newPosition) final override
|
||||
{
|
||||
SDL_WarpMouseGlobal(newPosition.X, newPosition.Y);
|
||||
|
||||
OnMouseMoved(newPosition);
|
||||
}
|
||||
|
||||
void SetRelativeMode(bool relativeMode) final override
|
||||
{
|
||||
if (relativeMode == _relativeMode)
|
||||
return;
|
||||
|
||||
if (relativeMode)
|
||||
SDL_GetGlobalMouseState(&oldPosition.X, &oldPosition.Y);
|
||||
|
||||
Mouse::SetRelativeMode(relativeMode);
|
||||
if (SDL_SetRelativeMouseMode(relativeMode ? SDL_TRUE : SDL_FALSE) != 0)
|
||||
LOG(Error, "Failed to set mouse relative mode: {0}", String(SDL_GetError()));
|
||||
|
||||
if (!relativeMode)
|
||||
{
|
||||
SDL_WarpMouseGlobal(oldPosition.X, oldPosition.Y);
|
||||
OnMouseMoved(oldPosition);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the gamepad device for SDL platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Gamepad" />
|
||||
class SDLGamepad : public Gamepad
|
||||
{
|
||||
private:
|
||||
|
||||
SDL_Gamepad* _gamepad;
|
||||
SDL_JoystickID _instanceId;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLGamepad"/> class.
|
||||
/// </summary>
|
||||
/// <param name="userIndex">The joystick.</param>
|
||||
explicit SDLGamepad(SDL_JoystickID instanceId);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SDLGamepad"/> class.
|
||||
/// </summary>
|
||||
~SDLGamepad();
|
||||
|
||||
private:
|
||||
|
||||
SDLGamepad(SDL_Gamepad* gamepad, SDL_JoystickID instanceId);
|
||||
|
||||
public:
|
||||
|
||||
static SDLGamepad* GetGamepadById(SDL_JoystickID id)
|
||||
{
|
||||
SDLGamepad* gamepad = nullptr;
|
||||
SDLInputImpl::Gamepads.TryGet(id, gamepad);
|
||||
return gamepad;
|
||||
}
|
||||
|
||||
SDL_JoystickID GetJoystickInstanceId() const
|
||||
{
|
||||
return _instanceId;
|
||||
}
|
||||
|
||||
void OnAxisMotion(SDL_GamepadAxis axis, int16 value);
|
||||
|
||||
void OnButtonState(SDL_GamepadButton axis, uint8 state);
|
||||
|
||||
// [Gamepad]
|
||||
void SetVibration(const GamepadVibrationState& state) override;
|
||||
|
||||
protected:
|
||||
|
||||
// [Gamepad]
|
||||
bool UpdateState() override;
|
||||
};
|
||||
|
||||
void SDLInput::Init()
|
||||
{
|
||||
Input::Mouse = SDLInputImpl::Mouse = New<SDLMouse>();
|
||||
Input::Keyboard = SDLInputImpl::Keyboard = New<SDLKeyboard>();
|
||||
}
|
||||
|
||||
void SDLInput::Update()
|
||||
{
|
||||
}
|
||||
|
||||
float NormalizeAxisValue(const int16 axisVal)
|
||||
{
|
||||
// Normalize [-32768..32767] -> [-1..1]
|
||||
const float norm = axisVal <= 0 ? 32768.0f : 32767.0f;
|
||||
return float(axisVal) / norm;
|
||||
}
|
||||
|
||||
bool SDLInput::HandleEvent(SDLWindow* window, SDL_Event& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
{
|
||||
const Float2 mousePos = window->ClientToScreen({ event.motion.x, event.motion.y });
|
||||
Input::Mouse->OnMouseMove(mousePos, window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_MOUSE_LEAVE:
|
||||
{
|
||||
Input::Mouse->OnMouseLeave(window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
const Float2 mousePos = window->ClientToScreen({ event.button.x, event.button.y });
|
||||
MouseButton button = MouseButton::None;
|
||||
if (event.button.button == SDL_BUTTON_LEFT)
|
||||
button = MouseButton::Left;
|
||||
else if (event.button.button == SDL_BUTTON_RIGHT)
|
||||
button = MouseButton::Right;
|
||||
else if (event.button.button == SDL_BUTTON_MIDDLE)
|
||||
button = MouseButton::Middle;
|
||||
else if (event.button.button == SDL_BUTTON_X1)
|
||||
button = MouseButton::Extended1;
|
||||
else if (event.button.button == SDL_BUTTON_X2)
|
||||
button = MouseButton::Extended2;
|
||||
|
||||
if (event.button.state == SDL_RELEASED)
|
||||
Input::Mouse->OnMouseUp(mousePos, button, window);
|
||||
// Prevent sending mouse down event when double-clicking
|
||||
else if (event.button.clicks % 2 == 1)
|
||||
Input::Mouse->OnMouseDown(mousePos, button, window);
|
||||
else
|
||||
Input::Mouse->OnMouseDoubleClick(mousePos, button, window);
|
||||
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
{
|
||||
const Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y });
|
||||
const float delta = event.wheel.y;
|
||||
|
||||
Input::Mouse->OnMouseWheel(mousePos, delta, window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
{
|
||||
// TODO: scancode support
|
||||
KeyboardKeys key = SDL_TO_FLAX_KEYS_MAP[event.key.scancode];
|
||||
//event.key.mod
|
||||
if (event.key.state == SDL_RELEASED)
|
||||
Input::Keyboard->OnKeyUp(key, window);
|
||||
else
|
||||
Input::Keyboard->OnKeyDown(key, window);
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_EDITING:
|
||||
{
|
||||
auto edit = event.edit;
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_TEXT_INPUT:
|
||||
{
|
||||
String text(event.text.text);
|
||||
for (int i = 0; i < text.Length(); i++)
|
||||
{
|
||||
Input::Keyboard->OnCharInput(text[i], window);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||
{
|
||||
SDLGamepad* gamepad = SDLGamepad::GetGamepadById(event.gaxis.which);
|
||||
SDL_GamepadAxis axis = (SDL_GamepadAxis)event.gaxis.axis;
|
||||
gamepad->OnAxisMotion(axis, event.gaxis.value);
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_AXIS_MOTION");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||
{
|
||||
SDLGamepad* gamepad = SDLGamepad::GetGamepadById(event.gbutton.which);
|
||||
SDL_GamepadButton button = (SDL_GamepadButton)event.gbutton.button;
|
||||
gamepad->OnButtonState(button, event.gbutton.state);
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_BUTTON_");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED:
|
||||
{
|
||||
Input::Gamepads.Add(New<SDLGamepad>(event.gdevice.which));
|
||||
Input::OnGamepadsChanged();
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_ADDED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||
{
|
||||
for (int i = 0; i < Input::Gamepads.Count(); i++)
|
||||
{
|
||||
SDLGamepad* gamepad = static_cast<SDLGamepad*>(Input::Gamepads[i]);
|
||||
if (gamepad->GetJoystickInstanceId() == event.gdevice.which)
|
||||
{
|
||||
Input::Gamepads[i]->DeleteObject();
|
||||
Input::Gamepads.RemoveAtKeepOrder(i);
|
||||
Input::OnGamepadsChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_REMOVED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMAPPED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_REMAPPED");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_TOUCHPAD_UP");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
|
||||
{
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_SENSOR_UPDATE");
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
|
||||
{
|
||||
auto ev = event.gdevice;
|
||||
LOG(Info, "SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Guid GetGamepadGuid(SDL_JoystickID instanceId)
|
||||
{
|
||||
SDL_GUID joystickGuid = SDL_GetGamepadGUIDForID(instanceId);
|
||||
Guid guid;
|
||||
Platform::MemoryCopy(&guid.Raw, joystickGuid.data, sizeof(uint8) * 16);
|
||||
return guid;
|
||||
}
|
||||
|
||||
SDLGamepad::SDLGamepad(SDL_JoystickID instanceId)
|
||||
: SDLGamepad(SDL_OpenGamepad(instanceId), instanceId)
|
||||
{
|
||||
}
|
||||
|
||||
SDLGamepad::SDLGamepad(SDL_Gamepad* gamepad, SDL_JoystickID instanceId)
|
||||
: Gamepad(GetGamepadGuid(instanceId), String(SDL_GetGamepadName(gamepad)))
|
||||
, _gamepad(gamepad)
|
||||
, _instanceId(instanceId)
|
||||
{
|
||||
SDLInputImpl::Gamepads.Add(_instanceId, this);
|
||||
}
|
||||
|
||||
SDLGamepad::~SDLGamepad()
|
||||
{
|
||||
SDL_CloseGamepad(_gamepad);
|
||||
SDLInputImpl::Gamepads.Remove(_instanceId);
|
||||
}
|
||||
|
||||
void SDLGamepad::SetVibration(const GamepadVibrationState& state)
|
||||
{
|
||||
Gamepad::SetVibration(state);
|
||||
}
|
||||
|
||||
bool SDLGamepad::UpdateState()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLGamepad::OnAxisMotion(SDL_GamepadAxis sdlAxis, int16 value)
|
||||
{
|
||||
GamepadAxis axis;
|
||||
int16 deadzone = 1; // SDL reports -1 for centered axis?
|
||||
float valueNormalized = NormalizeAxisValue(value);
|
||||
switch (sdlAxis)
|
||||
{
|
||||
case SDL_GAMEPAD_AXIS_LEFTX:
|
||||
axis = GamepadAxis::LeftStickX;
|
||||
deadzone = LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickLeft] = value > LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickRight] = value < -LEFT_STICK_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_LEFTY:
|
||||
axis = GamepadAxis::LeftStickY;
|
||||
deadzone = LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickUp] = value < -LEFT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::LeftStickDown] = value > LEFT_STICK_THRESHOLD;
|
||||
valueNormalized = -valueNormalized;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTX:
|
||||
deadzone = RIGHT_STICK_THRESHOLD;
|
||||
axis = GamepadAxis::RightStickX;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickLeft] = value > RIGHT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickRight] = value < -RIGHT_STICK_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHTY:
|
||||
deadzone = RIGHT_STICK_THRESHOLD;
|
||||
axis = GamepadAxis::RightStickY;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickUp] = value < -RIGHT_STICK_THRESHOLD;
|
||||
_state.Buttons[(int32)GamepadButton::RightStickDown] = value > RIGHT_STICK_THRESHOLD;
|
||||
valueNormalized = -valueNormalized;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_LEFT_TRIGGER:
|
||||
deadzone = TRIGGER_THRESHOLD;
|
||||
axis = GamepadAxis::LeftTrigger;
|
||||
_state.Buttons[(int32)GamepadButton::LeftTrigger] = value > TRIGGER_THRESHOLD;
|
||||
break;
|
||||
case SDL_GAMEPAD_AXIS_RIGHT_TRIGGER:
|
||||
deadzone = TRIGGER_THRESHOLD;
|
||||
axis = GamepadAxis::RightTrigger;
|
||||
_state.Buttons[(int32)GamepadButton::RightTrigger] = value > TRIGGER_THRESHOLD;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (value <= deadzone && value >= -deadzone)
|
||||
valueNormalized = 0.0f;
|
||||
_state.Axis[(int32)axis] = valueNormalized;
|
||||
}
|
||||
|
||||
void SDLGamepad::OnButtonState(SDL_GamepadButton sdlButton, uint8 state)
|
||||
{
|
||||
switch (sdlButton)
|
||||
{
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
_state.Buttons[(int32)GamepadButton::A] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_EAST:
|
||||
_state.Buttons[(int32)GamepadButton::B] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_WEST:
|
||||
_state.Buttons[(int32)GamepadButton::X] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_NORTH:
|
||||
_state.Buttons[(int32)GamepadButton::Y] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::LeftShoulder] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::RightShoulder] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_BACK:
|
||||
_state.Buttons[(int32)GamepadButton::Back] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_START:
|
||||
_state.Buttons[(int32)GamepadButton::Start] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::LeftThumb] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::RightThumb] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
_state.Buttons[(int32)GamepadButton::DPadUp] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
_state.Buttons[(int32)GamepadButton::DPadDown] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadLeft] = state == SDL_PRESSED;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadRight] = state == SDL_PRESSED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
/// <summary>
|
||||
/// SDL specific implementation of the input system parts.
|
||||
/// </summary>
|
||||
class SDLInput
|
||||
{
|
||||
public:
|
||||
|
||||
static void Init();
|
||||
static void Update();
|
||||
static bool HandleEvent(SDLWindow* window, SDL_Event& event);
|
||||
};
|
||||
|
||||
#endif
|
||||
930
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
930
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
@@ -0,0 +1,930 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Platform/IGuiData.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Platform/SDL/SDLClipboard.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
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
|
||||
// -
|
||||
|
||||
namespace
|
||||
{
|
||||
bool UseWayland = false;
|
||||
X11::Display* xDisplay = nullptr;
|
||||
X11::XIM IM = nullptr;
|
||||
X11::XIC IC = nullptr;
|
||||
X11::Atom xAtomDeleteWindow;
|
||||
X11::Atom xAtomXdndEnter;
|
||||
X11::Atom xAtomXdndPosition;
|
||||
X11::Atom xAtomXdndLeave;
|
||||
X11::Atom xAtomXdndDrop;
|
||||
X11::Atom xAtomXdndActionCopy;
|
||||
X11::Atom xAtomXdndStatus;
|
||||
X11::Atom xAtomXdndSelection;
|
||||
X11::Atom xAtomXdndFinished;
|
||||
X11::Atom xAtomXdndAware;
|
||||
X11::Atom xAtomWmState;
|
||||
X11::Atom xAtomWmStateHidden;
|
||||
X11::Atom xAtomWmStateMaxVert;
|
||||
X11::Atom xAtomWmStateMaxHorz;
|
||||
X11::Atom xAtomWmWindowOpacity;
|
||||
X11::Atom xAtomWmName;
|
||||
X11::Atom xAtomAtom;
|
||||
X11::Atom xAtomClipboard;
|
||||
X11::Atom xAtomPrimary;
|
||||
X11::Atom xAtomTargets;
|
||||
X11::Atom xAtomText;
|
||||
X11::Atom xAtomString;
|
||||
X11::Atom xAtomUTF8String;
|
||||
X11::Atom xAtomXselData;
|
||||
|
||||
X11::Atom xDnDRequested = 0;
|
||||
X11::Window xDndSourceWindow = 0;
|
||||
DragDropEffect xDndResult;
|
||||
Float2 xDndPos;
|
||||
int32 xDnDVersion = 0;
|
||||
int32 XFixesSelectionNotifyEvent = 0;
|
||||
}
|
||||
|
||||
class LinuxDropFilesData : public IGuiData
|
||||
{
|
||||
public:
|
||||
Array<String> Files;
|
||||
|
||||
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 LinuxDropTextData : public IGuiData
|
||||
{
|
||||
public:
|
||||
StringView Text;
|
||||
|
||||
Type GetType() const override
|
||||
{
|
||||
return Type::Text;
|
||||
}
|
||||
String GetAsText() const override
|
||||
{
|
||||
return String(Text);
|
||||
}
|
||||
void GetAsFiles(Array<String>* files) const override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
};
|
||||
|
||||
namespace Impl
|
||||
{
|
||||
StringAnsi ClipboardText;
|
||||
|
||||
void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window)
|
||||
{
|
||||
X11::Window selectionOwner = X11::XGetSelectionOwner(xDisplay, source);
|
||||
if (selectionOwner == 0)
|
||||
{
|
||||
// No copy owner
|
||||
return;
|
||||
}
|
||||
if (selectionOwner == window)
|
||||
{
|
||||
// Copy/paste from self
|
||||
result.Set(ClipboardText.Get(), ClipboardText.Length());
|
||||
return;
|
||||
}
|
||||
|
||||
// Send event to get data from the owner
|
||||
int format;
|
||||
unsigned long N, size;
|
||||
char* data;
|
||||
X11::Atom target;
|
||||
X11::XEvent event;
|
||||
X11::XConvertSelection(xDisplay, xAtomClipboard, atom, xAtomXselData, window, CurrentTime);
|
||||
X11::XSync(xDisplay, 0);
|
||||
if (X11::XCheckTypedEvent(xDisplay, SelectionNotify, &event))
|
||||
{
|
||||
if (event.xselection.selection != xAtomClipboard)
|
||||
return;
|
||||
if (event.xselection.property)
|
||||
{
|
||||
X11::XGetWindowProperty(event.xselection.display, event.xselection.requestor, event.xselection.property, 0L,(~0L), 0, AnyPropertyType, &target, &format, &size, &N,(unsigned char**)&data);
|
||||
if (target == xAtomUTF8String || target == xAtomString)
|
||||
{
|
||||
// Got text to paste
|
||||
result.Set(data , size);
|
||||
X11::XFree(data);
|
||||
}
|
||||
X11::XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Property ReadProperty(X11::Display* display, X11::Window window, X11::Atom property)
|
||||
{
|
||||
X11::Atom readType = 0;
|
||||
int readFormat = 0;
|
||||
unsigned long nitems = 0;
|
||||
unsigned long readBytes = 0;
|
||||
unsigned char* result = nullptr;
|
||||
int bytesCount = 1024;
|
||||
if (property != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (result != nullptr)
|
||||
X11::XFree(result);
|
||||
XGetWindowProperty(display, window, property, 0, bytesCount, 0, AnyPropertyType, &readType, &readFormat, &nitems, &readBytes, &result);
|
||||
bytesCount *= 2;
|
||||
} while (readBytes != 0);
|
||||
}
|
||||
Property p = { result, readFormat, (int)nitems, readType };
|
||||
return p;
|
||||
}
|
||||
|
||||
static X11::Atom SelectTargetFromList(X11::Display* display, const char* targetType, X11::Atom* list, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
X11::Atom atom = list[i];
|
||||
if (atom != 0 && StringAnsi(XGetAtomName(display, atom)) == targetType)
|
||||
return atom;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static X11::Atom SelectTargetFromAtoms(X11::Display* display, const char* targetType, X11::Atom t1, X11::Atom t2, X11::Atom t3)
|
||||
{
|
||||
if (t1 != 0 && StringAnsi(XGetAtomName(display, t1)) == targetType)
|
||||
return t1;
|
||||
if (t2 != 0 && StringAnsi(XGetAtomName(display, t2)) == targetType)
|
||||
return t2;
|
||||
if (t3 != 0 && StringAnsi(XGetAtomName(display, t3)) == targetType)
|
||||
return t3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static X11::Window FindAppWindow(X11::Display* display, X11::Window w)
|
||||
{
|
||||
int nprops, i = 0;
|
||||
X11::Atom* a;
|
||||
if (w == 0)
|
||||
return 0;
|
||||
a = X11::XListProperties(display, w, &nprops);
|
||||
for (i = 0; i < nprops; i++)
|
||||
{
|
||||
if (a[i] == xAtomXdndAware)
|
||||
break;
|
||||
}
|
||||
if (nprops)
|
||||
X11::XFree(a);
|
||||
if (i != nprops)
|
||||
return w;
|
||||
X11::Window child, wtmp;
|
||||
int tmp;
|
||||
unsigned int utmp;
|
||||
X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
|
||||
return FindAppWindow(display, child);
|
||||
}
|
||||
|
||||
static Float2 GetX11MousePosition()
|
||||
{
|
||||
if (!xDisplay)
|
||||
return Float2::Zero;
|
||||
int32 x = 0, y = 0;
|
||||
uint32 screenCount = (uint32)X11::XScreenCount(xDisplay);
|
||||
for (uint32 i = 0; i < screenCount; i++)
|
||||
{
|
||||
X11::Window outRoot, outChild;
|
||||
int32 childX, childY;
|
||||
uint32 mask;
|
||||
if (X11::XQueryPointer(xDisplay, X11::XRootWindow(xDisplay, i), &outRoot, &outChild, &x, &y, &childX, &childY, &mask))
|
||||
break;
|
||||
}
|
||||
return Float2((float)x, (float)y);
|
||||
}
|
||||
}
|
||||
|
||||
DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return DragDropEffect::None;
|
||||
|
||||
if (UseWayland)
|
||||
return DoDragDropWayland(data);
|
||||
else
|
||||
return DoDragDropX11(data);
|
||||
}
|
||||
|
||||
DragDropEffect Window::DoDragDropWayland(const StringView& data)
|
||||
{
|
||||
// TODO: Wayland
|
||||
ASSERT(false);
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
DragDropEffect Window::DoDragDropX11(const StringView& data)
|
||||
{
|
||||
auto cursorWrong = X11::XCreateFontCursor(xDisplay, 54);
|
||||
auto cursorTransient = X11::XCreateFontCursor(xDisplay, 24);
|
||||
auto cursorGood = X11::XCreateFontCursor(xDisplay, 4);
|
||||
Array<X11::Atom, FixedAllocation<3>> formats;
|
||||
formats.Add(X11::XInternAtom(xDisplay, "text/plain", 0));
|
||||
formats.Add(xAtomText);
|
||||
formats.Add(xAtomString);
|
||||
StringAnsi dataAnsi(data);
|
||||
LinuxDropTextData dropData;
|
||||
dropData.Text = data;
|
||||
#if !PLATFORM_SDL
|
||||
X11::Window mainWindow = _window;
|
||||
#else
|
||||
X11::Window mainWindow = static_cast<X11::Window>(GetX11WindowHandle());
|
||||
#endif
|
||||
|
||||
// Make sure SDL hasn't grabbed the pointer, and force ungrab it
|
||||
XUngrabPointer(xDisplay, CurrentTime);
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
|
||||
|
||||
// Begin dragging
|
||||
auto screen = X11::XDefaultScreen(xDisplay);
|
||||
auto rootWindow = X11::XRootWindow(xDisplay, screen);
|
||||
|
||||
if (X11::XGrabPointer(xDisplay, mainWindow, 1, Button1MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, rootWindow, cursorWrong, CurrentTime) != GrabSuccess)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "1");
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, mainWindow, CurrentTime);
|
||||
|
||||
// Process events
|
||||
X11::XEvent event;
|
||||
enum Status
|
||||
{
|
||||
Unaware,
|
||||
Unreceptive,
|
||||
CanDrop,
|
||||
};
|
||||
int status = Unaware, previousVersion = -1;
|
||||
X11::Window previousWindow = 0;
|
||||
DragDropEffect result = DragDropEffect::None;
|
||||
float lastDraw = Platform::GetTimeSeconds();
|
||||
float startTime = lastDraw;
|
||||
while (true)
|
||||
{
|
||||
X11::XNextEvent(xDisplay, &event);
|
||||
|
||||
if (event.type == SelectionClear)
|
||||
break;
|
||||
if (event.type == SelectionRequest)
|
||||
{
|
||||
// Extract the relavent data
|
||||
X11::Window owner = event.xselectionrequest.owner;
|
||||
X11::Atom selection = event.xselectionrequest.selection;
|
||||
X11::Atom target = event.xselectionrequest.target;
|
||||
X11::Atom property = event.xselectionrequest.property;
|
||||
X11::Window requestor = event.xselectionrequest.requestor;
|
||||
X11::Time timestamp = event.xselectionrequest.time;
|
||||
X11::Display* disp = event.xselection.display;
|
||||
X11::XEvent s;
|
||||
s.xselection.type = SelectionNotify;
|
||||
s.xselection.requestor = requestor;
|
||||
s.xselection.selection = selection;
|
||||
s.xselection.target = target;
|
||||
s.xselection.property = 0;
|
||||
s.xselection.time = timestamp;
|
||||
if (target == xAtomTargets)
|
||||
{
|
||||
Array<X11::Atom> targets;
|
||||
targets.Add(target);
|
||||
targets.Add(X11::XInternAtom(disp, "MULTIPLE", 0));
|
||||
targets.Add(formats.Get(), formats.Count());
|
||||
X11::XChangeProperty(disp, requestor, property, xAtomAtom, 32, PropModeReplace, (unsigned char*)targets.Get(), targets.Count());
|
||||
s.xselection.property = property;
|
||||
}
|
||||
else if (formats.Contains(target))
|
||||
{
|
||||
s.xselection.property = property;
|
||||
X11::XChangeProperty(disp, requestor, property, target, 8, PropModeReplace, reinterpret_cast<const unsigned char*>(dataAnsi.Get()), dataAnsi.Length());
|
||||
}
|
||||
X11::XSendEvent(event.xselection.display, event.xselectionrequest.requestor, 1, 0, &s);
|
||||
}
|
||||
else if (event.type == MotionNotify)
|
||||
{
|
||||
// Find window under mouse
|
||||
auto window = Impl::FindAppWindow(xDisplay, rootWindow);
|
||||
int fmt, version = -1;
|
||||
X11::Atom atmp;
|
||||
unsigned long nitems, bytesLeft;
|
||||
unsigned char* data = nullptr;
|
||||
if (window == previousWindow)
|
||||
version = previousVersion;
|
||||
else if(window == 0)
|
||||
;
|
||||
else if (X11::XGetWindowProperty(xDisplay, window, xAtomXdndAware, 0, 2, 0, AnyPropertyType, &atmp, &fmt, &nitems, &bytesLeft, &data) != Success)
|
||||
continue;
|
||||
else if (data == 0)
|
||||
continue;
|
||||
else if (fmt != 32)
|
||||
continue;
|
||||
else if (nitems != 1)
|
||||
continue;
|
||||
else
|
||||
version = data[0];
|
||||
if (status == Unaware && version != -1)
|
||||
status = Unreceptive;
|
||||
else if(version == -1)
|
||||
status = Unaware;
|
||||
xDndPos = Float2((float)event.xmotion.x_root, (float)event.xmotion.y_root);
|
||||
|
||||
// Update mouse grab
|
||||
if (status == Unaware)
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorWrong, CurrentTime);
|
||||
else if(status == Unreceptive)
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorTransient, CurrentTime);
|
||||
else
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, cursorGood, CurrentTime);
|
||||
|
||||
if (window != previousWindow && previousVersion != -1)
|
||||
{
|
||||
// Send drag left event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
|
||||
if (ww)
|
||||
{
|
||||
ww->_dragOver = false;
|
||||
ww->OnDragLeave();
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = 0;
|
||||
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
if (window != previousWindow && version != -1)
|
||||
{
|
||||
// Send drag enter event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)window);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition());
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragEnter(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndEnter;
|
||||
m.format = 32;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
|
||||
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
|
||||
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
|
||||
m.data.l[4] = formats.Count() > 2 ? formats[2] : 0;
|
||||
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
if (version != -1)
|
||||
{
|
||||
// Send position event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)window);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition());
|
||||
ww->_dragOver = true;
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragOver(&dropData, xDndPos, xDndResult);
|
||||
status = CanDrop;
|
||||
}
|
||||
else
|
||||
{
|
||||
int x, y, tmp;
|
||||
unsigned int utmp;
|
||||
X11::Window wtmp;
|
||||
X11::XQueryPointer(xDisplay, window, &wtmp, &wtmp, &tmp, &tmp, &x, &y, &utmp);
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = window;
|
||||
m.message_type = xAtomXdndPosition;
|
||||
m.format = 32;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = (x << 16) | y;
|
||||
m.data.l[3] = CurrentTime;
|
||||
m.data.l[4] = xAtomXdndActionCopy;
|
||||
X11::XSendEvent(xDisplay, window, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
previousWindow = window;
|
||||
previousVersion = version;
|
||||
}
|
||||
else if (event.type == ClientMessage && event.xclient.message_type == xAtomXdndStatus)
|
||||
{
|
||||
if ((event.xclient.data.l[1]&1) && status != Unaware)
|
||||
status = CanDrop;
|
||||
if (!(event.xclient.data.l[1]&1) && status != Unaware)
|
||||
status = Unreceptive;
|
||||
}
|
||||
else if (event.type == ButtonRelease && event.xbutton.button == Button1)
|
||||
{
|
||||
if (status == CanDrop)
|
||||
{
|
||||
// Send drop event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
|
||||
if (ww)
|
||||
{
|
||||
xDndPos = ww->ScreenToClient(Impl::GetX11MousePosition());
|
||||
xDndResult = DragDropEffect::None;
|
||||
ww->OnDragDrop(&dropData, xDndPos, xDndResult);
|
||||
ww->Focus();
|
||||
result = xDndResult;
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndDrop;
|
||||
m.format = 32;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = CurrentTime;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = 0;
|
||||
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
result = DragDropEffect::Copy;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Redraw
|
||||
const float time = Platform::GetTimeSeconds();
|
||||
if (time - lastDraw >= 1.0f / 20.0f)
|
||||
{
|
||||
lastDraw = time;
|
||||
|
||||
Engine::OnDraw();
|
||||
}
|
||||
|
||||
// Prevent dead-loop
|
||||
if (time - startTime >= 10.0f)
|
||||
{
|
||||
LOG(Warning, "DoDragDrop timed out after 10 seconds.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Drag end
|
||||
if (previousWindow != 0 && previousVersion != -1)
|
||||
{
|
||||
// Send drag left event
|
||||
auto ww = WindowsManager::GetByNativePtr((void*)previousWindow);
|
||||
if (ww)
|
||||
{
|
||||
ww->_dragOver = false;
|
||||
ww->OnDragLeave();
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = previousWindow;
|
||||
m.message_type = xAtomXdndLeave;
|
||||
m.format = 32;
|
||||
m.data.l[0] = mainWindow;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = 0;
|
||||
X11::XSendEvent(xDisplay, previousWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
}
|
||||
}
|
||||
|
||||
// End grabbing
|
||||
X11::XChangeActivePointerGrab(xDisplay, Button1MotionMask | ButtonReleaseMask, 0, CurrentTime);
|
||||
XUngrabPointer(xDisplay, CurrentTime);
|
||||
X11::XFlush(xDisplay);
|
||||
|
||||
SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "1");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SDLClipboard::Clear()
|
||||
{
|
||||
SetText(StringView::Empty);
|
||||
}
|
||||
|
||||
void SDLClipboard::SetText(const StringView& text)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return;
|
||||
|
||||
if (xDisplay)
|
||||
{
|
||||
X11::Window window = (X11::Window)(mainWindow->GetX11WindowHandle());
|
||||
Impl::ClipboardText.Set(text.Get(), text.Length());
|
||||
X11::XSetSelectionOwner(xDisplay, xAtomClipboard, window, CurrentTime); // CLIPBOARD
|
||||
//X11::XSetSelectionOwner(xDisplay, xAtomPrimary, window, CurrentTime); // XA_PRIMARY
|
||||
X11::XFlush(xDisplay);
|
||||
X11::XGetSelectionOwner(xDisplay, xAtomClipboard);
|
||||
//X11::XGetSelectionOwner(xDisplay, xAtomPrimary);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(false); // TODO: Wayland
|
||||
}
|
||||
}
|
||||
|
||||
void SDLClipboard::SetRawData(const Span<byte>& data)
|
||||
{
|
||||
}
|
||||
|
||||
void SDLClipboard::SetFiles(const Array<String>& files)
|
||||
{
|
||||
}
|
||||
|
||||
String SDLClipboard::GetText()
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return String::Empty;
|
||||
String result;
|
||||
auto mainWindow = Engine::MainWindow;
|
||||
if (!mainWindow)
|
||||
return result;
|
||||
if (xDisplay)
|
||||
{
|
||||
X11::Window window = (X11::Window)mainWindow->GetX11WindowHandle();
|
||||
|
||||
Impl::ClipboardGetText(result, xAtomClipboard, xAtomUTF8String, window);
|
||||
if (result.HasChars())
|
||||
return result;
|
||||
Impl::ClipboardGetText(result, xAtomClipboard, xAtomString, window);
|
||||
if (result.HasChars())
|
||||
return result;
|
||||
Impl::ClipboardGetText(result, xAtomPrimary, xAtomUTF8String, window);
|
||||
if (result.HasChars())
|
||||
return result;
|
||||
Impl::ClipboardGetText(result, xAtomPrimary, xAtomString, window);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(false); // TODO: Wayland
|
||||
}
|
||||
}
|
||||
|
||||
Array<byte> SDLClipboard::GetRawData()
|
||||
{
|
||||
return Array<byte>();
|
||||
}
|
||||
|
||||
Array<String> SDLClipboard::GetFiles()
|
||||
{
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
SDL_bool SDLCALL SDLPlatform::X11EventHook(void *userdata, _XEvent *xevent)
|
||||
{
|
||||
const X11::XEvent& event = *(X11::XEvent*)xevent;
|
||||
Window* window;
|
||||
|
||||
// External event handling
|
||||
xEventReceived(xevent);
|
||||
|
||||
if (event.type == ClientMessage)
|
||||
{
|
||||
if ((uint32)event.xclient.message_type == (uint32)xAtomXdndEnter)
|
||||
{
|
||||
// Drag&drop enter
|
||||
X11::Window source = event.xclient.data.l[0];
|
||||
xDnDVersion = (int32)(event.xclient.data.l[1] >> 24);
|
||||
const char* targetTypeFiles = "text/uri-list";
|
||||
if (event.xclient.data.l[1] & 1)
|
||||
{
|
||||
Property p = Impl::ReadProperty(xDisplay, source, XInternAtom(xDisplay, "XdndTypeList", 0));
|
||||
xDnDRequested = Impl::SelectTargetFromList(xDisplay, targetTypeFiles, (X11::Atom*)p.data, p.nitems);
|
||||
X11::XFree(p.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
xDnDRequested = Impl::SelectTargetFromAtoms(xDisplay, targetTypeFiles, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndPosition)
|
||||
{
|
||||
// Drag&drop move
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = event.xclient.data.l[0];
|
||||
m.message_type = xAtomXdndStatus;
|
||||
m.format = 32;
|
||||
m.data.l[0] = event.xany.window;
|
||||
m.data.l[1] = (xDnDRequested != 0);
|
||||
m.data.l[2] = 0;
|
||||
m.data.l[3] = 0;
|
||||
m.data.l[4] = xAtomXdndActionCopy;
|
||||
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
|
||||
X11::XFlush(xDisplay);
|
||||
xDndPos = Float2((float)(event.xclient.data.l[2] >> 16), (float)(event.xclient.data.l[2] & 0xffff));
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window)
|
||||
{
|
||||
LinuxDropFilesData dropData;
|
||||
xDndResult = DragDropEffect::None;
|
||||
if (window->_dragOver)
|
||||
{
|
||||
window->OnDragOver(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->_dragOver = true;
|
||||
window->OnDragEnter(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndLeave)
|
||||
{
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window && window->_dragOver)
|
||||
{
|
||||
window->_dragOver = false;
|
||||
window->OnDragLeave();
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
else if ((uint32)event.xclient.message_type == (uint32)xAtomXdndDrop)
|
||||
{
|
||||
auto w = event.xany.window;
|
||||
if (xDnDRequested != 0)
|
||||
{
|
||||
xDndSourceWindow = event.xclient.data.l[0];
|
||||
if (xDnDVersion >= 1)
|
||||
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, xAtomPrimary, w, event.xclient.data.l[2]);
|
||||
else
|
||||
XConvertSelection(xDisplay, xAtomXdndSelection, xDnDRequested, xAtomPrimary, w, CurrentTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = event.xclient.display;
|
||||
m.window = event.xclient.data.l[0];
|
||||
m.message_type = xAtomXdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = w;
|
||||
m.data.l[1] = 0;
|
||||
m.data.l[2] = 0;
|
||||
X11::XSendEvent(xDisplay, event.xclient.data.l[0], 0, NoEventMask, (X11::XEvent*)&m);
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
}
|
||||
else if (event.type == SelectionNotify)
|
||||
{
|
||||
if (event.xselection.target == xDnDRequested)
|
||||
{
|
||||
// Drag&drop
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xany.window);
|
||||
if (window)
|
||||
{
|
||||
Property p = Impl::ReadProperty(xDisplay, event.xany.window, xAtomPrimary);
|
||||
if (xDndResult != DragDropEffect::None)
|
||||
{
|
||||
LinuxDropFilesData dropData;
|
||||
const String filesList((const char*)p.data);
|
||||
filesList.Split('\n', dropData.Files);
|
||||
for (auto& e : dropData.Files)
|
||||
{
|
||||
e.Replace(TEXT("file://"), TEXT(""));
|
||||
e.Replace(TEXT("%20"), TEXT(" "));
|
||||
e = e.TrimTrailing();
|
||||
}
|
||||
xDndResult = DragDropEffect::None;
|
||||
window->OnDragDrop(&dropData, xDndPos, xDndResult);
|
||||
}
|
||||
}
|
||||
X11::XClientMessageEvent m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.type = ClientMessage;
|
||||
m.display = xDisplay;
|
||||
m.window = xDndSourceWindow;
|
||||
m.message_type = xAtomXdndFinished;
|
||||
m.format = 32;
|
||||
m.data.l[0] = event.xany.window;
|
||||
m.data.l[1] = 1;
|
||||
m.data.l[2] = xAtomXdndActionCopy;
|
||||
XSendEvent(xDisplay, xDndSourceWindow, 0, NoEventMask, (X11::XEvent*)&m);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
else if (event.type == SelectionRequest)
|
||||
{
|
||||
if (event.xselectionrequest.selection != xAtomClipboard)
|
||||
return SDL_FALSE;
|
||||
|
||||
const X11::XSelectionRequestEvent* xsr = &event.xselectionrequest;
|
||||
X11::XSelectionEvent ev = { 0 };
|
||||
ev.type = SelectionNotify;
|
||||
ev.display = xsr->display;
|
||||
ev.requestor = xsr->requestor;
|
||||
ev.selection = xsr->selection;
|
||||
ev.time = xsr->time;
|
||||
ev.target = xsr->target;
|
||||
ev.property = xsr->property;
|
||||
|
||||
int result = 0;
|
||||
if (ev.target == xAtomTargets)
|
||||
{
|
||||
Array<X11::Atom, FixedAllocation<2>> types(2);
|
||||
types.Add(xAtomTargets);
|
||||
types.Add(xAtomUTF8String);
|
||||
result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomAtom, 32, PropModeReplace, (unsigned char*)types.Get(), types.Count());
|
||||
}
|
||||
else if (ev.target == xAtomString || ev.target == xAtomText)
|
||||
result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomString, 8, PropModeReplace, (unsigned char*)Impl::ClipboardText.Get(), Impl::ClipboardText.Length());
|
||||
else if (ev.target == xAtomUTF8String)
|
||||
result = X11::XChangeProperty(xDisplay, ev.requestor, ev.property, xAtomUTF8String, 8, PropModeReplace, (unsigned char*)Impl::ClipboardText.Get(), Impl::ClipboardText.Length());
|
||||
else
|
||||
ev.property = 0;
|
||||
if ((result & 2) == 0)
|
||||
X11::XSendEvent(xDisplay, ev.requestor, 0, 0, (X11::XEvent*)&ev);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
else if (event.type == SelectionClear)
|
||||
return SDL_FALSE;
|
||||
else if (event.type == XFixesSelectionNotifyEvent)
|
||||
return SDL_FALSE;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
{
|
||||
if (event->error_code == 5)
|
||||
return 0; // BadAtom (invalid Atom parameter)
|
||||
char buffer[256];
|
||||
XGetErrorText(display, event->error_code, buffer, sizeof(buffer));
|
||||
LOG(Error, "X11 Error: {0}", String(buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitPlatform()
|
||||
{
|
||||
if (LinuxPlatform::Init())
|
||||
return true;
|
||||
|
||||
if (!CommandLine::Options.Headless)
|
||||
UseWayland = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitPlatformX11(void* display)
|
||||
{
|
||||
if (xDisplay || UseWayland)
|
||||
return false;
|
||||
|
||||
// The Display instance must be the same one SDL uses internally
|
||||
xDisplay = (X11::Display*)display;
|
||||
SDL_SetX11EventHook((SDL_X11EventHook)&X11EventHook, nullptr);
|
||||
X11::XSetErrorHandler(X11ErrorHandler);
|
||||
|
||||
//xDisplay = X11::XOpenDisplay(nullptr);
|
||||
xAtomDeleteWindow = X11::XInternAtom(xDisplay, "WM_DELETE_WINDOW", 0);
|
||||
xAtomXdndEnter = X11::XInternAtom(xDisplay, "XdndEnter", 0);
|
||||
xAtomXdndPosition = X11::XInternAtom(xDisplay, "XdndPosition", 0);
|
||||
xAtomXdndLeave = X11::XInternAtom(xDisplay, "XdndLeave", 0);
|
||||
xAtomXdndDrop = X11::XInternAtom(xDisplay, "XdndDrop", 0);
|
||||
xAtomXdndActionCopy = X11::XInternAtom(xDisplay, "XdndActionCopy", 0);
|
||||
xAtomXdndStatus = X11::XInternAtom(xDisplay, "XdndStatus", 0);
|
||||
xAtomXdndSelection = X11::XInternAtom(xDisplay, "XdndSelection", 0);
|
||||
xAtomXdndFinished = X11::XInternAtom(xDisplay, "XdndFinished", 0);
|
||||
xAtomXdndAware = X11::XInternAtom(xDisplay, "XdndAware", 0);
|
||||
xAtomWmStateHidden = X11::XInternAtom(xDisplay, "_NET_WM_STATE_HIDDEN", 0);
|
||||
xAtomWmStateMaxHorz = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", 0);
|
||||
xAtomWmStateMaxVert = X11::XInternAtom(xDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", 0);
|
||||
xAtomWmWindowOpacity = X11::XInternAtom(xDisplay, "_NET_WM_WINDOW_OPACITY", 0);
|
||||
xAtomWmName = X11::XInternAtom(xDisplay, "_NET_WM_NAME", 0);
|
||||
xAtomAtom = static_cast<X11::Atom>(4); // XA_ATOM
|
||||
xAtomClipboard = X11::XInternAtom(xDisplay, "CLIPBOARD", 0);
|
||||
xAtomPrimary = static_cast<X11::Atom>(1); // XA_PRIMARY
|
||||
xAtomTargets = X11::XInternAtom(xDisplay, "TARGETS", 0);
|
||||
xAtomText = X11::XInternAtom(xDisplay, "TEXT", 0);
|
||||
xAtomString = static_cast<X11::Atom>(31); // XA_STRING
|
||||
xAtomUTF8String = X11::XInternAtom(xDisplay, "UTF8_STRING", 1);
|
||||
if (xAtomUTF8String == 0)
|
||||
xAtomUTF8String = xAtomString;
|
||||
xAtomXselData = X11::XInternAtom(xDisplay, "XSEL_DATA", 0);
|
||||
|
||||
// We need to override handling of the XFixes selection tracking events from SDL
|
||||
auto screen = X11::XDefaultScreen(xDisplay);
|
||||
auto rootWindow = X11::XRootWindow(xDisplay, screen);
|
||||
int eventBase = 0, errorBase = 0;
|
||||
if (X11::XFixesQueryExtension(xDisplay, &eventBase, &errorBase))
|
||||
{
|
||||
XFixesSelectionNotifyEvent = eventBase + XFixesSelectionNotify;
|
||||
X11::XFixesSelectSelectionInput(xDisplay, rootWindow, xAtomClipboard, XFixesSetSelectionOwnerNotifyMask);
|
||||
X11::XFixesSelectSelectionInput(xDisplay, rootWindow, xAtomPrimary, XFixesSetSelectionOwnerNotifyMask);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void* SDLPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
base::SetHighDpiAwarenessEnabled(enable);
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return UseWayland;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesXWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return !UseWayland;
|
||||
}
|
||||
|
||||
#endif
|
||||
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_MAC
|
||||
|
||||
static_assert(false, "TODO");
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// TODO: This is now called before Platform::Init, ensure the scaling is changed accordingly during Platform::Init (see ApplePlatform::SetHighDpiAwarenessEnabled)
|
||||
}
|
||||
|
||||
#endif
|
||||
64
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
64
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WINDOWS
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
|
||||
// The events for releasing the mouse during window dragging are missing, handle the mouse release event here
|
||||
SDL_bool SDLCALL SDLPlatform::EventMessageHook(void* userdata, MSG* msg)
|
||||
{
|
||||
#define GET_WINDOW_WITH_HWND(window, hwnd) \
|
||||
do { \
|
||||
(window) = nullptr; \
|
||||
WindowsManager::WindowsLocker.Lock(); \
|
||||
for (int32 i = 0; i < WindowsManager::Windows.Count(); i++) \
|
||||
{ \
|
||||
if (WindowsManager::Windows[i]->GetNativePtr() == (hwnd)) \
|
||||
{ \
|
||||
(window) = WindowsManager::Windows[i]; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
WindowsManager::WindowsLocker.Unlock(); \
|
||||
ASSERT((window) != nullptr); \
|
||||
} while (false)
|
||||
|
||||
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 SDL_FALSE;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
#undef GET_WINDOW_WITH_HWND
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitPlatform()
|
||||
{
|
||||
// Workaround required for handling window dragging events properly for DockHintWindow
|
||||
SDL_SetWindowsMessageHook(&EventMessageHook, nullptr);
|
||||
|
||||
if (WindowsPlatform::Init())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// Other supported values: "permonitor", "permonitorv2"
|
||||
SDL_SetHint("SDL_WINDOWS_DPI_AWARENESS", enable ? "system" : "unaware");
|
||||
}
|
||||
|
||||
#endif
|
||||
485
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
485
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Platform/BatteryInfo.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/SDL/SDLInput.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_misc.h>
|
||||
#include <SDL3/SDL_power.h>
|
||||
#include <SDL3/SDL_revision.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_version.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
bool SDLPlatform::Init()
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
if (CommandLine::Options.X11)
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
|
||||
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);
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
// TODO: This should be read from the platform configuration (needed for desktop icon handling)
|
||||
#if USE_EDITOR
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi("com.FlaxEngine.FlaxEditor").Get());
|
||||
#else
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi("com.FlaxEngine.FlaxGame").Get());
|
||||
#endif
|
||||
#else
|
||||
SDL_SetHint(SDL_HINT_APP_ID, StringAnsi(ApplicationClassName).Get());
|
||||
#endif
|
||||
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, "0");
|
||||
SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, "0");
|
||||
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); // Fixes context menu focus issues when clicking unfocused menus
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE, "0");
|
||||
SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0"); // Already handled during platform initialization
|
||||
SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1"); // Allow borderless windows to be resizable on Windows
|
||||
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, "1");
|
||||
|
||||
//SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); // Disables raw mouse input
|
||||
SDL_SetHint(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1");
|
||||
|
||||
// Disable SDL clipboard support
|
||||
SDL_SetEventEnabled(SDL_EVENT_CLIPBOARD_UPDATE, SDL_FALSE);
|
||||
|
||||
// Disable SDL drag and drop support
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, SDL_FALSE);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, SDL_FALSE);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, SDL_FALSE);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, SDL_FALSE);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, SDL_FALSE);
|
||||
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) < 0)
|
||||
Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError())));
|
||||
|
||||
if (InitPlatform())
|
||||
return true;
|
||||
|
||||
SDLInput::Init();
|
||||
|
||||
SystemDpi = (int)(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()) * DefaultDPI);
|
||||
|
||||
//SDL_StartTextInput(); // TODO: Call this only when text input is expected (shows virtual keyboard in some cases)
|
||||
|
||||
return base::Init();
|
||||
}
|
||||
|
||||
void SDLPlatform::LogInfo()
|
||||
{
|
||||
base::LogInfo();
|
||||
|
||||
const int32 runtimeVersion = SDL_GetVersion();
|
||||
LOG(Info, "Using SDL version {}.{}.{} ({}), runtime: {}.{}.{} ({})",
|
||||
SDL_VERSIONNUM_MAJOR(SDL_VERSION), SDL_VERSIONNUM_MINOR(SDL_VERSION), SDL_VERSIONNUM_MICRO(SDL_VERSION), String(SDL_REVISION),
|
||||
SDL_VERSIONNUM_MAJOR(runtimeVersion), SDL_VERSIONNUM_MINOR(runtimeVersion), SDL_VERSIONNUM_MICRO(runtimeVersion), String(SDL_GetRevision()));
|
||||
}
|
||||
|
||||
bool SDLPlatform::CheckWindowDragging(Window* window, WindowHitCodes hit)
|
||||
{
|
||||
bool handled = false;
|
||||
window->OnLeftButtonHit(hit, handled);
|
||||
if (handled)
|
||||
{
|
||||
DraggedWindowId = window->_windowId;
|
||||
LOG(Info, "Dragging: {}", window->_settings.Title);
|
||||
|
||||
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();
|
||||
|
||||
Float2 mousePos;
|
||||
auto buttons = SDL_GetGlobalMouseState(&mousePos.X, &mousePos.Y);
|
||||
if (window != nullptr)
|
||||
{
|
||||
/*int top, left, bottom, right;
|
||||
SDL_GetWindowBordersSize(window->_window, &top, &left, &bottom, &right);
|
||||
mousePos += Float2(left, -top);
|
||||
Input::Mouse->OnMouseDown(mousePos, MouseButton::Left, window);*/
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
|
||||
void SDLPlatform::Tick()
|
||||
{
|
||||
SDLInput::Update();
|
||||
|
||||
if (DraggedWindowId != 0)
|
||||
{
|
||||
Float2 mousePos;
|
||||
auto buttons = SDL_GetGlobalMouseState(&mousePos.X, &mousePos.Y);
|
||||
if (!(buttons & SDL_BUTTON(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
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PumpEvents();
|
||||
SDL_Event events[32];
|
||||
int count;
|
||||
while ((count = SDL_PeepEvents(events, SDL_arraysize(events), SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST)))
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
SDLWindow* window = SDLWindow::GetWindowFromEvent(events[i]);
|
||||
if (window)
|
||||
window->HandleEvent(events[i]);
|
||||
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]);
|
||||
}
|
||||
SDL_PumpEvents();
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLPlatform::HandleEvent(SDL_Event& event)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
BatteryInfo SDLPlatform::GetBatteryInfo()
|
||||
{
|
||||
BatteryInfo info;
|
||||
int percentage;
|
||||
SDL_PowerState powerState = SDL_GetPowerInfo(nullptr, &percentage);
|
||||
|
||||
if (percentage < 0)
|
||||
info.BatteryLifePercent = 1.0f;
|
||||
else
|
||||
info.BatteryLifePercent = (float)percentage / 100.0f;
|
||||
|
||||
switch (powerState)
|
||||
{
|
||||
case SDL_POWERSTATE_CHARGING:
|
||||
info.State = BatteryInfo::States::BatteryCharging;
|
||||
break;
|
||||
case SDL_POWERSTATE_ON_BATTERY:
|
||||
info.State = BatteryInfo::States::BatteryDischarging;
|
||||
break;
|
||||
case SDL_POWERSTATE_CHARGED:
|
||||
info.State = BatteryInfo::States::Connected;
|
||||
break;
|
||||
default:
|
||||
info.State = BatteryInfo::States::Unknown;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
int32 SDLPlatform::GetDpi()
|
||||
{
|
||||
return SystemDpi;
|
||||
}
|
||||
|
||||
void SDLPlatform::OpenUrl(const StringView& url)
|
||||
{
|
||||
StringAnsi urlStr(url);
|
||||
SDL_OpenURL(urlStr.GetText());
|
||||
}
|
||||
|
||||
Float2 SDLPlatform::GetMousePosition()
|
||||
{
|
||||
Float2 pos;
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
return pos;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetMousePosition(const Float2& pos)
|
||||
{
|
||||
SDL_WarpMouseGlobal(pos.X, pos.Y);
|
||||
}
|
||||
|
||||
Float2 SDLPlatform::GetDesktopSize()
|
||||
{
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(SDL_GetPrimaryDisplay(), &rect);
|
||||
return Float2(static_cast<float>(rect.w), static_cast<float>(rect.h));
|
||||
}
|
||||
|
||||
Rectangle SDLPlatform::GetMonitorBounds(const Float2& screenPos)
|
||||
{
|
||||
SDL_Point point{ (int32)screenPos.X, (int32)screenPos.Y };
|
||||
SDL_DisplayID display = SDL_GetDisplayForPoint(&point);
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(display, &rect);
|
||||
return Rectangle(static_cast<float>(rect.x), static_cast<float>(rect.y), static_cast<float>(rect.w), static_cast<float>(rect.h));
|
||||
}
|
||||
|
||||
Rectangle SDLPlatform::GetVirtualDesktopBounds()
|
||||
{
|
||||
int count;
|
||||
const SDL_DisplayID* displays = SDL_GetDisplays(&count);
|
||||
if (displays == nullptr)
|
||||
return Rectangle::Empty;
|
||||
|
||||
Rectangle bounds = Rectangle::Empty;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SDL_DisplayID display = displays[i];
|
||||
SDL_Rect rect;
|
||||
SDL_GetDisplayBounds(display, &rect);
|
||||
bounds = Rectangle::Union(bounds, Rectangle(static_cast<float>(rect.x), static_cast<float>(rect.y), static_cast<float>(rect.w), static_cast<float>(rect.h)));
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
Window* SDLPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
return New<SDLWindow>(settings);
|
||||
}
|
||||
|
||||
#if !PLATFORM_LINUX
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesXWayland()
|
||||
{
|
||||
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) != 0)
|
||||
{
|
||||
LOG(Error, "Failed to show message box: {0}", String(SDL_GetError()));
|
||||
return DialogResult::Abort;
|
||||
}
|
||||
if (result < 0)
|
||||
return DialogResult::None;
|
||||
return (DialogResult)result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
82
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
82
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/Enums.h"
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Windows/WindowsPlatform.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#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.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLPlatform
|
||||
#if PLATFORM_WINDOWS
|
||||
: public WindowsPlatform
|
||||
{
|
||||
using base = WindowsPlatform;
|
||||
#elif PLATFORM_LINUX
|
||||
: public LinuxPlatform
|
||||
{
|
||||
using base = LinuxPlatform;
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
friend SDLWindow;
|
||||
|
||||
private:
|
||||
static uint32 DraggedWindowId;
|
||||
|
||||
private:
|
||||
static bool InitPlatform();
|
||||
#if PLATFORM_LINUX
|
||||
static bool InitPlatformX11(void* display);
|
||||
#endif
|
||||
static bool HandleEvent(SDL_Event& event);
|
||||
#if PLATFORM_WINDOWS
|
||||
static int __cdecl EventMessageHook(void* userdata, MSG* msg);
|
||||
#elif PLATFORM_LINUX
|
||||
static int __cdecl X11EventHook(void *userdata, _XEvent *xevent);
|
||||
#endif
|
||||
|
||||
public:
|
||||
static bool CheckWindowDragging(Window* window, WindowHitCodes hit);
|
||||
#if PLATFORM_LINUX
|
||||
static void* GetXDisplay();
|
||||
#endif
|
||||
static bool UsesWayland();
|
||||
static bool UsesXWayland();
|
||||
static bool UsesX11();
|
||||
|
||||
public:
|
||||
|
||||
// [PlatformBase]
|
||||
static bool Init();
|
||||
static void LogInfo();
|
||||
static void Tick();
|
||||
static void SetHighDpiAwarenessEnabled(bool enable);
|
||||
static BatteryInfo GetBatteryInfo();
|
||||
static int32 GetDpi();
|
||||
static void OpenUrl(const StringView& url);
|
||||
static Float2 GetMousePosition();
|
||||
static void SetMousePosition(const Float2& pos);
|
||||
static Float2 GetDesktopSize();
|
||||
static Rectangle GetMonitorBounds(const Float2& screenPos);
|
||||
static Rectangle GetVirtualDesktopBounds();
|
||||
static Window* CreateWindow(const CreateWindowSettings& settings);
|
||||
};
|
||||
|
||||
#endif
|
||||
1067
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
1067
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
120
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
120
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/WindowBase.h"
|
||||
|
||||
struct SDL_Window;
|
||||
union SDL_Event;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the window class for SDL platform
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLWindow : public WindowBase
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
, public Windows::IDropTarget
|
||||
#endif
|
||||
{
|
||||
friend SDLPlatform;
|
||||
#if PLATFORM_LINUX
|
||||
friend LinuxPlatform;
|
||||
friend class LinuxWindow;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void* _handle; // Opaque, platform specific window handle
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
Windows::ULONG _refCount;
|
||||
#endif
|
||||
#if PLATFORM_LINUX
|
||||
bool _resizeDisabled, _focusOnMapped = false, _dragOver = false;
|
||||
#endif
|
||||
SDL_Window* _window;
|
||||
uint32 _windowId;
|
||||
Rectangle _clipCursorRect;
|
||||
Rectangle _cachedClientRectangle;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SDLWindow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="settings">The initial window settings.</param>
|
||||
SDLWindow(const CreateWindowSettings& settings);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="SDLWindow"/> class.
|
||||
/// </summary>
|
||||
~SDLWindow();
|
||||
|
||||
private:
|
||||
|
||||
static SDLWindow* GetWindowFromEvent(const SDL_Event& event);
|
||||
static SDLWindow* GetWindowWithId(uint32 windowId);
|
||||
void HandleEvent(SDL_Event& event);
|
||||
void CheckForWindowResize();
|
||||
void UpdateCursor() const;
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
DragDropEffect DoDragDropWayland(const StringView& data);
|
||||
DragDropEffect DoDragDropX11(const StringView& data);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
void* GetWaylandSurfacePtr() const;
|
||||
void* GetWaylandDisplay() const;
|
||||
uintptr GetX11WindowHandle() const;
|
||||
void* GetX11Display() const;
|
||||
#endif
|
||||
|
||||
// [WindowBase]
|
||||
void* GetNativePtr() const override;
|
||||
void Show() override;
|
||||
void Hide() override;
|
||||
void Minimize() override;
|
||||
void Maximize() override;
|
||||
void SetBorderless(bool isBorderless, bool maximized = false) override;
|
||||
void Restore() override;
|
||||
bool IsClosed() const override;
|
||||
bool IsForegroundWindow() const override;
|
||||
void BringToFront(bool force = false) override;
|
||||
void SetClientBounds(const Rectangle& clientArea) override;
|
||||
void SetPosition(const Float2& position) override;
|
||||
void SetClientPosition(const Float2& position) override;
|
||||
void SetIsFullscreen(bool isFullscreen) override;
|
||||
Float2 GetPosition() const override;
|
||||
Float2 GetSize() const override;
|
||||
Float2 GetClientSize() const override;
|
||||
Float2 ScreenToClient(const Float2& screenPos) const override;
|
||||
Float2 ClientToScreen(const Float2& clientPos) const override;
|
||||
void FlashWindow() override;
|
||||
float GetOpacity() const override;
|
||||
void SetOpacity(float opacity) override;
|
||||
void Focus() override;
|
||||
String GetTitle() const override;
|
||||
void SetTitle(const StringView& title) override;
|
||||
DragDropEffect DoDragDrop(const StringView& data) override;
|
||||
void StartTrackingMouse(bool useMouseScreenOffset) override;
|
||||
void EndTrackingMouse() override;
|
||||
void StartClippingCursor(const Rectangle& bounds) override;
|
||||
void EndClippingCursor() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
// [IUnknown]
|
||||
Windows::HRESULT __stdcall QueryInterface(const Windows::IID& id, void** ppvObject) override;
|
||||
Windows::ULONG __stdcall AddRef() override;
|
||||
Windows::ULONG __stdcall Release() override;
|
||||
|
||||
// [Windows::IDropTarget]
|
||||
Windows::HRESULT __stdcall DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragLeave() override;
|
||||
Windows::HRESULT __stdcall Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -18,10 +18,12 @@ class Win32Thread;
|
||||
typedef Win32Thread Thread;
|
||||
class WindowsClipboard;
|
||||
typedef WindowsClipboard Clipboard;
|
||||
#if !PLATFORM_SDL
|
||||
class WindowsPlatform;
|
||||
typedef WindowsPlatform Platform;
|
||||
class WindowsWindow;
|
||||
typedef WindowsWindow Window;
|
||||
#endif
|
||||
class Win32Network;
|
||||
typedef Win32Network Network;
|
||||
class UserBase;
|
||||
@@ -68,12 +70,14 @@ class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class LinuxThread;
|
||||
typedef LinuxThread Thread;
|
||||
#if !PLATFORM_SDL
|
||||
class LinuxClipboard;
|
||||
typedef LinuxClipboard Clipboard;
|
||||
class LinuxPlatform;
|
||||
typedef LinuxPlatform Platform;
|
||||
class LinuxWindow;
|
||||
typedef LinuxWindow Window;
|
||||
#endif
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
@@ -288,3 +292,14 @@ typedef UserBase User;
|
||||
#error Missing Types implementation!
|
||||
|
||||
#endif
|
||||
|
||||
#if PLATFORM_SDL
|
||||
#if PLATFORM_LINUX
|
||||
class SDLClipboard;
|
||||
typedef SDLClipboard Clipboard;
|
||||
#endif
|
||||
class SDLPlatform;
|
||||
typedef SDLPlatform Platform;
|
||||
class SDLWindow;
|
||||
typedef SDLWindow Window;
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLWindow.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsWindow.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPWindow.h"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include "WindowsWindow.h"
|
||||
#include "WindowsFileSystem.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
@@ -317,7 +318,7 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin
|
||||
if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder))))
|
||||
fd->SetFolder(defaultFolder);
|
||||
|
||||
HWND hwndOwner = parentWindow ? parentWindow->GetHWND() : NULL;
|
||||
HWND hwndOwner = parentWindow ? (HWND)parentWindow->GetNativePtr() : NULL;
|
||||
if (SUCCEEDED(fd->Show(hwndOwner)))
|
||||
{
|
||||
ComPtr<IShellItem> si;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
|
||||
#include "WindowsInput.h"
|
||||
#include "WindowsWindow.h"
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Platform/Windows/WindowsInput.h"
|
||||
#include "Engine/Platform/Windows/WindowsWindow.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/CreateWindowSettings.h"
|
||||
#include "Engine/Platform/CreateProcessSettings.h"
|
||||
@@ -255,6 +257,8 @@ void GetWindowsVersion(String& windowsName, int32& versionMajor, int32& versionM
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// Find window to process that message
|
||||
@@ -272,6 +276,8 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
long __stdcall WindowsPlatform::SehExceptionHandler(EXCEPTION_POINTERS* ep)
|
||||
{
|
||||
if (ep->ExceptionRecord->ExceptionCode == CLR_EXCEPTION)
|
||||
@@ -518,6 +524,7 @@ void WindowsPlatform::PreInit(void* hInstance)
|
||||
// Disable the process from being showing "ghosted" while not responding messages during slow tasks
|
||||
DisableProcessWindowsGhosting();
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Register window class
|
||||
WNDCLASS windowsClass;
|
||||
Platform::MemoryClear(&windowsClass, sizeof(WNDCLASS));
|
||||
@@ -532,6 +539,7 @@ void WindowsPlatform::PreInit(void* hInstance)
|
||||
Error(TEXT("Window class registration failed!"));
|
||||
exit(-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Init OLE
|
||||
if (OleInitialize(nullptr) != S_OK)
|
||||
@@ -680,7 +688,9 @@ bool WindowsPlatform::Init()
|
||||
}
|
||||
OnPlatformUserAdd(New<User>(userName));
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
WindowsInput::Init();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -708,7 +718,9 @@ void WindowsPlatform::LogInfo()
|
||||
|
||||
void WindowsPlatform::Tick()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
WindowsInput::Update();
|
||||
#endif
|
||||
|
||||
// Check to see if any messages are waiting in the queue
|
||||
MSG msg;
|
||||
@@ -739,8 +751,10 @@ void WindowsPlatform::Exit()
|
||||
DbgHelpUnlock();
|
||||
#endif
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
// Unregister app class
|
||||
UnregisterClassW(ApplicationClassName, nullptr);
|
||||
#endif
|
||||
|
||||
Win32Platform::Exit();
|
||||
}
|
||||
@@ -1185,11 +1199,15 @@ int32 WindowsPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
Window* WindowsPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
return New<WindowsWindow>(settings);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* WindowsPlatform::LoadLibrary(const Char* filename)
|
||||
{
|
||||
ASSERT(filename);
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#if PLATFORM_SDL
|
||||
#include "Engine/Platform/SDL/SDLWindow.h"
|
||||
#endif
|
||||
|
||||
#include "Engine/Platform/Windows/WindowsWindow.h"
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -596,7 +600,11 @@ DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
::POINT point;
|
||||
::GetCursorPos(&point);
|
||||
#if PLATFORM_SDL
|
||||
Input::Mouse->OnMouseUp(Float2((float)point.x, (float)point.y), MouseButton::Left, (Window*)this);
|
||||
#else
|
||||
Input::Mouse->OnMouseUp(Float2((float)point.x, (float)point.y), MouseButton::Left, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
return SUCCEEDED(result) ? dropEffectFromOleEnum(dwEffect) : DragDropEffect::None;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
|
||||
#include "WindowsWindow.h"
|
||||
#include "WindowsPlatform.h"
|
||||
@@ -1308,7 +1308,7 @@ LRESULT WindowsWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
|
||||
if (_settings.AllowInput)
|
||||
{
|
||||
if (WindowsInput::WndProc(this, msg, wParam, lParam))
|
||||
if (WindowsInput::WndProc((Window*)this, msg, wParam, lParam))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/WindowBase.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
Windows::ULONG __stdcall AddRef() override;
|
||||
Windows::ULONG __stdcall Release() override;
|
||||
|
||||
// [IDropTarget]
|
||||
// [Windows::IDropTarget]
|
||||
Windows::HRESULT __stdcall DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect) override;
|
||||
Windows::HRESULT __stdcall DragLeave() override;
|
||||
|
||||
Reference in New Issue
Block a user