25 Commits

Author SHA1 Message Date
8ba7351206 asd
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2024-07-25 23:18:05 +03:00
7c901a56e5 _inp 2024-07-25 23:17:24 +03:00
8ba44e75a4 _header 2024-07-25 22:59:34 +03:00
7cadc8d331 _bin 2024-07-25 22:59:08 +03:00
7debd885b9 _windowtype 2024-07-25 22:59:01 +03:00
44a9c52c44 _mouse 2024-07-25 22:58:55 +03:00
dcc444173d _cleanup 2024-07-25 22:52:22 +03:00
d3c4b5b8c1 Enable warning sound in question dialogs
Some checks failed
Build Android / Game (Android, Release ARM64) (push) Has been cancelled
Build iOS / Game (iOS, Release ARM64) (push) Has been cancelled
Build Linux / Editor (Linux, Development x64) (push) Has been cancelled
Build Linux / Game (Linux, Release x64) (push) Has been cancelled
Build macOS / Editor (Mac, Development ARM64) (push) Has been cancelled
Build macOS / Game (Mac, Release ARM64) (push) Has been cancelled
Build Windows / Editor (Windows, Development x64) (push) Has been cancelled
Build Windows / Game (Windows, Release x64) (push) Has been cancelled
Tests / Tests (Linux) (push) Has been cancelled
Tests / Tests (Windows) (push) Has been cancelled
2024-07-25 22:33:15 +03:00
56236b0579 Enable modern Windows dialog boxes 2024-07-25 22:33:15 +03:00
c2a588f738 Implement relative mouse mode (raw input) for SDL platform 2024-07-25 22:33:15 +03:00
09331b601b Add flag for Window types 2024-07-25 22:33:14 +03:00
17d7716e41 Enable native windowing system settings with SDL platform 2024-07-25 22:33:14 +03:00
0344cde98f Add command-line switches to force X11 and Wayland SDL drivers 2024-07-25 22:33:14 +03:00
9f35f175ab Implement SDL platform, windowing and input handling 2024-07-25 22:33:13 +03:00
cabf8736c7 Refactor application window class name 2024-07-25 22:32:54 +03:00
93344a33d3 Move Window related enums to separate header file 2024-07-25 22:32:27 +03:00
3898df1b68 Refactor Windows drag and drop implementation 2024-07-25 22:32:26 +03:00
33909cf931 Refactor ScreenUtilities 2024-07-25 22:32:26 +03:00
3bc70c147b Add more helper methods for managing Git repos 2024-07-25 22:32:26 +03:00
70dea56d44 Fix centered window location on X11 2024-07-25 22:32:25 +03:00
348e15475f Fix rebuilding dependencies using Git with existing local folders 2024-07-25 22:32:25 +03:00
fe568cf16a Fix initial position of Tooltips 2024-07-25 22:32:25 +03:00
1066a38130 _bin 2024-07-25 22:32:24 +03:00
2494c3cb23 _deps build sdl only 2024-07-25 22:08:10 +03:00
ecc34320d1 Disable LFS override 2024-07-25 20:18:26 +03:00
178 changed files with 69268 additions and 1273 deletions

View File

@@ -1,4 +1,4 @@
# Redirect to our own Git LFS server
[lfs]
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
locksverify = false

View File

@@ -13,6 +13,7 @@
"Configuration": {
"UseCSharp": true,
"UseLargeWorlds": false,
"UseDotNet": true
"UseDotNet": true,
"UseSDL": true
}
}

View File

@@ -1,7 +1,10 @@
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
#define USE_IS_FOREGROUND
#else
#endif
#if PLATFORM_SDL
#define USE_SDL_WORKAROUNDS
#endif
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
@@ -111,7 +114,7 @@ namespace FlaxEditor.GUI.ContextMenu
}
/// <summary>
/// Shows the empty menu popup o na screen.
/// Shows the empty menu popup on a screen.
/// </summary>
/// <param name="control">The target control.</param>
/// <param name="area">The target control area to cover.</param>
@@ -215,7 +218,7 @@ namespace FlaxEditor.GUI.ContextMenu
desc.AllowMaximize = false;
desc.AllowDragAndDrop = false;
desc.IsTopmost = true;
desc.IsRegularWindow = false;
desc.Type = WindowType.Utility;
desc.HasSizingFrame = false;
OnWindowCreating(ref desc);
_window = Platform.CreateWindow(ref desc);
@@ -228,8 +231,6 @@ namespace FlaxEditor.GUI.ContextMenu
// Show
Visible = true;
if (_window == null)
return;
_window.Show();
PerformLayout();
_previouslyFocused = parentWin.FocusedControl;
@@ -378,6 +379,11 @@ namespace FlaxEditor.GUI.ContextMenu
}
}
#if USE_SDL_WORKAROUNDS
private void OnWindowGotFocus()
{
}
#else
private void OnWindowGotFocus()
{
var child = _childCM;
@@ -391,6 +397,7 @@ namespace FlaxEditor.GUI.ContextMenu
});
}
}
#endif
private void OnWindowLostFocus()
{
@@ -489,7 +496,12 @@ namespace FlaxEditor.GUI.ContextMenu
// Let root context menu to check if none of the popup windows
if (_parentCM == null && !IsForeground)
{
#if USE_SDL_WORKAROUNDS
if (!IsMouseOver)
Hide();
#else
Hide();
#endif
}
}
#endif

View File

@@ -67,9 +67,7 @@ namespace FlaxEditor.GUI.Docking
Proxy.Window.MouseUp += OnMouseUp;
Proxy.Window.MouseMove += OnMouseMove;
Proxy.Window.LostFocus += OnLostFocus;
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
_toMove.Window.Window.MouseUp += OnMouseUp; // Intercept the drag release mouse event from source window
// Update window GUI
Proxy.Window.GUI.PerformLayout();
@@ -77,13 +75,16 @@ namespace FlaxEditor.GUI.Docking
// Update rectangles
UpdateRects();
// Hide base window
window.Hide();
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
Proxy.Window.Focus();
// Hide base window
window.Hide();
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
}
/// <summary>
@@ -101,6 +102,8 @@ namespace FlaxEditor.GUI.Docking
Proxy.Window.MouseUp -= OnMouseUp;
Proxy.Window.MouseMove -= OnMouseMove;
Proxy.Window.LostFocus -= OnLostFocus;
if (_toMove?.Window?.Window)
_toMove.Window.Window.MouseUp -= OnMouseUp;
// Hide the proxy
Proxy.Hide();
@@ -438,7 +441,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.Type = WindowType.Utility;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ShowAfterFirstPaint = false;
@@ -470,7 +473,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.Type = WindowType.Utility;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ActivateWhenFirstShown = false;

View File

@@ -82,7 +82,7 @@ namespace FlaxEditor.GUI.Docking
settings.AllowMaximize = true;
settings.AllowDragAndDrop = true;
settings.IsTopmost = false;
settings.IsRegularWindow = true;
settings.Type = WindowType.Regular;
settings.HasSizingFrame = true;
settings.ShowAfterFirstPaint = false;
settings.ShowInTaskbar = true;

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button);
}
#if !PLATFORM_SDL
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
@@ -292,6 +293,36 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location);
}
#else
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
var location = Root.TrackingMouseOffset;
if (_isSliding)
{
// Update sliding
ApplySliding(Root.TrackingMouseOffset.X * _slideSpeed);
return;
}
// Update cursor type so user knows they can slide value
if (CanUseSliding && SlideRect.Contains(location) && !_isSliding)
{
Cursor = CursorType.SizeWE;
_cursorChanged = true;
}
else if (_cursorChanged && !_isSliding)
{
Cursor = CursorType.Default;
_cursorChanged = false;
}
base.OnMouseMoveRelative(mouseMotion);
}
#endif
/// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button)
{

View File

@@ -12,7 +12,7 @@ namespace FlaxEditor.GUI
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public sealed class MainMenu : ContainerControl
{
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
private bool _useCustomWindowSystem;
private Image _icon;
private Label _title;
@@ -67,7 +67,7 @@ namespace FlaxEditor.GUI
AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
if (_useCustomWindowSystem)
{
@@ -166,7 +166,7 @@ namespace FlaxEditor.GUI
}
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void Update(float deltaTime)
{
@@ -291,7 +291,7 @@ namespace FlaxEditor.GUI
if (base.OnMouseDoubleClick(location, button))
return true;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
var child = GetChildAtRecursive(location);
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
{
@@ -321,7 +321,7 @@ namespace FlaxEditor.GUI
{
float x = 0;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Icon
@@ -349,7 +349,7 @@ namespace FlaxEditor.GUI
}
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem)
{
// Buttons
@@ -367,7 +367,7 @@ namespace FlaxEditor.GUI
#endif
}
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc />
public override void OnDestroy()
{

View File

@@ -42,7 +42,7 @@ namespace FlaxEditor.GUI
Text = text;
var style = Style.Current;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (Editor.Instance.Options.Options.Interface.UseNativeWindowSystem)
{
BackgroundColorMouseOver = style.BackgroundHighlighted;

View File

@@ -433,6 +433,9 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;

View File

@@ -381,7 +381,7 @@ namespace FlaxEditor.Modules
Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif
{

View File

@@ -760,8 +760,10 @@ namespace FlaxEditor.Modules
{
settings.HasBorder = false;
#if !PLATFORM_SDL
// Skip OS sizing frame and implement it using LeftButtonHit
settings.HasSizingFrame = false;
#endif
}
#elif PLATFORM_LINUX
settings.HasBorder = false;

View File

@@ -145,7 +145,7 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")]
public float InterfaceScale { get; set; } = 1.0f;
#if PLATFORM_WINDOWS
#if PLATFORM_WINDOWS || PLATFORM_SDL
/// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required.
/// </summary>

View File

@@ -1,131 +0,0 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#include "ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
Delegate<Color32> ScreenUtilities::PickColorDone;
#if PLATFORM_WINDOWS
#include <Windows.h>
#pragma comment(lib, "Gdi32.lib")
static HHOOK MouseCallbackHook;
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
{
UnhookWindowsHookEx(MouseCallbackHook);
// Push event with the picked color
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
ScreenUtilities::PickColorDone(colorPicked);
return 1;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
PROFILE_CPU();
HDC deviceContext = GetDC(NULL);
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
ReleaseDC(NULL, deviceContext);
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
}
void ScreenUtilities::PickColor()
{
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
if (MouseCallbackHook == NULL)
{
LOG(Warning, "Failed to set mouse hook.");
LOG(Warning, "Error: {0}", GetLastError());
}
}
#elif PLATFORM_LINUX
#include "Engine/Platform/Linux/LinuxPlatform.h"
#include "Engine/Platform/Linux/IncludeX11.h"
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
X11::XColor color;
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
int defaultScreen = X11::XDefaultScreen(display);
X11::XImage* image;
image = X11::XGetImage(display, X11::XRootWindow(display, defaultScreen), (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
color.pixel = XGetPixel(image, 0, 0);
X11::XFree(image);
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
Color32 outputColor;
outputColor.R = color.red / 256;
outputColor.G = color.green / 256;
outputColor.B = color.blue / 256;
outputColor.A = 255;
return outputColor;
}
void OnScreenUtilsXEventCallback(void* eventPtr)
{
X11::XEvent* event = (X11::XEvent*) eventPtr;
X11::Display* display = (X11::Display*)LinuxPlatform::GetXDisplay();
if (event->type == ButtonPress)
{
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
X11::XUngrabPointer(display, CurrentTime);
ScreenUtilities::PickColorDone(colorPicked);
LinuxPlatform::xEventRecieved.Unbind(OnScreenUtilsXEventCallback);
}
}
void ScreenUtilities::PickColor()
{
PROFILE_CPU();
X11::Display* display = (X11::Display*) LinuxPlatform::GetXDisplay();
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
X11::Cursor cursor = XCreateFontCursor(display, 130);
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
if (grabbedPointer != GrabSuccess)
{
LOG(Error, "Failed to grab cursor for events.");
X11::XFreeCursor(display, cursor);
return;
}
X11::XFreeCursor(display, cursor);
LinuxPlatform::xEventRecieved.Bind(OnScreenUtilsXEventCallback);
}
#elif PLATFORM_MAC
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
Color32 ScreenUtilities::GetColorAt(const Float2& pos)
{
// TODO: implement ScreenUtilities for macOS
return { 0, 0, 0, 255 };
}
void ScreenUtilities::PickColor()
{
// This is what C# calls to start the color picking sequence
// This should stop mouse clicks from working for one click, and that click is on the selected color
// There is a class called NSColorSample that might implement that for you, but maybe not.
}
#endif

View File

@@ -2,32 +2,4 @@
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API ScreenUtilities
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ScreenUtilities);
/// <summary>
/// Gets the pixel color at the specified coordinates.
/// </summary>
/// <param name="pos">Screen-space coordinate to read.</param>
/// <returns>Pixel color at the specified coordinates.</returns>
API_FUNCTION() static Color32 GetColorAt(const Float2& pos);
/// <summary>
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
/// </summary>
API_FUNCTION() static void PickColor();
/// <summary>
/// Called when PickColor action is finished.
/// </summary>
API_EVENT() static Delegate<Color32> PickColorDone;
};
#include "Engine/Platform/ScreenUtilities.h"

View File

@@ -155,18 +155,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed;
private float _minMovementSpeed;
private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale;
private bool _useMouseFiltering;
private bool _useMouseAcceleration;
#endif
// Input
internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary>
/// The previous input (from the previous update).
@@ -522,10 +526,11 @@ namespace FlaxEditor.Viewport
: base(task)
{
_editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f;
_useMouseFiltering = false;
_useMouseAcceleration = false;
#endif
_camera = camera;
if (_camera != null)
_camera.Viewport = this;
@@ -1420,7 +1425,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge
var size = Size;
var center = Float2.Round(size * 0.5f);
@@ -1429,6 +1436,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos);
}
#endif
}
/// <summary>
@@ -1440,6 +1448,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default;
win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
}
/// <summary>
@@ -1544,6 +1553,14 @@ namespace FlaxEditor.Viewport
else
EndMouseCapture();
}
#if PLATFORM_SDL
bool useMouse = IsControllingMouse || true;
_prevInput = _input;
if (canUseInput && ContainsFocus)
_input.Gather(win.Window, useMouse, ref _prevInput);
else
_input.Clear();
#else
bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
@@ -1551,6 +1568,7 @@ namespace FlaxEditor.Viewport
_input.Gather(win.Window, useMouse, ref _prevInput);
else
_input.Clear();
#endif
// Track controlling mouse state change
bool wasControllingMouse = _prevInput.IsControllingMouse;
@@ -1659,6 +1677,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown)
moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1700,18 +1722,21 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta;
}
#endif
// Update
moveDelta *= dt * (60.0f * 4.0f);
mouseDelta *= 0.1833f * MouseSpeed * _mouseSensitivity;
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{
var center = PointToWindow(_startPos);
win.MousePosition = center;
}
#endif
// Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel)
@@ -1723,6 +1748,8 @@ namespace FlaxEditor.Viewport
}
else
{
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{
// Calculate smooth mouse delta not dependant on viewport size
@@ -1737,6 +1764,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero;
}
_mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus)
{
@@ -1786,6 +1814,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0;
}
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button)
{

View File

@@ -28,7 +28,7 @@ const Char* SplashScreenQuotes[] =
#elif PLATFORM_LINUX
TEXT("Try it on a Raspberry"),
TEXT("Trying to exit vim"),
TEXT("Sudo flax --loadproject"),
TEXT("sudo flax --project HelloWorld.flaxproj"),
#elif PLATFORM_MAC
TEXT("don't compare Macbooks to oranges."),
TEXT("Why does macbook heat up?\nBecause it doesn't have windows"),
@@ -104,6 +104,7 @@ const Char* SplashScreenQuotes[] =
TEXT("You have my bow.\nAnd my axe!"),
TEXT("To the bridge of Khazad-dum."),
TEXT("One ring to rule them all.\nOne ring to find them."),
TEXT("Where there's a whip, there's a way."),
TEXT("That's what she said"),
TEXT("We could be compiling shaders here"),
TEXT("Hello There"),
@@ -163,7 +164,7 @@ void SplashScreen::Show()
settings.AllowMaximize = false;
settings.AllowDragAndDrop = false;
settings.IsTopmost = false;
settings.IsRegularWindow = false;
settings.Type = WindowType::Utility;
settings.HasSizingFrame = false;
settings.ShowAfterFirstPaint = true;
settings.StartPosition = WindowStartPosition::CenterScreen;

View File

@@ -146,6 +146,13 @@ bool CommandLine::Parse(const Char* cmdLine)
PARSE_BOOL_SWITCH("-mute ", Mute);
PARSE_BOOL_SWITCH("-lowdpi ", LowDPI);
#if PLATFORM_LINUX && PLATFORM_SDL
PARSE_BOOL_SWITCH("-wayland ", Wayland);
PARSE_BOOL_SWITCH("-x11 ", X11);
#endif
#if USE_EDITOR
PARSE_BOOL_SWITCH("-clearcache ", ClearCache);

View File

@@ -127,6 +127,20 @@ public:
/// </summary>
Nullable<bool> LowDPI;
#if PLATFORM_LINUX && PLATFORM_SDL
/// <summary>
/// -wayland (prefer Wayland over X11 as display server)
/// </summary>
Nullable<bool> Wayland;
/// <summary>
/// -x11 (prefer X11 over Wayland as display server)
/// </summary>
Nullable<bool> X11;
#endif
#if USE_EDITOR
/// <summary>

View File

@@ -94,13 +94,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

View File

@@ -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
{
@@ -100,11 +106,25 @@ void Screen::SetCursorVisible(const bool value)
#else
const auto win = Engine::MainWindow;
#endif
bool focused = false;
if (win && Engine::HasGameViewportFocus())
{
win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
focused = true;
}
else if (win)
win->SetCursor(CursorType::Default);
CursorVisible = value;
// Just enable relative mode when cursor is constrained and not visible
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
{
Input::Mouse->SetRelativeMode(true);
}
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
{
Input::Mouse->SetRelativeMode(false);
}
}
CursorLockMode Screen::GetCursorLock()
@@ -133,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
win->EndClippingCursor();
}
CursorLock = mode;
// Just enable relative mode when cursor is constrained and not visible
bool focused = win && Engine::HasGameViewportFocus();
if (CursorLock != CursorLockMode::None && !CursorVisible && focused)
{
Input::Mouse->SetRelativeMode(true);
}
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
{
Input::Mouse->SetRelativeMode(false);
}
}
GameWindowMode Screen::GetGameWindowMode()
@@ -190,7 +221,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
}

View File

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

View File

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

View File

@@ -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.");

View File

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

View File

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

View File

@@ -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));
}

View File

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

View File

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

View File

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

View File

@@ -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]()
{

View File

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

View File

@@ -93,6 +93,7 @@ Delegate<const Float2&, MouseButton> Input::MouseUp;
Delegate<const Float2&, MouseButton> Input::MouseDoubleClick;
Delegate<const Float2&, float> Input::MouseWheel;
Delegate<const Float2&> Input::MouseMove;
Delegate<const Float2&> Input::MouseMoveRelative;
Action Input::MouseLeave;
Delegate<const Float2&, int32> Input::TouchDown;
Delegate<const Float2&, int32> Input::TouchMove;
@@ -221,6 +222,14 @@ void Mouse::OnMouseMove(const Float2& position, Window* target)
e.MouseData.Position = position;
}
void Mouse::OnMouseMoveRelative(const Float2& positionRelative, Window* target)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseMoveRelative;
e.Target = target;
e.MouseMovementData.PositionRelative = positionRelative;
}
void Mouse::OnMouseLeave(Window* target)
{
Event& e = _queue.AddOne();
@@ -286,6 +295,11 @@ bool Mouse::Update(EventQueue& queue)
_state.MousePosition = e.MouseData.Position;
break;
}
case EventType::MouseMoveRelative:
{
_state.MousePosition += e.MouseMovementData.PositionRelative;
break;
}
case EventType::MouseLeave:
{
break;
@@ -743,6 +757,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;
@@ -799,6 +816,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove:
Input::MouseMove(e.MouseData.Position);
break;
case InputDevice::EventType::MouseMoveRelative:
Input::MouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave:
Input::MouseLeave();
break;
@@ -1011,12 +1031,14 @@ void InputService::Update()
}
}
#if !PLATFORM_SDL
// Lock mouse if need to
const auto lockMode = Screen::GetCursorLock();
if (lockMode == CursorLockMode::Locked)
{
Input::SetMousePosition(Screen::GetSize() * 0.5f);
}
#endif
// Send events for the active actions and axes (send events only in play mode)
if (!Time::GetGamePaused())

View File

@@ -107,6 +107,11 @@ public:
/// </summary>
API_EVENT() static Delegate<const Float2&> MouseMove;
/// <summary>
/// Event fired when mouse moves while in relative mode.
/// </summary>
API_EVENT() static Delegate<const Float2&> MouseMoveRelative;
/// <summary>
/// Event fired when mouse leaves window.
/// </summary>

View File

@@ -25,6 +25,7 @@ public:
MouseDoubleClick,
MouseWheel,
MouseMove,
MouseMoveRelative,
MouseLeave,
TouchDown,
TouchMove,
@@ -54,6 +55,11 @@ public:
Float2 Position;
} MouseData;
struct
{
Float2 PositionRelative;
} MouseMovementData;
struct
{
float WheelDelta;

View File

@@ -46,12 +46,14 @@ public:
protected:
State _state;
State _prevState;
bool _relativeMode;
explicit Mouse()
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse"))
{
_state.Clear();
_prevState.Clear();
_relativeMode = false;
}
public:
@@ -114,6 +116,14 @@ public:
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)];
}
/// <summary>
/// Gets the current state of mouse relative mode.
/// </summary>
API_FUNCTION() FORCE_INLINE bool IsRelative() const
{
return _relativeMode;
}
public:
/// <summary>
/// Sets the mouse position.
@@ -121,6 +131,16 @@ public:
/// <param name="newPosition">The new position.</param>
virtual void SetMousePosition(const Float2& newPosition) = 0;
/// <summary>
/// Sets the mouse relative mode state. While enabled, the mouse movement tracking becomes more accurate.
/// The cursor will be hidden while in relative mode.
/// </summary>
/// <param name="relativeMode">The new relative mode state.</param>
virtual void SetRelativeMode(bool relativeMode)
{
_relativeMode = relativeMode;
}
/// <summary>
/// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programmatically.
/// </summary>
@@ -158,6 +178,13 @@ public:
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseMove(const Float2& position, Window* target = nullptr);
/// <summary>
/// Called when mouse moves in relative mode.
/// </summary>
/// <param name="positionRelative">The mouse position change.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseMoveRelative(const Float2& positionRelative, Window* target = nullptr);
/// <summary>
/// Called when mouse leaves the input source area.
/// </summary>

View File

@@ -0,0 +1,251 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Config.h"
/// <summary>
/// Window closing reasons.
/// </summary>
API_ENUM() enum class ClosingReason
{
/// <summary>
/// The unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// The user.
/// </summary>
User,
/// <summary>
/// The engine exit.
/// </summary>
EngineExit,
/// <summary>
/// The close event.
/// </summary>
CloseEvent,
};
/// <summary>
/// Types of default cursors.
/// </summary>
API_ENUM() enum class CursorType
{
/// <summary>
/// The default.
/// </summary>
Default = 0,
/// <summary>
/// The cross.
/// </summary>
Cross,
/// <summary>
/// The hand.
/// </summary>
Hand,
/// <summary>
/// The help icon
/// </summary>
Help,
/// <summary>
/// The I beam.
/// </summary>
IBeam,
/// <summary>
/// The blocking image.
/// </summary>
No,
/// <summary>
/// The wait.
/// </summary>
Wait,
/// <summary>
/// The size all sides.
/// </summary>
SizeAll,
/// <summary>
/// The size NE-SW.
/// </summary>
SizeNESW,
/// <summary>
/// The size NS.
/// </summary>
SizeNS,
/// <summary>
/// The size NW-SE.
/// </summary>
SizeNWSE,
/// <summary>
/// The size WE.
/// </summary>
SizeWE,
/// <summary>
/// The cursor is hidden.
/// </summary>
Hidden,
MAX
};
/// <summary>
/// Data drag and drop effects.
/// </summary>
API_ENUM() enum class DragDropEffect
{
/// <summary>
/// The none.
/// </summary>
None = 0,
/// <summary>
/// The copy.
/// </summary>
Copy,
/// <summary>
/// The move.
/// </summary>
Move,
/// <summary>
/// The link.
/// </summary>
Link,
};
/// <summary>
/// Window hit test codes. Note: they are 1:1 mapping for Win32 values.
/// </summary>
API_ENUM() enum class WindowHitCodes
{
/// <summary>
/// The transparent area.
/// </summary>
Transparent = -1,
/// <summary>
/// The no hit.
/// </summary>
NoWhere = 0,
/// <summary>
/// The client area.
/// </summary>
Client = 1,
/// <summary>
/// The caption area.
/// </summary>
Caption = 2,
/// <summary>
/// The system menu.
/// </summary>
SystemMenu = 3,
/// <summary>
/// The grow box
/// </summary>
GrowBox = 4,
/// <summary>
/// The menu.
/// </summary>
Menu = 5,
/// <summary>
/// The horizontal scroll.
/// </summary>
HScroll = 6,
/// <summary>
/// The vertical scroll.
/// </summary>
VScroll = 7,
/// <summary>
/// The minimize button.
/// </summary>
MinButton = 8,
/// <summary>
/// The maximize button.
/// </summary>
MaxButton = 9,
/// <summary>
/// The left side;
/// </summary>
Left = 10,
/// <summary>
/// The right side.
/// </summary>
Right = 11,
/// <summary>
/// The top side.
/// </summary>
Top = 12,
/// <summary>
/// The top left corner.
/// </summary>
TopLeft = 13,
/// <summary>
/// The top right corner.
/// </summary>
TopRight = 14,
/// <summary>
/// The bottom side.
/// </summary>
Bottom = 15,
/// <summary>
/// The bottom left corner.
/// </summary>
BottomLeft = 16,
/// <summary>
/// The bottom right corner.
/// </summary>
BottomRight = 17,
/// <summary>
/// The border.
/// </summary>
Border = 18,
/// <summary>
/// The object.
/// </summary>
Object = 19,
/// <summary>
/// The close button.
/// </summary>
Close = 20,
/// <summary>
/// The help button.
/// </summary>
Help = 21,
};

View File

@@ -45,6 +45,7 @@ static_assert(sizeof(double) == 8, "Invalid double type size.");
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two.");
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0, "Threads limit must be multiple of 4.");
const Char* PlatformBase::ApplicationClassName = TEXT("FlaxWindow");
float PlatformBase::CustomDpiScale = 1.0f;
Array<User*, FixedAllocation<8>> PlatformBase::Users;
Delegate<User*> PlatformBase::UserAdded;

View File

@@ -166,6 +166,13 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(PlatformBase);
/// </summary>
static void Exit();
public:
/// <summary>
/// Application windows class name.
/// </summary>
static const Char* ApplicationClassName;
public:
/// <summary>
/// Copy memory region

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
API_INJECT_CODE(cpp, "#include \"Engine/Platform/ScreenUtilities.h\"");
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
API_CLASS(Static, Name="ScreenUtilities", Tag="NativeInvokeUseName")
class FLAXENGINE_API ScreenUtilitiesBase
{
public:
static struct FLAXENGINE_API ScriptingTypeInitializer TypeInitializer;
/// <summary>
/// Gets the pixel color at the specified coordinates.
/// </summary>
/// <param name="pos">Screen-space coordinate to read.</param>
/// <returns>Pixel color at the specified coordinates.</returns>
API_FUNCTION() static Color32 GetColorAt(const Float2& pos)
{
return Color32::Transparent;
}
/// <summary>
/// Starts async color picking. Color will be returned through PickColorDone event when the actions ends (user selected the final color with a mouse). When action is active, GetColorAt can be used to read the current value.
/// </summary>
API_FUNCTION() static void PickColor()
{
}
/// <summary>
/// Called when PickColor action is finished.
/// </summary>
API_EVENT() static Delegate<Color32> PickColorDone;
};

View File

@@ -106,7 +106,7 @@ WindowBase::WindowBase(const CreateWindowSettings& settings)
if (settings.StartPosition == WindowStartPosition::CenterParent
|| settings.StartPosition == WindowStartPosition::CenterScreen)
{
Rectangle parentBounds = Rectangle(Float2::Zero, Platform::GetDesktopSize());
Rectangle parentBounds = Platform::GetMonitorBounds(Float2::Minimum);
if (settings.Parent != nullptr && settings.StartPosition == WindowStartPosition::CenterParent)
parentBounds = settings.Parent->GetClientBounds();
@@ -257,6 +257,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition)
INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&mousePosition);
}
void WindowBase::OnMouseMoveRelative(const Float2& mousePositionRelative)
{
PROFILE_CPU_NAMED("GUI.OnMouseMoveRelative");
MouseMoveRelative(mousePositionRelative);
INVOKE_EVENT_PARAMS_1(OnMouseMoveRelative, (void*)&mousePositionRelative);
}
void WindowBase::OnMouseLeave()
{
PROFILE_CPU_NAMED("GUI.OnMouseLeave");

View File

@@ -8,6 +8,7 @@
#include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Input/KeyboardKeys.h"
#include "Engine/Input/Enums.h"
#include "Enums.h"
class Input;
class Engine;
@@ -17,252 +18,6 @@ class GPUSwapChain;
class TextureData;
class IGuiData;
/// <summary>
/// Window closing reasons.
/// </summary>
API_ENUM() enum class ClosingReason
{
/// <summary>
/// The unknown.
/// </summary>
Unknown = 0,
/// <summary>
/// The user.
/// </summary>
User,
/// <summary>
/// The engine exit.
/// </summary>
EngineExit,
/// <summary>
/// The close event.
/// </summary>
CloseEvent,
};
/// <summary>
/// Types of default cursors.
/// </summary>
API_ENUM() enum class CursorType
{
/// <summary>
/// The default.
/// </summary>
Default = 0,
/// <summary>
/// The cross.
/// </summary>
Cross,
/// <summary>
/// The hand.
/// </summary>
Hand,
/// <summary>
/// The help icon
/// </summary>
Help,
/// <summary>
/// The I beam.
/// </summary>
IBeam,
/// <summary>
/// The blocking image.
/// </summary>
No,
/// <summary>
/// The wait.
/// </summary>
Wait,
/// <summary>
/// The size all sides.
/// </summary>
SizeAll,
/// <summary>
/// The size NE-SW.
/// </summary>
SizeNESW,
/// <summary>
/// The size NS.
/// </summary>
SizeNS,
/// <summary>
/// The size NW-SE.
/// </summary>
SizeNWSE,
/// <summary>
/// The size WE.
/// </summary>
SizeWE,
/// <summary>
/// The cursor is hidden.
/// </summary>
Hidden,
MAX
};
/// <summary>
/// Data drag and drop effects.
/// </summary>
API_ENUM() enum class DragDropEffect
{
/// <summary>
/// The none.
/// </summary>
None = 0,
/// <summary>
/// The copy.
/// </summary>
Copy,
/// <summary>
/// The move.
/// </summary>
Move,
/// <summary>
/// The link.
/// </summary>
Link,
};
/// <summary>
/// Window hit test codes. Note: they are 1:1 mapping for Win32 values.
/// </summary>
API_ENUM() enum class WindowHitCodes
{
/// <summary>
/// The transparent area.
/// </summary>
Transparent = -1,
/// <summary>
/// The no hit.
/// </summary>
NoWhere = 0,
/// <summary>
/// The client area.
/// </summary>
Client = 1,
/// <summary>
/// The caption area.
/// </summary>
Caption = 2,
/// <summary>
/// The system menu.
/// </summary>
SystemMenu = 3,
/// <summary>
/// The grow box
/// </summary>
GrowBox = 4,
/// <summary>
/// The menu.
/// </summary>
Menu = 5,
/// <summary>
/// The horizontal scroll.
/// </summary>
HScroll = 6,
/// <summary>
/// The vertical scroll.
/// </summary>
VScroll = 7,
/// <summary>
/// The minimize button.
/// </summary>
MinButton = 8,
/// <summary>
/// The maximize button.
/// </summary>
MaxButton = 9,
/// <summary>
/// The left side;
/// </summary>
Left = 10,
/// <summary>
/// The right side.
/// </summary>
Right = 11,
/// <summary>
/// The top side.
/// </summary>
Top = 12,
/// <summary>
/// The top left corner.
/// </summary>
TopLeft = 13,
/// <summary>
/// The top right corner.
/// </summary>
TopRight = 14,
/// <summary>
/// The bottom side.
/// </summary>
Bottom = 15,
/// <summary>
/// The bottom left corner.
/// </summary>
BottomLeft = 16,
/// <summary>
/// The bottom right corner.
/// </summary>
BottomRight = 17,
/// <summary>
/// The border.
/// </summary>
Border = 18,
/// <summary>
/// The object.
/// </summary>
Object = 19,
/// <summary>
/// The close button.
/// </summary>
Close = 20,
/// <summary>
/// The help button.
/// </summary>
Help = 21,
};
API_INJECT_CODE(cpp, "#include \"Engine/Platform/Window.h\"");
/// <summary>
@@ -837,6 +592,12 @@ public:
MouseDelegate MouseMove;
void OnMouseMove(const Float2& mousePosition);
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
MouseDelegate MouseMoveRelative;
void OnMouseMoveRelative(const Float2& mousePositionRelative);
/// <summary>
/// Event fired when mouse leaves window.
/// </summary>

View File

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

View File

@@ -21,7 +21,7 @@ namespace FlaxEngine
AllowMinimize = true,
AllowMaximize = true,
AllowDragAndDrop = true,
IsRegularWindow = true,
Type = WindowType.Regular,
HasSizingFrame = true,
ShowAfterFirstPaint = true,
};

View File

@@ -26,6 +26,32 @@ API_ENUM() enum class WindowStartPosition
Manual,
};
/// <summary>
/// Specifies the type of the window.
/// </summary>
API_ENUM() enum class WindowType
{
/// <summary>
/// Regular window.
/// </summary>
Regular,
/// <summary>
/// Utility window.
/// </summary>
Utility,
/// <summary>
/// Tooltip window.
/// </summary>
Tooltip,
/// <summary>
/// Popup window.
/// </summary>
Popup,
};
/// <summary>
/// Settings for new window.
/// </summary>
@@ -119,9 +145,15 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings);
API_FIELD() bool IsTopmost = false;
/// <summary>
/// True if it's a regular window, false for tooltips, contextmenu and other utility windows.
/// True if it's a regular window, false for tooltips, context menu and other utility windows.
/// </summary>
API_FIELD() bool IsRegularWindow = true;
API_FIELD() DEPRECATED bool IsRegularWindow = true;
/// <summary>
/// The type of window. The type affects the behaviour of the window in system level.
/// Note: Tooltip and Popup windows require Parent to be set.
/// </summary>
API_FIELD() WindowType Type = WindowType::Regular;
/// <summary>
/// Enable/disable window sizing frame.

View File

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

View File

@@ -30,7 +30,6 @@ inline bool operator==(const APP_LOCAL_DEVICE_ID& l, const APP_LOCAL_DEVICE_ID&
return Platform::MemoryCompare(&l, &r, sizeof(APP_LOCAL_DEVICE_ID)) == 0;
}
const Char* GDKPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
void* GDKPlatform::Instance = nullptr;
Delegate<> GDKPlatform::Suspended;
Delegate<> GDKPlatform::Resumed;
@@ -316,7 +315,7 @@ void GDKPlatform::PreInit(void* hInstance)
windowsClass.style = CS_HREDRAW | CS_VREDRAW;
windowsClass.lpfnWndProc = WndProc;
windowsClass.hInstance = (HINSTANCE)Instance;
windowsClass.lpszClassName = ApplicationWindowClass;
windowsClass.lpszClassName = ApplicationClassName;
if (!RegisterClassW(&windowsClass))
{
Error(TEXT("Window class registration failed!"));
@@ -474,7 +473,7 @@ void GDKPlatform::Exit()
if (PlmSignalResume)
CloseHandle(PlmSignalResume);
UnregisterClassW(ApplicationWindowClass, nullptr);
UnregisterClassW(ApplicationClassName, nullptr);
XGameRuntimeUninitialize();
}

View File

@@ -13,11 +13,6 @@ class FLAXENGINE_API GDKPlatform : public Win32Platform
{
public:
/// <summary>
/// Win32 application windows class name.
/// </summary>
static const Char* ApplicationWindowClass;
/// <summary>
/// Handle to Win32 application instance.
/// </summary>

View File

@@ -71,7 +71,7 @@ GDKWindow::GDKWindow(const CreateWindowSettings& settings)
// Creating the window
_handle = CreateWindowExW(
exStyle,
Platform::ApplicationWindowClass,
Platform::ApplicationClassName,
settings.Title.GetText(),
style,
x,

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if USE_EDITOR && PLATFORM_LINUX
#include "Engine/Platform/Types.h"
#include "Engine/Platform/ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Platform/Linux/LinuxPlatform.h"
#include "Engine/Platform/Linux/IncludeX11.h"
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
Color32 LinuxScreenUtilities::GetColorAt(const Float2& pos)
{
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
if (display)
{
int defaultScreen = X11::XDefaultScreen(display);
X11::Window rootWindow = X11::XRootWindow(display, defaultScreen);
X11::XImage* image = X11::XGetImage(display, rootWindow, (int)pos.X, (int)pos.Y, 1, 1, AllPlanes, XYPixmap);
if (image)
{
X11::XColor color;
color.pixel = XGetPixel(image, 0, 0);
X11::XFree(image);
X11::XQueryColor(display, X11::XDefaultColormap(display, defaultScreen), &color);
Color32 outputColor;
outputColor.R = color.red / 256;
outputColor.G = color.green / 256;
outputColor.B = color.blue / 256;
outputColor.A = 255;
return outputColor;
}
else
{
// XWayland doesn't support XGetImage...
// TODO: Fallback to Wayland implementation here?
return Color32::Black;
}
}
else
{
// TODO: Wayland
ASSERT(false);
}
}
void OnScreenUtilsXEventCallback(void* eventPtr)
{
X11::XEvent* event = (X11::XEvent*) eventPtr;
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
if (event->type == ButtonPress)
{
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
X11::XUngrabPointer(display, CurrentTime);
ScreenUtilities::PickColorDone(colorPicked);
LinuxPlatform::xEventReceived.Unbind(OnScreenUtilsXEventCallback);
}
}
void LinuxScreenUtilities::PickColor()
{
PROFILE_CPU();
X11::Display* display = (X11::Display*)Platform::GetXDisplay();
if (display)
{
X11::Window rootWindow = X11::XRootWindow(display, X11::XDefaultScreen(display));
X11::Cursor cursor = XCreateFontCursor(display, 130);
int grabbedPointer = X11::XGrabPointer(display, rootWindow, 0, ButtonPressMask, GrabModeAsync, GrabModeAsync, rootWindow, cursor, CurrentTime);
if (grabbedPointer != GrabSuccess)
{
LOG(Error, "Failed to grab cursor for events.");
X11::XFreeCursor(display, cursor);
return;
}
X11::XFreeCursor(display, cursor);
LinuxPlatform::xEventReceived.Bind(OnScreenUtilsXEventCallback);
}
else
{
// TODO: Wayland
ASSERT(false);
}
}
#endif

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR && PLATFORM_LINUX
#include "Engine/Platform/Base/ScreenUtilitiesBase.h"
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
class FLAXENGINE_API LinuxScreenUtilities : public ScreenUtilitiesBase
{
public:
// [ScreenUtilitiesBase]
static Color32 GetColorAt(const Float2& pos);
static void PickColor();
};
#endif

View File

@@ -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"
@@ -617,7 +617,7 @@ void LinuxWindow::OnButtonPress(void* event)
}
// Handle double-click
if (buttonEvent->button == Button1)
if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative())
{
if (
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if USE_EDITOR && PLATFORM_MAC
#include "Engine/Platform/Types.h"
#include "Engine/Platform/ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include <Cocoa/Cocoa.h>
#include <AppKit/AppKit.h>
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
Color32 MacScreenUtilities::GetColorAt(const Float2& pos)
{
// TODO: implement ScreenUtilities for macOS
return { 0, 0, 0, 255 };
}
void MacScreenUtilities::PickColor()
{
// This is what C# calls to start the color picking sequence
// This should stop mouse clicks from working for one click, and that click is on the selected color
// There is a class called NSColorSample that might implement that for you, but maybe not.
}
#endif

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR && PLATFORM_MAC
#include "Engine/Platform/Base/ScreenUtilitiesBase.h"
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
class FLAXENGINE_API MacScreenUtilities : public ScreenUtilitiesBase
{
public:
// [ScreenUtilitiesBase]
static Color32 GetColorAt(const Float2& pos);
static void PickColor();
};
#endif

View File

@@ -507,7 +507,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
Float2 mousePos = GetMousePosition(Window, event);
mousePos = Window->ClientToScreen(mousePos);
MouseButton mouseButton = MouseButton::Left;
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(mousePos, mouseButton, Window);
else
Input::Mouse->OnMouseDown(mousePos, mouseButton, Window);
@@ -544,7 +544,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
if (IsWindowInvalid(Window)) return;
Float2 mousePos = GetMousePosition(Window, event);
MouseButton mouseButton = MouseButton::Right;
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);
@@ -582,7 +582,7 @@ static void ConvertNSRect(NSScreen *screen, NSRect *r)
default:
return;
}
if ([event clickCount] == 2)
if ([event clickCount] == 2 && !Input::Mouse->IsRelative())
Input::Mouse->OnMouseDoubleClick(Window->ClientToScreen(mousePos), mouseButton, Window);
else
Input::Mouse->OnMouseDown(Window->ClientToScreen(mousePos), mouseButton, Window);

View File

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

View File

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

View 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

View File

@@ -0,0 +1,785 @@
// 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:
{
if (Input::Mouse->IsRelative())
{
const Float2 mouseDelta(event.motion.xrel, event.motion.yrel);
Input::Mouse->OnMouseMoveRelative(mouseDelta, window);
}
else
{
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:
{
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 (Input::Mouse->IsRelative())
{
// Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition();
}
if (event.button.state == SDL_RELEASED)
Input::Mouse->OnMouseUp(mousePos, button, window);
// Prevent sending multiple mouse down event when double-clicking UI elements
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:
{
Float2 mousePos = window->ClientToScreen({ event.wheel.mouse_x, event.wheel.mouse_y });
const float delta = event.wheel.y;
if (Input::Mouse->IsRelative())
{
// Use the previous visible mouse position here, the event or global
// mouse position would cause input to trigger in other editor windows.
mousePos = SDLInputImpl::Mouse->GetMousePosition();
}
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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,487 @@
// 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()));
LOG(Info, "SDL video driver: {}", String(SDL_GetCurrentVideoDriver()));
}
bool SDLPlatform::CheckWindowDragging(Window* window, WindowHitCodes hit)
{
bool handled = false;
window->OnLeftButtonHit(hit, handled);
if (handled)
{
DraggedWindowId = window->_windowId;
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

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_WINDOWS
#include "Windows/WindowsScreenUtilities.h"
#elif PLATFORM_LINUX
#include "Linux/LinuxScreenUtilities.h"
#elif PLATFORM_MAC
#include "Mac/MacScreenUtilities.h"
#else
#include "Base/ScreenUtilitiesBase.h"
#endif
#include "Types.h"

View File

@@ -4,8 +4,6 @@
#if PLATFORM_WINDOWS
class WindowsClipboard;
typedef WindowsClipboard Clipboard;
class Win32CriticalSection;
typedef Win32CriticalSection CriticalSection;
class Win32ConditionVariable;
@@ -16,21 +14,25 @@ class WindowsFileSystemWatcher;
typedef WindowsFileSystemWatcher FileSystemWatcher;
class Win32File;
typedef Win32File File;
class WindowsPlatform;
typedef WindowsPlatform Platform;
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;
typedef UserBase User;
class WindowsScreenUtilities;
typedef WindowsScreenUtilities ScreenUtilities;
#elif PLATFORM_UWP
class ClipboardBase;
typedef ClipboardBase Clipboard;
class Win32CriticalSection;
typedef Win32CriticalSection CriticalSection;
class Win32ConditionVariable;
@@ -41,10 +43,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class Win32File;
typedef Win32File File;
class UWPPlatform;
typedef UWPPlatform Platform;
class Win32Thread;
typedef Win32Thread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UWPPlatform;
typedef UWPPlatform Platform;
class UWPWindow;
typedef UWPWindow Window;
class Win32Network;
@@ -54,8 +58,6 @@ typedef UserBase User;
#elif PLATFORM_LINUX
class LinuxClipboard;
typedef LinuxClipboard Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -66,21 +68,25 @@ class LinuxFileSystemWatcher;
typedef LinuxFileSystemWatcher FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class LinuxPlatform;
typedef LinuxPlatform Platform;
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;
typedef UserBase User;
class LinuxScreenUtilities;
typedef LinuxScreenUtilities ScreenUtilities;
#elif PLATFORM_PS4
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -91,10 +97,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class PS4Platform;
typedef PS4Platform Platform;
class PS4Thread;
typedef PS4Thread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class PS4Platform;
typedef PS4Platform Platform;
class PS4Window;
typedef PS4Window Window;
class PS4Network;
@@ -104,8 +112,6 @@ typedef PS4User User;
#elif PLATFORM_PS5
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -116,10 +122,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class PS5Platform;
typedef PS5Platform Platform;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class PS5Thread;
typedef PS5Thread Thread;
class PS5Platform;
typedef PS5Platform Platform;
class PS5Window;
typedef PS5Window Window;
class PS5Network;
@@ -129,8 +137,6 @@ typedef PS5User User;
#elif PLATFORM_XBOX_ONE
class ClipboardBase;
typedef ClipboardBase Clipboard;
class Win32CriticalSection;
typedef Win32CriticalSection CriticalSection;
class Win32ConditionVariable;
@@ -141,10 +147,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class Win32File;
typedef Win32File File;
class XboxOnePlatform;
typedef XboxOnePlatform Platform;
class Win32Thread;
typedef Win32Thread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class XboxOnePlatform;
typedef XboxOnePlatform Platform;
class GDKWindow;
typedef GDKWindow Window;
class Win32Network;
@@ -154,8 +162,6 @@ typedef GDKUser User;
#elif PLATFORM_XBOX_SCARLETT
class ClipboardBase;
typedef ClipboardBase Clipboard;
class Win32CriticalSection;
typedef Win32CriticalSection CriticalSection;
class Win32ConditionVariable;
@@ -166,10 +172,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class Win32File;
typedef Win32File File;
class XboxScarlettPlatform;
typedef XboxScarlettPlatform Platform;
class Win32Thread;
typedef Win32Thread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class XboxScarlettPlatform;
typedef XboxScarlettPlatform Platform;
class GDKWindow;
typedef GDKWindow Window;
class Win32Network;
@@ -179,8 +187,6 @@ typedef GDKUser User;
#elif PLATFORM_ANDROID
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -191,10 +197,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class AndroidFile;
typedef AndroidFile File;
class AndroidPlatform;
typedef AndroidPlatform Platform;
class AndroidThread;
typedef AndroidThread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class AndroidPlatform;
typedef AndroidPlatform Platform;
class AndroidWindow;
typedef AndroidWindow Window;
class UnixNetwork;
@@ -204,8 +212,6 @@ typedef UserBase User;
#elif PLATFORM_SWITCH
class ClipboardBase;
typedef ClipboardBase Clipboard;
class SwitchCriticalSection;
typedef SwitchCriticalSection CriticalSection;
class SwitchConditionVariable;
@@ -216,10 +222,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class SwitchFile;
typedef SwitchFile File;
class SwitchPlatform;
typedef SwitchPlatform Platform;
class SwitchThread;
typedef SwitchThread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class SwitchPlatform;
typedef SwitchPlatform Platform;
class SwitchWindow;
typedef SwitchWindow Window;
class SwitchNetwork;
@@ -229,8 +237,6 @@ typedef SwitchUser User;
#elif PLATFORM_MAC
class MacClipboard;
typedef MacClipboard Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -241,21 +247,23 @@ class MacFileSystemWatcher;
typedef MacFileSystemWatcher FileSystemWatcher;
class UnixFile;
typedef UnixFile File;
class MacPlatform;
typedef MacPlatform Platform;
class AppleThread;
typedef AppleThread Thread;
class MacClipboard;
typedef MacClipboard Clipboard;
class MacPlatform;
typedef MacPlatform Platform;
class MacWindow;
typedef MacWindow Window;
class UnixNetwork;
typedef UnixNetwork Network;
class UserBase;
typedef UserBase User;
class MacScreenUtilities;
typedef MacScreenUtilities ScreenUtilities;
#elif PLATFORM_IOS
class ClipboardBase;
typedef ClipboardBase Clipboard;
class UnixCriticalSection;
typedef UnixCriticalSection CriticalSection;
class UnixConditionVariable;
@@ -266,10 +274,12 @@ class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class iOSFile;
typedef iOSFile File;
class iOSPlatform;
typedef iOSPlatform Platform;
class AppleThread;
typedef AppleThread Thread;
class ClipboardBase;
typedef ClipboardBase Clipboard;
class iOSPlatform;
typedef iOSPlatform Platform;
class iOSWindow;
typedef iOSWindow Window;
class UnixNetwork;
@@ -282,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

View File

@@ -17,31 +17,37 @@ namespace FlaxEngine
/// <summary>
/// Perform window hit test delegate.
/// </summary>
/// <param name="mouse">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
/// <param name="mousePosition">The mouse position. The coordinate is relative to the upper-left corner of the screen. Use <see cref="ScreenToClient"/> to convert position into client space coordinates.</param>
/// <returns>Hit result.</returns>
public delegate WindowHitCodes HitTestDelegate(ref Float2 mouse);
public delegate WindowHitCodes HitTestDelegate(ref Float2 mousePosition);
/// <summary>
/// Perform mouse buttons action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
/// <param name="mousePosition">The mouse position.</param>
/// <param name="button">The mouse buttons state.</param>
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
public delegate void MouseButtonDelegate(ref Float2 mouse, MouseButton button, ref bool handled);
public delegate void MouseButtonDelegate(ref Float2 mousePosition, MouseButton button, ref bool handled);
/// <summary>
/// Perform mouse move action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
public delegate void MouseMoveDelegate(ref Float2 mouse);
/// <param name="mousePosition">The mouse position.</param>
public delegate void MouseMoveDelegate(ref Float2 mousePosition);
/// <summary>
/// Perform mouse move action in relative mode.
/// </summary>
/// <param name="mouseMotion">The relative mouse motion.</param>
public delegate void MouseMoveRelativeDelegate(ref Float2 mouseMotion);
/// <summary>
/// Perform mouse wheel action.
/// </summary>
/// <param name="mouse">The mouse position.</param>
/// <param name="mousePosition">The mouse position.</param>
/// <param name="delta">The mouse wheel move delta (can be positive or negative; normalized to [-1;1] range).</param>
/// <param name="handled">The flag that indicated that event has been handled by the custom code and should not be passed further. By default it is set to false.</param>
public delegate void MouseWheelDelegate(ref Float2 mouse, float delta, ref bool handled);
public delegate void MouseWheelDelegate(ref Float2 mousePosition, float delta, ref bool handled);
/// <summary>
/// Perform touch action.
@@ -99,9 +105,14 @@ namespace FlaxEngine
public event MouseWheelDelegate MouseWheel;
/// <summary>
/// Event fired when mouse moves
/// Event fired when mouse moves.
/// </summary>
public event MouseMoveDelegate MouseMove;
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
public event MouseMoveRelativeDelegate MouseMoveRelative;
/// <summary>
/// Event fired when mouse leaves window.
@@ -273,6 +284,12 @@ namespace FlaxEngine
MouseMove?.Invoke(ref pos);
GUI.OnMouseMove(pos);
}
internal void Internal_OnMouseMoveRelative(ref Float2 mouseMotion)
{
MouseMoveRelative?.Invoke(ref mouseMotion);
GUI.OnMouseMoveRelative(mouseMotion);
}
internal void Internal_OnMouseLeave()
{

View File

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

View File

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

View File

@@ -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"
@@ -265,19 +265,38 @@ bool WindowsMouse::WndProc(Window* window, const UINT msg, WPARAM wParam, LPARAM
}
case WM_LBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Left, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Left, window);
else
OnMouseDown(mousePos, MouseButton::Left, window);
result = true;
break;
}
case WM_RBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Right, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Right, window);
else
OnMouseDown(mousePos, MouseButton::Right, window);
result = true;
break;
}
case WM_MBUTTONDBLCLK:
{
OnMouseDoubleClick(mousePos, MouseButton::Middle, window);
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, MouseButton::Middle, window);
else
OnMouseDown(mousePos, MouseButton::Middle, window);
result = true;
break;
}
case WM_XBUTTONDBLCLK:
{
const auto button = (HIWORD(wParam) & XBUTTON1) ? MouseButton::Extended1 : MouseButton::Extended2;
if (!Input::Mouse->IsRelative())
OnMouseDoubleClick(mousePos, button, window);
else
OnMouseDown(mousePos, button, window);
result = true;
break;
}

View File

@@ -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"
@@ -33,7 +35,6 @@
#define CLR_EXCEPTION 0xE0434352
#define VCPP_EXCEPTION 0xE06D7363
const Char* WindowsPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
void* WindowsPlatform::Instance = nullptr;
#if CRASH_LOG_ENABLE || TRACY_ENABLE
@@ -256,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
@@ -273,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)
@@ -437,11 +442,12 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
flags |= MB_ICONHAND;
break;
case MessageBoxIcon::Information:
case MessageBoxIcon::Question:
flags |= MB_ICONINFORMATION;
break;
case MessageBoxIcon::Question:
flags |= MB_ICONQUESTION;
break;
//case MessageBoxIcon::Question:
// flags |= MB_ICONQUESTION;
// break;
case MessageBoxIcon::Stop:
flags |= MB_ICONSTOP;
break;
@@ -519,6 +525,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));
@@ -527,12 +534,13 @@ void WindowsPlatform::PreInit(void* hInstance)
windowsClass.hInstance = (HINSTANCE)Instance;
windowsClass.hIcon = LoadIconW(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDR_MAINFRAME));
windowsClass.hCursor = LoadCursor(nullptr, IDC_ARROW);
windowsClass.lpszClassName = ApplicationWindowClass;
windowsClass.lpszClassName = ApplicationClassName;
if (!RegisterClassW(&windowsClass))
{
Error(TEXT("Window class registration failed!"));
exit(-1);
}
#endif
// Init OLE
if (OleInitialize(nullptr) != S_OK)
@@ -681,7 +689,9 @@ bool WindowsPlatform::Init()
}
OnPlatformUserAdd(New<User>(userName));
#if !PLATFORM_SDL
WindowsInput::Init();
#endif
return false;
}
@@ -709,7 +719,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;
@@ -740,8 +752,10 @@ void WindowsPlatform::Exit()
DbgHelpUnlock();
#endif
#if !PLATFORM_SDL
// Unregister app class
UnregisterClassW(ApplicationWindowClass, nullptr);
UnregisterClassW(ApplicationClassName, nullptr);
#endif
Win32Platform::Exit();
}
@@ -1186,11 +1200,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);

View File

@@ -13,11 +13,6 @@ class FLAXENGINE_API WindowsPlatform : public Win32Platform
{
public:
/// <summary>
/// Win32 application windows class name.
/// </summary>
static const Char* ApplicationWindowClass;
/// <summary>
/// Handle to Win32 application instance.
/// </summary>

View File

@@ -0,0 +1,54 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if USE_EDITOR && PLATFORM_WINDOWS
#include "Engine/Platform/Types.h"
#include "Engine/Platform/ScreenUtilities.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Log.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include <Windows.h>
#pragma comment(lib, "Gdi32.lib")
Delegate<Color32> ScreenUtilitiesBase::PickColorDone;
static HHOOK MouseCallbackHook;
LRESULT CALLBACK OnScreenUtilsMouseCallback(_In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (nCode >= 0 && wParam == WM_LBUTTONDOWN)
{
UnhookWindowsHookEx(MouseCallbackHook);
// Push event with the picked color
const Float2 cursorPos = Platform::GetMousePosition();
const Color32 colorPicked = ScreenUtilities::GetColorAt(cursorPos);
ScreenUtilities::PickColorDone(colorPicked);
return 1;
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
Color32 WindowsScreenUtilities::GetColorAt(const Float2& pos)
{
PROFILE_CPU();
HDC deviceContext = GetDC(NULL);
COLORREF color = GetPixel(deviceContext, (int)pos.X, (int)pos.Y);
ReleaseDC(NULL, deviceContext);
return Color32(GetRValue(color), GetGValue(color), GetBValue(color), 255);
}
void WindowsScreenUtilities::PickColor()
{
MouseCallbackHook = SetWindowsHookEx(WH_MOUSE_LL, OnScreenUtilsMouseCallback, NULL, NULL);
if (MouseCallbackHook == NULL)
{
LOG(Warning, "Failed to set mouse hook.");
LOG(Warning, "Error: {0}", GetLastError());
}
}
#endif

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR && PLATFORM_WINDOWS
#include "Engine/Platform/Base/ScreenUtilitiesBase.h"
#include "Engine/Core/Types/BaseTypes.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Delegate.h"
/// <summary>
/// Platform-dependent screen utilities.
/// </summary>
class FLAXENGINE_API WindowsScreenUtilities : public ScreenUtilitiesBase
{
public:
// [ScreenUtilitiesBase]
static Color32 GetColorAt(const Float2& pos);
static void PickColor();
};
#endif

View File

@@ -0,0 +1,661 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if PLATFORM_WINDOWS
#if PLATFORM_SDL
#include "Engine/Platform/SDL/SDLWindow.h"
#endif
#include "Engine/Platform/Windows/WindowsWindow.h"
#if USE_EDITOR
#include "Engine/Core/Collections/Array.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Platform/Base/DragDropHelper.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#endif
#include "../Win32/IncludeWindowsHeaders.h"
#include <propidl.h>
#if USE_EDITOR
#include <oleidl.h>
#include <shellapi.h>
#endif
#if USE_EDITOR
Windows::HRESULT Window::QueryInterface(const Windows::IID& id, void** ppvObject)
{
// Check to see what interface has been requested
if ((const IID&)id == IID_IUnknown || (const IID&)id == IID_IDropTarget)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
Windows::ULONG Window::AddRef()
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
Windows::ULONG Window::Release()
{
return _InterlockedDecrement(&_refCount);
}
HGLOBAL duplicateGlobalMem(HGLOBAL hMem)
{
auto len = GlobalSize(hMem);
auto source = GlobalLock(hMem);
auto dest = GlobalAlloc(GMEM_FIXED, len);
Platform::MemoryCopy(dest, source, len);
GlobalUnlock(hMem);
return dest;
}
DWORD dropEffect2OleEnum(DragDropEffect effect)
{
DWORD result;
switch (effect)
{
case DragDropEffect::None:
result = DROPEFFECT_NONE;
break;
case DragDropEffect::Copy:
result = DROPEFFECT_COPY;
break;
case DragDropEffect::Move:
result = DROPEFFECT_MOVE;
break;
case DragDropEffect::Link:
result = DROPEFFECT_LINK;
break;
default:
result = DROPEFFECT_NONE;
break;
}
return result;
}
DragDropEffect dropEffectFromOleEnum(DWORD effect)
{
DragDropEffect result;
switch (effect)
{
case DROPEFFECT_NONE:
result = DragDropEffect::None;
break;
case DROPEFFECT_COPY:
result = DragDropEffect::Copy;
break;
case DROPEFFECT_MOVE:
result = DragDropEffect::Move;
break;
case DROPEFFECT_LINK:
result = DragDropEffect::Link;
break;
default:
result = DragDropEffect::None;
break;
}
return result;
}
HANDLE StringToHandle(const StringView& str)
{
// Allocate and lock a global memory buffer.
// Make it fixed data so we don't have to use GlobalLock
const int32 length = str.Length();
char* ptr = static_cast<char*>(GlobalAlloc(GMEM_FIXED, length + 1));
// Copy the string into the buffer as ANSI text
StringUtils::ConvertUTF162ANSI(str.Get(), ptr, length);
ptr[length] = '\0';
return ptr;
}
void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source)
{
// Copy the source FORMATETC into dest
*dest = *source;
if (source->ptd)
{
// Allocate memory for the DVTARGETDEVICE if necessary
dest->ptd = static_cast<DVTARGETDEVICE*>(CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
// Copy the contents of the source DVTARGETDEVICE into dest->ptd
*(dest->ptd) = *(source->ptd);
}
}
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc);
/// <summary>
/// GUI data for Windows platform
/// </summary>
class WindowsGuiData : public IGuiData
{
private:
Type _type;
Array<String> _data;
public:
/// <summary>
/// Init
/// </summary>
WindowsGuiData()
: _type(Type::Unknown)
, _data(1)
{
}
public:
/// <summary>
/// Init from Ole IDataObject
/// </summary>
/// <param name="pDataObj">Object</param>
void Init(IDataObject* pDataObj)
{
// Temporary data
FORMATETC fmtetc;
STGMEDIUM stgmed;
// Clear
_type = Type::Unknown;
_data.Clear();
// Check type
fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Text
_type = Type::Text;
// Get data
char* text = static_cast<char*>(GlobalLock(stgmed.hGlobal));
_data.Add(String(text));
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
else
{
fmtetc = { CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Unicode Text
_type = Type::Text;
// Get data
Char* text = static_cast<Char*>(GlobalLock(stgmed.hGlobal));
_data.Add(String(text));
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
else
{
fmtetc = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Files
_type = Type::Files;
// Get data
Char item[MAX_PATH];
HDROP hdrop = static_cast<HDROP>(GlobalLock(stgmed.hGlobal));
UINT filesCount = DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0);
for (UINT i = 0; i < filesCount; i++)
{
if (DragQueryFileW(hdrop, i, item, MAX_PATH) != 0)
{
_data.Add(String(item));
}
}
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
}
}
}
public:
// [IGuiData]
Type GetType() const override
{
return _type;
}
String GetAsText() const override
{
String result;
if (_type == Type::Text)
{
result = _data[0];
}
return result;
}
void GetAsFiles(Array<String>* files) const override
{
if (_type == Type::Files)
{
files->Add(_data);
}
}
};
/// <summary>
/// Tool class for Windows Ole support
/// </summary>
class WindowsEnumFormatEtc : public IEnumFORMATETC
{
private:
ULONG _refCount;
ULONG _index;
ULONG _formatsCount;
FORMATETC* _formatEtc;
public:
WindowsEnumFormatEtc(FORMATETC* pFormatEtc, int32 nNumFormats)
: _refCount(1)
, _index(0)
, _formatsCount(nNumFormats)
, _formatEtc(nullptr)
{
// Allocate memory
_formatEtc = new FORMATETC[nNumFormats];
// Copy the FORMATETC structures
for (int32 i = 0; i < nNumFormats; i++)
{
DeepCopyFormatEtc(&_formatEtc[i], &pFormatEtc[i]);
}
}
~WindowsEnumFormatEtc()
{
if (_formatEtc)
{
for (uint32 i = 0; i < _formatsCount; i++)
{
if (_formatEtc[i].ptd)
{
CoTaskMemFree(_formatEtc[i].ptd);
}
}
delete[] _formatEtc;
}
}
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) override
{
// Check to see what interface has been requested
if (riid == IID_IEnumFORMATETC || riid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
ULONG ulRefCount = _InterlockedDecrement(&_refCount);
if (_refCount == 0)
{
delete this;
}
return ulRefCount;
}
// [IEnumFormatEtc]
HRESULT STDMETHODCALLTYPE Next(ULONG celt, FORMATETC* pFormatEtc, ULONG* pceltFetched) override
{
ULONG copied = 0;
// validate arguments
if (celt == 0 || pFormatEtc == nullptr)
return E_INVALIDARG;
// copy FORMATETC structures into caller's buffer
while (_index < _formatsCount && copied < celt)
{
DeepCopyFormatEtc(&pFormatEtc[copied], &_formatEtc[_index]);
copied++;
_index++;
}
// store result
if (pceltFetched != nullptr)
*pceltFetched = copied;
// did we copy all that was requested?
return (copied == celt) ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Skip(ULONG celt) override
{
_index += celt;
return (_index <= _formatsCount) ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Reset() override
{
_index = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Clone(IEnumFORMATETC** ppEnumFormatEtc) override
{
HRESULT result;
// Make a duplicate enumerator
result = CreateEnumFormatEtc(_formatsCount, _formatEtc, ppEnumFormatEtc);
if (result == S_OK)
{
// Manually set the index state
static_cast<WindowsEnumFormatEtc*>(*ppEnumFormatEtc)->_index = _index;
}
return result;
}
};
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc)
{
if (nNumFormats == 0 || pFormatEtc == nullptr || ppEnumFormatEtc == nullptr)
return E_INVALIDARG;
*ppEnumFormatEtc = new WindowsEnumFormatEtc(pFormatEtc, nNumFormats);
return *ppEnumFormatEtc ? S_OK : E_OUTOFMEMORY;
}
/// <summary>
/// Drag drop source and data container for Ole
/// </summary>
class WindowsDragSource : public IDataObject, public IDropSource
{
private:
ULONG _refCount;
int32 _formatsCount;
FORMATETC* _formatEtc;
STGMEDIUM* _stgMedium;
public:
WindowsDragSource(FORMATETC* fmtetc, STGMEDIUM* stgmed, int32 count)
: _refCount(1)
, _formatsCount(count)
, _formatEtc(nullptr)
, _stgMedium(nullptr)
{
// Allocate memory
_formatEtc = new FORMATETC[count];
_stgMedium = new STGMEDIUM[count];
// Copy descriptors
for (int32 i = 0; i < count; i++)
{
_formatEtc[i] = fmtetc[i];
_stgMedium[i] = stgmed[i];
}
}
virtual ~WindowsDragSource()
{
delete[] _formatEtc;
delete[] _stgMedium;
}
public:
// [IUnknown]
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR* __RPC_FAR* ppvObject) override
{
// Check to see what interface has been requested
if (riid == IID_IDataObject || riid == IID_IUnknown || riid == IID_IDropSource)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
ULONG ulRefCount = _InterlockedDecrement(&_refCount);
if (_refCount == 0)
{
delete this;
}
return ulRefCount;
}
// [IDropSource]
HRESULT STDMETHODCALLTYPE QueryContinueDrag(_In_ BOOL fEscapePressed, _In_ DWORD grfKeyState) override
{
// If the Escape key has been pressed since the last call, cancel the drop
if (fEscapePressed == TRUE || grfKeyState & MK_RBUTTON)
return DRAGDROP_S_CANCEL;
// If the LeftMouse button has been released, then do the drop!
if ((grfKeyState & MK_LBUTTON) == 0)
return DRAGDROP_S_DROP;
// Continue with the drag-drop
return S_OK;
}
HRESULT STDMETHODCALLTYPE GiveFeedback(_In_ DWORD dwEffect) override
{
// TODO: allow to use custom mouse cursor during drop and drag operation
return DRAGDROP_S_USEDEFAULTCURSORS;
}
// [IDataObject]
HRESULT STDMETHODCALLTYPE GetData(_In_ FORMATETC* pformatetcIn, _Out_ STGMEDIUM* pmedium) override
{
if (pformatetcIn == nullptr || pmedium == nullptr)
return E_INVALIDARG;
// Try to match the specified FORMATETC with one of our supported formats
int32 index = lookupFormatEtc(pformatetcIn);
if (index == INVALID_INDEX)
return DV_E_FORMATETC;
// Found a match - transfer data into supplied storage medium
pmedium->tymed = _formatEtc[index].tymed;
pmedium->pUnkForRelease = nullptr;
// Copy the data into the caller's storage medium
switch (_formatEtc[index].tymed)
{
case TYMED_HGLOBAL:
pmedium->hGlobal = duplicateGlobalMem(_stgMedium[index].hGlobal);
break;
default:
return DV_E_FORMATETC;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetDataHere(_In_ FORMATETC* pformatetc, _Inout_ STGMEDIUM* pmedium) override
{
return DATA_E_FORMATETC;
}
HRESULT STDMETHODCALLTYPE QueryGetData(__RPC__in_opt FORMATETC* pformatetc) override
{
return lookupFormatEtc(pformatetc) == INVALID_INDEX ? DV_E_FORMATETC : S_OK;
}
HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(__RPC__in_opt FORMATETC* pformatectIn, __RPC__out FORMATETC* pformatetcOut) override
{
// Apparently we have to set this field to NULL even though we don't do anything else
pformatetcOut->ptd = nullptr;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetData(_In_ FORMATETC* pformatetc, _In_ STGMEDIUM* pmedium, BOOL fRelease) override
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection, __RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override
{
// Only the get direction is supported for OLE
if (dwDirection == DATADIR_GET)
{
// TODO: use SHCreateStdEnumFmtEtc API call
return CreateEnumFormatEtc(_formatsCount, _formatEtc, ppenumFormatEtc);
}
// The direction specified is not supported for drag+drop
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DAdvise(__RPC__in FORMATETC* pformatetc, DWORD advf, __RPC__in_opt IAdviseSink* pAdvSink, __RPC__out DWORD* pdwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
HRESULT STDMETHODCALLTYPE EnumDAdvise(__RPC__deref_out_opt IEnumSTATDATA** ppenumAdvise) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
private:
int32 lookupFormatEtc(FORMATETC* pFormatEtc) const
{
// Check each of our formats in turn to see if one matches
for (int32 i = 0; i < _formatsCount; i++)
{
if ((_formatEtc[i].tymed & pFormatEtc->tymed) &&
_formatEtc[i].cfFormat == pFormatEtc->cfFormat &&
_formatEtc[i].dwAspect == pFormatEtc->dwAspect)
{
// Return index of stored format
return i;
}
}
// Format not found
return INVALID_INDEX;
}
};
WindowsGuiData GuiDragDropData;
DragDropEffect Window::DoDragDrop(const StringView& data)
{
// Create background worker that will keep updating GUI (perform rendering)
const auto task = New<DoDragDropJob>();
Task::StartNew(task);
while (task->GetState() == TaskState::Queued)
Platform::Sleep(1);
// Create descriptors
FORMATETC fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stgmed = { TYMED_HGLOBAL, { nullptr }, nullptr };
// Create a HGLOBAL inside the storage medium
stgmed.hGlobal = StringToHandle(data);
// Create drop source
auto dropSource = new WindowsDragSource(&fmtetc, &stgmed, 1);
// Do the drag drop operation
DWORD dwEffect;
HRESULT result = ::DoDragDrop(dropSource, dropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK | DROPEFFECT_SCROLL, &dwEffect);
// Wait for job end
Platform::AtomicStore(&task->ExitFlag, 1);
task->Wait();
// Release allocated data
dropSource->Release();
ReleaseStgMedium(&stgmed);
// Fix hanging mouse state (Windows doesn't send WM_LBUTTONUP when we end the drag and drop)
if (Input::GetMouseButton(MouseButton::Left))
{
::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;
}
HRESULT Window::DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient((HWND)_handle, &p);
GuiDragDropData.Init((IDataObject*)pDataObj);
DragDropEffect effect = DragDropEffect::None;
OnDragEnter(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
Focus();
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
HRESULT Window::DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient((HWND)_handle, &p);
DragDropEffect effect = DragDropEffect::None;
OnDragOver(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
HRESULT Window::DragLeave()
{
OnDragLeave();
return S_OK;
}
HRESULT Window::Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient((HWND)_handle, &p);
GuiDragDropData.Init((IDataObject*)pDataObj);
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
#else
DragDropEffect Window::DoDragDrop(const StringView& data)
{
return DragDropEffect::None;
}
#endif
#endif

View File

@@ -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"
@@ -10,13 +10,6 @@
#include "Engine/Graphics/GPUSwapChain.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/GPUDevice.h"
#if USE_EDITOR
#include "Engine/Core/Collections/Array.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Platform/Base/DragDropHelper.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#endif
#include "../Win32/IncludeWindowsHeaders.h"
#include <propidl.h>
#if USE_EDITOR
@@ -118,7 +111,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings)
if (settings.IsRegularWindow)
style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP;
#elif WINDOWS_USE_NEWER_BORDER_LESS
if (settings.IsRegularWindow)
if (settings.Type == WindowType::Regular)
style |= WS_THICKFRAME | WS_SYSMENU | WS_CAPTION;
#endif
exStyle |= WS_EX_WINDOWEDGE;
@@ -127,7 +120,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings)
// Creating the window
_handle = CreateWindowExW(
exStyle,
Platform::ApplicationWindowClass,
Platform::ApplicationClassName,
settings.Title.GetText(),
style,
x,
@@ -162,7 +155,7 @@ WindowsWindow::WindowsWindow(const CreateWindowSettings& settings)
#if WINDOWS_USE_NEWER_BORDER_LESS
// Enable shadow
if (_settings.IsRegularWindow && !_settings.HasBorder && IsCompositionEnabled())
if (settings.Type == WindowType::Regular && !_settings.HasBorder && IsCompositionEnabled())
{
const int margin[4] = { 1, 1, 1, 1 };
::DwmExtendFrameIntoClientArea(_handle, margin);
@@ -295,7 +288,7 @@ void WindowsWindow::SetBorderless(bool isBorderless, bool maximized)
if (_settings.IsRegularWindow)
style |= WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_THICKFRAME | WS_GROUP;
#elif WINDOWS_USE_NEWER_BORDER_LESS
if (_settings.IsRegularWindow)
if (_settings.Type == WindowType::Regular)
lStyle |= WS_THICKFRAME | WS_SYSMENU;
#endif
@@ -371,7 +364,7 @@ void WindowsWindow::BringToFront(bool force)
{
ASSERT(HasHWND());
if (_settings.IsRegularWindow)
if (_settings.Type == WindowType::Regular)
{
if (IsIconic(_handle))
{
@@ -698,36 +691,6 @@ void WindowsWindow::SetCursor(CursorType type)
UpdateCursor();
}
#if USE_EDITOR
Windows::HRESULT WindowsWindow::QueryInterface(const Windows::IID& id, void** ppvObject)
{
// Check to see what interface has been requested
if ((const IID&)id == IID_IUnknown || (const IID&)id == IID_IDropTarget)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
Windows::ULONG WindowsWindow::AddRef()
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
Windows::ULONG WindowsWindow::Release()
{
return _InterlockedDecrement(&_refCount);
}
#endif
void WindowsWindow::CheckForWindowResize()
{
// Skip for minimized window (GetClientRect for minimized window returns 0)
@@ -1345,617 +1308,11 @@ 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;
}
return DefWindowProc(_handle, msg, wParam, lParam);
}
#if USE_EDITOR
HGLOBAL duplicateGlobalMem(HGLOBAL hMem)
{
auto len = GlobalSize(hMem);
auto source = GlobalLock(hMem);
auto dest = GlobalAlloc(GMEM_FIXED, len);
Platform::MemoryCopy(dest, source, len);
GlobalUnlock(hMem);
return dest;
}
DWORD dropEffect2OleEnum(DragDropEffect effect)
{
DWORD result;
switch (effect)
{
case DragDropEffect::None:
result = DROPEFFECT_NONE;
break;
case DragDropEffect::Copy:
result = DROPEFFECT_COPY;
break;
case DragDropEffect::Move:
result = DROPEFFECT_MOVE;
break;
case DragDropEffect::Link:
result = DROPEFFECT_LINK;
break;
default:
result = DROPEFFECT_NONE;
break;
}
return result;
}
DragDropEffect dropEffectFromOleEnum(DWORD effect)
{
DragDropEffect result;
switch (effect)
{
case DROPEFFECT_NONE:
result = DragDropEffect::None;
break;
case DROPEFFECT_COPY:
result = DragDropEffect::Copy;
break;
case DROPEFFECT_MOVE:
result = DragDropEffect::Move;
break;
case DROPEFFECT_LINK:
result = DragDropEffect::Link;
break;
default:
result = DragDropEffect::None;
break;
}
return result;
}
HANDLE StringToHandle(const StringView& str)
{
// Allocate and lock a global memory buffer.
// Make it fixed data so we don't have to use GlobalLock
const int32 length = str.Length();
char* ptr = static_cast<char*>(GlobalAlloc(GMEM_FIXED, length + 1));
// Copy the string into the buffer as ANSI text
StringUtils::ConvertUTF162ANSI(str.Get(), ptr, length);
ptr[length] = '\0';
return ptr;
}
void DeepCopyFormatEtc(FORMATETC* dest, FORMATETC* source)
{
// Copy the source FORMATETC into dest
*dest = *source;
if (source->ptd)
{
// Allocate memory for the DVTARGETDEVICE if necessary
dest->ptd = static_cast<DVTARGETDEVICE*>(CoTaskMemAlloc(sizeof(DVTARGETDEVICE)));
// Copy the contents of the source DVTARGETDEVICE into dest->ptd
*(dest->ptd) = *(source->ptd);
}
}
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc);
/// <summary>
/// GUI data for Windows platform
/// </summary>
class WindowsGuiData : public IGuiData
{
private:
Type _type;
Array<String> _data;
public:
/// <summary>
/// Init
/// </summary>
WindowsGuiData()
: _type(Type::Unknown)
, _data(1)
{
}
public:
/// <summary>
/// Init from Ole IDataObject
/// </summary>
/// <param name="pDataObj">Object</param>
void Init(IDataObject* pDataObj)
{
// Temporary data
FORMATETC fmtetc;
STGMEDIUM stgmed;
// Clear
_type = Type::Unknown;
_data.Clear();
// Check type
fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Text
_type = Type::Text;
// Get data
char* text = static_cast<char*>(GlobalLock(stgmed.hGlobal));
_data.Add(String(text));
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
else
{
fmtetc = { CF_UNICODETEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Unicode Text
_type = Type::Text;
// Get data
Char* text = static_cast<Char*>(GlobalLock(stgmed.hGlobal));
_data.Add(String(text));
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
else
{
fmtetc = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
if (pDataObj->GetData(&fmtetc, &stgmed) == S_OK)
{
// Files
_type = Type::Files;
// Get data
Char item[MAX_PATH];
HDROP hdrop = static_cast<HDROP>(GlobalLock(stgmed.hGlobal));
UINT filesCount = DragQueryFileW(hdrop, 0xFFFFFFFF, nullptr, 0);
for (UINT i = 0; i < filesCount; i++)
{
if (DragQueryFileW(hdrop, i, item, MAX_PATH) != 0)
{
_data.Add(String(item));
}
}
GlobalUnlock(stgmed.hGlobal);
ReleaseStgMedium(&stgmed);
}
}
}
}
public:
// [IGuiData]
Type GetType() const override
{
return _type;
}
String GetAsText() const override
{
String result;
if (_type == Type::Text)
{
result = _data[0];
}
return result;
}
void GetAsFiles(Array<String>* files) const override
{
if (_type == Type::Files)
{
files->Add(_data);
}
}
};
/// <summary>
/// Tool class for Windows Ole support
/// </summary>
class WindowsEnumFormatEtc : public IEnumFORMATETC
{
private:
ULONG _refCount;
ULONG _index;
ULONG _formatsCount;
FORMATETC* _formatEtc;
public:
WindowsEnumFormatEtc(FORMATETC* pFormatEtc, int32 nNumFormats)
: _refCount(1)
, _index(0)
, _formatsCount(nNumFormats)
, _formatEtc(nullptr)
{
// Allocate memory
_formatEtc = new FORMATETC[nNumFormats];
// Copy the FORMATETC structures
for (int32 i = 0; i < nNumFormats; i++)
{
DeepCopyFormatEtc(&_formatEtc[i], &pFormatEtc[i]);
}
}
~WindowsEnumFormatEtc()
{
if (_formatEtc)
{
for (uint32 i = 0; i < _formatsCount; i++)
{
if (_formatEtc[i].ptd)
{
CoTaskMemFree(_formatEtc[i].ptd);
}
}
delete[] _formatEtc;
}
}
public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override
{
// Check to see what interface has been requested
if (riid == IID_IEnumFORMATETC || riid == IID_IUnknown)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
ULONG ulRefCount = _InterlockedDecrement(&_refCount);
if (_refCount == 0)
{
delete this;
}
return ulRefCount;
}
// [IEnumFormatEtc]
HRESULT STDMETHODCALLTYPE Next(ULONG celt, FORMATETC* pFormatEtc, ULONG* pceltFetched) override
{
ULONG copied = 0;
// validate arguments
if (celt == 0 || pFormatEtc == nullptr)
return E_INVALIDARG;
// copy FORMATETC structures into caller's buffer
while (_index < _formatsCount && copied < celt)
{
DeepCopyFormatEtc(&pFormatEtc[copied], &_formatEtc[_index]);
copied++;
_index++;
}
// store result
if (pceltFetched != nullptr)
*pceltFetched = copied;
// did we copy all that was requested?
return (copied == celt) ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Skip(ULONG celt) override
{
_index += celt;
return (_index <= _formatsCount) ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Reset() override
{
_index = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Clone(IEnumFORMATETC** ppEnumFormatEtc) override
{
HRESULT result;
// Make a duplicate enumerator
result = CreateEnumFormatEtc(_formatsCount, _formatEtc, ppEnumFormatEtc);
if (result == S_OK)
{
// Manually set the index state
static_cast<WindowsEnumFormatEtc*>(*ppEnumFormatEtc)->_index = _index;
}
return result;
}
};
HRESULT CreateEnumFormatEtc(UINT nNumFormats, FORMATETC* pFormatEtc, IEnumFORMATETC** ppEnumFormatEtc)
{
if (nNumFormats == 0 || pFormatEtc == nullptr || ppEnumFormatEtc == nullptr)
return E_INVALIDARG;
*ppEnumFormatEtc = new WindowsEnumFormatEtc(pFormatEtc, nNumFormats);
return *ppEnumFormatEtc ? S_OK : E_OUTOFMEMORY;
}
/// <summary>
/// Drag drop source and data container for Ole
/// </summary>
class WindowsDragSource : public IDataObject, public IDropSource
{
private:
ULONG _refCount;
int32 _formatsCount;
FORMATETC* _formatEtc;
STGMEDIUM* _stgMedium;
public:
WindowsDragSource(FORMATETC* fmtetc, STGMEDIUM* stgmed, int32 count)
: _refCount(1)
, _formatsCount(count)
, _formatEtc(nullptr)
, _stgMedium(nullptr)
{
// Allocate memory
_formatEtc = new FORMATETC[count];
_stgMedium = new STGMEDIUM[count];
// Copy descriptors
for (int32 i = 0; i < count; i++)
{
_formatEtc[i] = fmtetc[i];
_stgMedium[i] = stgmed[i];
}
}
virtual ~WindowsDragSource()
{
delete[] _formatEtc;
delete[] _stgMedium;
}
public:
// [IUnknown]
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, _COM_Outptr_ void __RPC_FAR *__RPC_FAR * ppvObject) override
{
// Check to see what interface has been requested
if (riid == IID_IDataObject || riid == IID_IUnknown || riid == IID_IDropSource)
{
AddRef();
*ppvObject = this;
return S_OK;
}
// No interface
*ppvObject = nullptr;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef() override
{
_InterlockedIncrement(&_refCount);
return _refCount;
}
ULONG STDMETHODCALLTYPE Release() override
{
ULONG ulRefCount = _InterlockedDecrement(&_refCount);
if (_refCount == 0)
{
delete this;
}
return ulRefCount;
}
// [IDropSource]
HRESULT STDMETHODCALLTYPE QueryContinueDrag(_In_ BOOL fEscapePressed, _In_ DWORD grfKeyState) override
{
// If the Escape key has been pressed since the last call, cancel the drop
if (fEscapePressed == TRUE || grfKeyState & MK_RBUTTON)
return DRAGDROP_S_CANCEL;
// If the LeftMouse button has been released, then do the drop!
if ((grfKeyState & MK_LBUTTON) == 0)
return DRAGDROP_S_DROP;
// Continue with the drag-drop
return S_OK;
}
HRESULT STDMETHODCALLTYPE GiveFeedback(_In_ DWORD dwEffect) override
{
// TODO: allow to use custom mouse cursor during drop and drag operation
return DRAGDROP_S_USEDEFAULTCURSORS;
}
// [IDataObject]
HRESULT STDMETHODCALLTYPE GetData(_In_ FORMATETC* pformatetcIn, _Out_ STGMEDIUM* pmedium) override
{
if (pformatetcIn == nullptr || pmedium == nullptr)
return E_INVALIDARG;
// Try to match the specified FORMATETC with one of our supported formats
int32 index = lookupFormatEtc(pformatetcIn);
if (index == INVALID_INDEX)
return DV_E_FORMATETC;
// Found a match - transfer data into supplied storage medium
pmedium->tymed = _formatEtc[index].tymed;
pmedium->pUnkForRelease = nullptr;
// Copy the data into the caller's storage medium
switch (_formatEtc[index].tymed)
{
case TYMED_HGLOBAL:
pmedium->hGlobal = duplicateGlobalMem(_stgMedium[index].hGlobal);
break;
default:
return DV_E_FORMATETC;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetDataHere(_In_ FORMATETC* pformatetc, _Inout_ STGMEDIUM* pmedium) override
{
return DATA_E_FORMATETC;
}
HRESULT STDMETHODCALLTYPE QueryGetData(__RPC__in_opt FORMATETC* pformatetc) override
{
return lookupFormatEtc(pformatetc) == INVALID_INDEX ? DV_E_FORMATETC : S_OK;
}
HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(__RPC__in_opt FORMATETC* pformatectIn, __RPC__out FORMATETC* pformatetcOut) override
{
// Apparently we have to set this field to NULL even though we don't do anything else
pformatetcOut->ptd = nullptr;
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE SetData(_In_ FORMATETC* pformatetc, _In_ STGMEDIUM* pmedium, BOOL fRelease) override
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE EnumFormatEtc(DWORD dwDirection, __RPC__deref_out_opt IEnumFORMATETC** ppenumFormatEtc) override
{
// Only the get direction is supported for OLE
if (dwDirection == DATADIR_GET)
{
// TODO: use SHCreateStdEnumFmtEtc API call
return CreateEnumFormatEtc(_formatsCount, _formatEtc, ppenumFormatEtc);
}
// The direction specified is not supported for drag+drop
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE DAdvise(__RPC__in FORMATETC* pformatetc, DWORD advf, __RPC__in_opt IAdviseSink* pAdvSink, __RPC__out DWORD* pdwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
HRESULT STDMETHODCALLTYPE DUnadvise(DWORD dwConnection) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
HRESULT STDMETHODCALLTYPE EnumDAdvise(__RPC__deref_out_opt IEnumSTATDATA** ppenumAdvise) override
{
return OLE_E_ADVISENOTSUPPORTED;
}
private:
int32 lookupFormatEtc(FORMATETC* pFormatEtc) const
{
// Check each of our formats in turn to see if one matches
for (int32 i = 0; i < _formatsCount; i++)
{
if ((_formatEtc[i].tymed & pFormatEtc->tymed) &&
_formatEtc[i].cfFormat == pFormatEtc->cfFormat &&
_formatEtc[i].dwAspect == pFormatEtc->dwAspect)
{
// Return index of stored format
return i;
}
}
// Format not found
return INVALID_INDEX;
}
};
WindowsGuiData GuiDragDropData;
DragDropEffect WindowsWindow::DoDragDrop(const StringView& data)
{
// Create background worker that will keep updating GUI (perform rendering)
const auto task = New<DoDragDropJob>();
Task::StartNew(task);
while (task->GetState() == TaskState::Queued)
Platform::Sleep(1);
// Create descriptors
FORMATETC fmtetc = { CF_TEXT, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stgmed = { TYMED_HGLOBAL, { nullptr }, nullptr };
// Create a HGLOBAL inside the storage medium
stgmed.hGlobal = StringToHandle(data);
// Create drop source
auto dropSource = new WindowsDragSource(&fmtetc, &stgmed, 1);
// Do the drag drop operation
DWORD dwEffect;
HRESULT result = ::DoDragDrop(dropSource, dropSource, DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK | DROPEFFECT_SCROLL, &dwEffect);
// Wait for job end
Platform::AtomicStore(&task->ExitFlag, 1);
task->Wait();
// Release allocated data
dropSource->Release();
ReleaseStgMedium(&stgmed);
// Fix hanging mouse state (Windows doesn't send WM_LBUTTONUP when we end the drag and drop)
if (Input::GetMouseButton(MouseButton::Left))
{
::POINT point;
::GetCursorPos(&point);
Input::Mouse->OnMouseUp(Float2((float)point.x, (float)point.y), MouseButton::Left, this);
}
return SUCCEEDED(result) ? dropEffectFromOleEnum(dwEffect) : DragDropEffect::None;
}
HRESULT WindowsWindow::DragEnter(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient(_handle, &p);
GuiDragDropData.Init((IDataObject*)pDataObj);
DragDropEffect effect = DragDropEffect::None;
OnDragEnter(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
Focus();
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
HRESULT WindowsWindow::DragOver(Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient(_handle, &p);
DragDropEffect effect = DragDropEffect::None;
OnDragOver(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
HRESULT WindowsWindow::DragLeave()
{
OnDragLeave();
return S_OK;
}
HRESULT WindowsWindow::Drop(Windows::IDataObject* pDataObj, Windows::DWORD grfKeyState, Windows::POINTL pt, Windows::DWORD* pdwEffect)
{
POINT p = { pt.x, pt.y };
::ScreenToClient(_handle, &p);
GuiDragDropData.Init((IDataObject*)pDataObj);
DragDropEffect effect = DragDropEffect::None;
OnDragDrop(&GuiDragDropData, Float2(static_cast<float>(p.x), static_cast<float>(p.y)), effect);
*pdwEffect = dropEffect2OleEnum(effect);
return S_OK;
}
#else
DragDropEffect WindowsWindow::DoDragDrop(const StringView& data)
{
return DragDropEffect::None;
}
#endif
#endif

View File

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

View File

@@ -734,6 +734,15 @@ namespace FlaxEngine.GUI
Tooltip.OnMouseLeaveControl(this);
}
}
/// <summary>
/// When mouse moves over control's area while mouse is in relative mode
/// </summary>
/// <param name="mouseMotion">Mouse relative motion</param>
[NoAnimate]
public virtual void OnMouseMoveRelative(Float2 mouseMotion)
{
}
/// <summary>
/// When mouse leaves control's area

View File

@@ -72,8 +72,11 @@ namespace FlaxEngine.GUI
var dpiSize = Size * dpiScale;
var locationWS = target.PointToWindow(location);
var locationSS = parentWin.PointToScreen(locationWS);
var mousePos = Input.MouseScreenPosition;
_showTarget = target;
WrapPosition(ref locationSS);
//WrapPosition(ref locationSS);
WrapPosition(ref mousePos, 10);
locationSS = mousePos + new Float2(15, 10);
// Create window
var desc = CreateWindowSettings.Default;
@@ -90,7 +93,7 @@ namespace FlaxEngine.GUI
desc.AllowMaximize = false;
desc.AllowDragAndDrop = false;
desc.IsTopmost = true;
desc.IsRegularWindow = false;
desc.Type = WindowType.Tooltip;
desc.HasSizingFrame = false;
desc.ShowAfterFirstPaint = true;
_window = Platform.CreateWindow(ref desc);

View File

@@ -338,5 +338,17 @@ namespace FlaxEngine.GUI
base.OnMouseMove(location);
}
/// <inheritdoc />
public override void OnMouseMoveRelative(Float2 mouseMotion)
{
if (_trackingControl != null)
{
_trackingControl.OnMouseMoveRelative(mouseMotion);
return;
}
base.OnMouseMoveRelative(mouseMotion);
}
}
}

BIN
Source/Platforms/Linux/Binaries/ThirdParty/x64/libSDL3.a (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
Source/Platforms/Windows/Binaries/ThirdParty/x64/SDL3.lib (Stored with Git LFS) vendored Normal file

Binary file not shown.

18
Source/ThirdParty/SDL/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,18 @@
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

53
Source/ThirdParty/SDL/SDL.Build.cs vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System.IO;
using Flax.Build;
using Flax.Build.NativeCpp;
/// <summary>
/// https://www.libsdl.org/
/// </summary>
public class SDL : DepsModule
{
/// <inheritdoc />
public override void Init()
{
base.Init();
LicenseType = LicenseTypes.Custom;
LicenseFilePath = "LICENSE.txt";
// Merge third-party modules into engine binary
BinaryModuleName = "FlaxEngine";
}
/// <inheritdoc />
public override void Setup(BuildOptions options)
{
base.Setup(options);
var depsRoot = options.DepsFolder;
switch (options.Platform.Target)
{
case TargetPlatform.Windows:
options.OutputFiles.Add(Path.Combine(depsRoot, "SDL3.lib"));
options.OptionalDependencyFiles.Add(Path.Combine(depsRoot, "SDL3.pdb"));
options.OptionalDependencyFiles.Add(Path.Combine(depsRoot, "SDL3.dll"));
// For static linkage
options.OutputFiles.Add("Setupapi.lib");
options.OutputFiles.Add("Version.lib");
options.OutputFiles.Add("Imm32.lib");
options.OutputFiles.Add("Gdi32.lib");
break;
case TargetPlatform.Linux:
case TargetPlatform.Mac:
options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a"));
break;
default: throw new InvalidPlatformException(options.Platform.Target);
}
options.PublicIncludePaths.Add(Path.Combine(Globals.EngineRoot, @"Source\ThirdParty\SDL"));
}
}

83
Source/ThirdParty/SDL/SDL3/SDL.h vendored Normal file
View File

@@ -0,0 +1,83 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* \file SDL.h
*
* Main include header for the SDL library, version 3.1.2
*/
#ifndef SDL_h_
#define SDL_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_assert.h>
#include <SDL3/SDL_atomic.h>
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_bits.h>
#include <SDL3/SDL_blendmode.h>
#include <SDL3/SDL_camera.h>
#include <SDL3/SDL_clipboard.h>
#include <SDL3/SDL_cpuinfo.h>
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_endian.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_filesystem.h>
#include <SDL3/SDL_gamepad.h>
#include <SDL3/SDL_guid.h>
#include <SDL3/SDL_haptic.h>
#include <SDL3/SDL_hidapi.h>
#include <SDL3/SDL_hints.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_joystick.h>
#include <SDL3/SDL_keyboard.h>
#include <SDL3/SDL_keycode.h>
#include <SDL3/SDL_loadso.h>
#include <SDL3/SDL_locale.h>
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_messagebox.h>
#include <SDL3/SDL_metal.h>
#include <SDL3/SDL_misc.h>
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_mutex.h>
#include <SDL3/SDL_pen.h>
#include <SDL3/SDL_pixels.h>
#include <SDL3/SDL_platform.h>
#include <SDL3/SDL_power.h>
#include <SDL3/SDL_properties.h>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_scancode.h>
#include <SDL3/SDL_sensor.h>
#include <SDL3/SDL_storage.h>
#include <SDL3/SDL_surface.h>
#include <SDL3/SDL_system.h>
#include <SDL3/SDL_thread.h>
#include <SDL3/SDL_time.h>
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_touch.h>
#include <SDL3/SDL_version.h>
#include <SDL3/SDL_video.h>
#include <SDL3/SDL_oldnames.h>
#endif /* SDL_h_ */

551
Source/ThirdParty/SDL/SDL3/SDL_assert.h vendored Normal file
View File

@@ -0,0 +1,551 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryAssert
*
* A helpful assertion macro!
*
* SDL assertions operate like your usual `assert` macro, but with some added
* features:
*
* - It uses a trick with the `sizeof` operator, so disabled assertions
* vaporize out of the compiled code, but variables only referenced in the
* assertion won't trigger compiler warnings about being unused.
* - It is safe to use with a dangling-else: `if (x) SDL_assert(y); else
* do_something();`
* - It works the same everywhere, instead of counting on various platforms'
* compiler and C runtime to behave.
* - It provides multiple levels of assertion (SDL_assert, SDL_assert_release,
* SDL_assert_paranoid) instead of a single all-or-nothing option.
* - It offers a variety of responses when an assertion fails (retry, trigger
* the debugger, abort the program, ignore the failure once, ignore it for
* the rest of the program's run).
* - It tries to show the user a dialog by default, if possible, but the app
* can provide a callback to handle assertion failures however they like.
* - It lets failed assertions be retried. Perhaps you had a network failure
* and just want to retry the test after plugging your network cable back
* in? You can.
* - It lets the user ignore an assertion failure, if there's a harmless
* problem that one can continue past.
* - It lets the user mark an assertion as ignored for the rest of the
* program's run; if there's a harmless problem that keeps popping up.
* - It provides statistics and data on all failed assertions to the app.
* - It allows the default assertion handler to be controlled with environment
* variables, in case an automated script needs to control it.
*
* To use it: do a debug build and just sprinkle around tests to check your
* code!
*/
#ifndef SDL_assert_h_
#define SDL_assert_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* The level of assertion aggressiveness.
*
* This value changes depending on compiler options and other preprocessor
* defines.
*
* It is currently one of the following values, but future SDL releases might
* add more:
*
* - 0: All SDL assertion macros are disabled.
* - 1: Release settings: SDL_assert disabled, SDL_assert_release enabled.
* - 2: Debug settings: SDL_assert and SDL_assert_release enabled.
* - 3: Paranoid settings: All SDL assertion macros enabled, including
* SDL_assert_paranoid.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_ASSERT_LEVEL SomeNumberBasedOnVariousFactors
#elif !defined(SDL_ASSERT_LEVEL)
#ifdef SDL_DEFAULT_ASSERT_LEVEL
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
#elif defined(_DEBUG) || defined(DEBUG) || \
(defined(__GNUC__) && !defined(__OPTIMIZE__))
#define SDL_ASSERT_LEVEL 2
#else
#define SDL_ASSERT_LEVEL 1
#endif
#endif
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* Attempt to tell an attached debugger to pause.
*
* This allows an app to programmatically halt ("break") the debugger as if it
* had hit a breakpoint, allowing the developer to examine program state, etc.
*
* This is a macro--not a function--so that the debugger breaks on the source
* code line that used SDL_TriggerBreakpoint and not in some random guts of
* SDL. SDL_assert uses this macro for the same reason.
*
* If the program is not running under a debugger, SDL_TriggerBreakpoint will
* likely terminate the app, possibly without warning. If the current platform
* isn't supported (SDL doesn't know how to trigger a breakpoint), this macro
* does nothing.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_TriggerBreakpoint() TriggerABreakpointInAPlatformSpecificManner
#elif defined(_MSC_VER)
/* Don't include intrin.h here because it contains C++ code */
extern void __cdecl __debugbreak(void);
#define SDL_TriggerBreakpoint() __debugbreak()
#elif defined(ANDROID)
#include <assert.h>
#define SDL_TriggerBreakpoint() assert(0)
#elif SDL_HAS_BUILTIN(__builtin_debugtrap)
#define SDL_TriggerBreakpoint() __builtin_debugtrap()
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
#elif (defined(__GNUC__) || defined(__clang__)) && defined(__riscv)
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "ebreak\n\t" )
#elif ( defined(SDL_PLATFORM_APPLE) && (defined(__arm64__) || defined(__aarch64__)) ) /* this might work on other ARM targets, but this is a known quantity... */
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #22\n\t" )
#elif defined(SDL_PLATFORM_APPLE) && defined(__arm__)
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "bkpt #22\n\t" )
#elif defined(_WIN32) && ((defined(__GNUC__) || defined(__clang__)) && (defined(__arm64__) || defined(__aarch64__)) )
#define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "brk #0xF000\n\t" )
#elif defined(__386__) && defined(__WATCOMC__)
#define SDL_TriggerBreakpoint() { _asm { int 0x03 } }
#elif defined(HAVE_SIGNAL_H) && !defined(__WATCOMC__)
#include <signal.h>
#define SDL_TriggerBreakpoint() raise(SIGTRAP)
#else
/* How do we trigger breakpoints on this platform? */
#define SDL_TriggerBreakpoint()
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
# define SDL_FUNCTION __func__
#elif ((defined(__GNUC__) && (__GNUC__ >= 2)) || defined(_MSC_VER) || defined (__WATCOMC__))
# define SDL_FUNCTION __FUNCTION__
#else
# define SDL_FUNCTION "???"
#endif
#define SDL_FILE __FILE__
#define SDL_LINE __LINE__
/*
sizeof (x) makes the compiler still parse the expression even without
assertions enabled, so the code is always checked at compile time, but
doesn't actually generate code for it, so there are no side effects or
expensive checks at run time, just the constant size of what x WOULD be,
which presumably gets optimized out as unused.
This also solves the problem of...
int somevalue = blah();
SDL_assert(somevalue == 1);
...which would cause compiles to complain that somevalue is unused if we
disable assertions.
*/
/* "while (0,0)" fools Microsoft's compiler's /W4 warning level into thinking
this condition isn't constant. And looks like an owl's face! */
#ifdef _MSC_VER /* stupid /W4 warnings. */
#define SDL_NULL_WHILE_LOOP_CONDITION (0,0)
#else
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
#endif
#define SDL_disabled_assert(condition) \
do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION)
/**
* Possible outcomes from a triggered assertion.
*
* When an enabled assertion triggers, it may call the assertion handler
* (possibly one provided by the app via SDL_SetAssertionHandler), which will
* return one of these values, possibly after asking the user.
*
* Then SDL will respond based on this outcome (loop around to retry the
* condition, try to break in a debugger, kill the program, or ignore the
* problem).
*
* \since This enum is available since SDL 3.0.0.
*/
typedef enum SDL_AssertState
{
SDL_ASSERTION_RETRY, /**< Retry the assert immediately. */
SDL_ASSERTION_BREAK, /**< Make the debugger trigger a breakpoint. */
SDL_ASSERTION_ABORT, /**< Terminate the program. */
SDL_ASSERTION_IGNORE, /**< Ignore the assert. */
SDL_ASSERTION_ALWAYS_IGNORE /**< Ignore the assert from now on. */
} SDL_AssertState;
/**
* Information about an assertion failure.
*
* This structure is filled in with information about a triggered assertion,
* used by the assertion handler, then added to the assertion report. This is
* returned as a linked list from SDL_GetAssertionReport().
*
* \since This struct is available since SDL 3.0.0.
*/
typedef struct SDL_AssertData
{
SDL_bool always_ignore; /**< SDL_TRUE if app should always continue when assertion is triggered. */
unsigned int trigger_count; /**< Number of times this assertion has been triggered. */
const char *condition; /**< A string of this assert's test code. */
const char *filename; /**< The source file where this assert lives. */
int linenum; /**< The line in `filename` where this assert lives. */
const char *function; /**< The name of the function where this assert lives. */
const struct SDL_AssertData *next; /**< next item in the linked list. */
} SDL_AssertData;
/**
* Never call this directly.
*
* Use the SDL_assert* macros instead.
*
* \param data assert data structure.
* \param func function name.
* \param file file name.
* \param line line number.
* \returns assert state.
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData *data,
const char *func,
const char *file, int line) SDL_ANALYZER_NORETURN;
/* Define the trigger breakpoint call used in asserts */
#ifndef SDL_AssertBreakpoint
#if defined(ANDROID) && defined(assert)
/* Define this as empty in case assert() is defined as SDL_assert */
#define SDL_AssertBreakpoint()
#else
#define SDL_AssertBreakpoint() SDL_TriggerBreakpoint()
#endif
#endif /* !SDL_AssertBreakpoint */
/* the do {} while(0) avoids dangling else problems:
if (x) SDL_assert(y); else blah();
... without the do/while, the "else" could attach to this macro's "if".
We try to handle just the minimum we need here in a macro...the loop,
the static vars, and break points. The heavy lifting is handled in
SDL_ReportAssertion(), in SDL_assert.c.
*/
#define SDL_enabled_assert(condition) \
do { \
while ( !(condition) ) { \
static struct SDL_AssertData sdl_assert_data = { 0, 0, #condition, 0, 0, 0, 0 }; \
const SDL_AssertState sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \
if (sdl_assert_state == SDL_ASSERTION_RETRY) { \
continue; /* go again. */ \
} else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \
SDL_AssertBreakpoint(); \
} \
break; /* not retrying. */ \
} \
} while (SDL_NULL_WHILE_LOOP_CONDITION)
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* An assertion test that is normally performed only in debug builds.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 2, otherwise it is
* disabled. This is meant to only do these tests in debug builds, so they can
* tend to be more expensive, and they are meant to bring everything to a halt
* when they fail, with the programmer there to assess the problem.
*
* In short: you can sprinkle these around liberally and assume they will
* evaporate out of the build when building for end-users.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* Note that SDL_ASSERT is an _environment variable_ and not an SDL hint!
* Please refer to your platform's documentation for how to set it!
*
* \param condition boolean value to test.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_assert(condition) if (assertion_enabled && (condition)) { trigger_assertion; }
/**
* An assertion test that is performed even in release builds.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 1, otherwise it is
* disabled. This is meant to be for tests that are cheap to make and
* extremely unlikely to fail; generally it is frowned upon to have an
* assertion failure in a release build, so these assertions generally need to
* be of more than life-and-death importance if there's a chance they might
* trigger. You should almost always consider handling these cases more
* gracefully than an assert allows.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* Note that SDL_ASSERT is an _environment variable_ and not an SDL hint!
* Please refer to your platform's documentation for how to set it!
*
* \param condition boolean value to test.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_assert_release(condition) SDL_disabled_assert(condition)
/**
* An assertion test that is performed only when built with paranoid settings.
*
* This macro is enabled when the SDL_ASSERT_LEVEL is >= 3, otherwise it is
* disabled. This is a higher level than both release and debug, so these
* tests are meant to be expensive and only run when specifically looking for
* extremely unexpected failure cases in a special build.
*
* When assertions are disabled, this wraps `condition` in a `sizeof`
* operator, which means any function calls and side effects will not run, but
* the compiler will not complain about any otherwise-unused variables that
* are only referenced in the assertion.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* Note that SDL_ASSERT is an _environment variable_ and not an SDL hint!
* Please refer to your platform's documentation for how to set it!
*
* \param condition boolean value to test.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#endif
/* Enable various levels of assertions. */
#if SDL_ASSERT_LEVEL == 0 /* assertions disabled */
# define SDL_assert(condition) SDL_disabled_assert(condition)
# define SDL_assert_release(condition) SDL_disabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 1 /* release settings. */
# define SDL_assert(condition) SDL_disabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 2 /* debug settings. */
# define SDL_assert(condition) SDL_enabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
#elif SDL_ASSERT_LEVEL == 3 /* paranoid settings. */
# define SDL_assert(condition) SDL_enabled_assert(condition)
# define SDL_assert_release(condition) SDL_enabled_assert(condition)
# define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
#else
# error Unknown assertion level.
#endif
/**
* An assertion test that always performed.
*
* This macro is always enabled no matter what SDL_ASSERT_LEVEL is set to. You
* almost never want to use this, as it could trigger on an end-user's system,
* crashing your program.
*
* One can set the environment variable "SDL_ASSERT" to one of several strings
* ("abort", "break", "retry", "ignore", "always_ignore") to force a default
* behavior, which may be desirable for automation purposes. If your platform
* requires GUI interfaces to happen on the main thread but you're debugging
* an assertion in a background thread, it might be desirable to set this to
* "break" so that your debugger takes control as soon as assert is triggered,
* instead of risking a bad UI interaction (deadlock, etc) in the application.
*
* Note that SDL_ASSERT is an _environment variable_ and not an SDL hint!
* Please refer to your platform's documentation for how to set it!
*
* \param condition boolean value to test.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_assert_always(condition) SDL_enabled_assert(condition)
/**
* A callback that fires when an SDL assertion fails.
*
* \param data a pointer to the SDL_AssertData structure corresponding to the
* current assertion.
* \param userdata what was passed as `userdata` to SDL_SetAssertionHandler().
* \returns an SDL_AssertState value indicating how to handle the failure.
*
* \since This datatype is available since SDL 3.0.0.
*/
typedef SDL_AssertState (SDLCALL *SDL_AssertionHandler)(
const SDL_AssertData *data, void *userdata);
/**
* Set an application-defined assertion handler.
*
* This function allows an application to show its own assertion UI and/or
* force the response to an assertion failure. If the application doesn't
* provide this, SDL will try to do the right thing, popping up a
* system-specific GUI dialog, and probably minimizing any fullscreen windows.
*
* This callback may fire from any thread, but it runs wrapped in a mutex, so
* it will only fire from one thread at a time.
*
* This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
*
* \param handler the SDL_AssertionHandler function to call when an assertion
* fails or NULL for the default handler.
* \param userdata a pointer that is passed to `handler`.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetAssertionHandler
*/
extern SDL_DECLSPEC void SDLCALL SDL_SetAssertionHandler(
SDL_AssertionHandler handler,
void *userdata);
/**
* Get the default assertion handler.
*
* This returns the function pointer that is called by default when an
* assertion is triggered. This is an internal function provided by SDL, that
* is used for assertions when SDL_SetAssertionHandler() hasn't been used to
* provide a different function.
*
* \returns the default SDL_AssertionHandler that is called when an assert
* triggers.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetAssertionHandler
*/
extern SDL_DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetDefaultAssertionHandler(void);
/**
* Get the current assertion handler.
*
* This returns the function pointer that is called when an assertion is
* triggered. This is either the value last passed to
* SDL_SetAssertionHandler(), or if no application-specified function is set,
* is equivalent to calling SDL_GetDefaultAssertionHandler().
*
* The parameter `puserdata` is a pointer to a void*, which will store the
* "userdata" pointer that was passed to SDL_SetAssertionHandler(). This value
* will always be NULL for the default handler. If you don't care about this
* data, it is safe to pass a NULL pointer to this function to ignore it.
*
* \param puserdata pointer which is filled with the "userdata" pointer that
* was passed to SDL_SetAssertionHandler().
* \returns the SDL_AssertionHandler that is called when an assert triggers.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetAssertionHandler
*/
extern SDL_DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetAssertionHandler(void **puserdata);
/**
* Get a list of all assertion failures.
*
* This function gets all assertions triggered since the last call to
* SDL_ResetAssertionReport(), or the start of the program.
*
* The proper way to examine this data looks something like this:
*
* ```c
* const SDL_AssertData *item = SDL_GetAssertionReport();
* while (item) {
* printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\\n",
* item->condition, item->function, item->filename,
* item->linenum, item->trigger_count,
* item->always_ignore ? "yes" : "no");
* item = item->next;
* }
* ```
*
* \returns a list of all failed assertions or NULL if the list is empty. This
* memory should not be modified or freed by the application.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_ResetAssertionReport
*/
extern SDL_DECLSPEC const SDL_AssertData * SDLCALL SDL_GetAssertionReport(void);
/**
* Clear the list of all assertion failures.
*
* This function will clear the list of all assertions triggered up to that
* point. Immediately following this call, SDL_GetAssertionReport will return
* no items. In addition, any previously-triggered assertions will be reset to
* a trigger_count of zero, and their always_ignore state will be false.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetAssertionReport
*/
extern SDL_DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_assert_h_ */

507
Source/ThirdParty/SDL/SDL3/SDL_atomic.h vendored Normal file
View File

@@ -0,0 +1,507 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryAtomic
*
* Atomic operations.
*
* IMPORTANT: If you are not an expert in concurrent lockless programming, you
* should not be using any functions in this file. You should be protecting
* your data structures with full mutexes instead.
*
* ***Seriously, here be dragons!***
*
* You can find out a little more about lockless programming and the subtle
* issues that can arise here:
* https://learn.microsoft.com/en-us/windows/win32/dxtecharts/lockless-programming
*
* There's also lots of good information here:
*
* - https://www.1024cores.net/home/lock-free-algorithms
* - https://preshing.com/
*
* These operations may or may not actually be implemented using processor
* specific atomic operations. When possible they are implemented as true
* processor specific atomic operations. When that is not possible the are
* implemented using locks that *do* use the available atomic operations.
*
* All of the atomic operations that modify memory are full memory barriers.
*/
#ifndef SDL_atomic_h_
#define SDL_atomic_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_platform_defines.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* An atomic spinlock.
*
* The atomic locks are efficient spinlocks using CPU instructions, but are
* vulnerable to starvation and can spin forever if a thread holding a lock
* has been terminated. For this reason you should minimize the code executed
* inside an atomic lock and never do expensive things like API or system
* calls while holding them.
*
* They are also vulnerable to starvation if the thread holding the lock is
* lower priority than other threads and doesn't get scheduled. In general you
* should use mutexes instead, since they have better performance and
* contention behavior.
*
* The atomic locks are not safe to lock recursively.
*
* Porting Note: The spin lock functions and type are required and can not be
* emulated because they are used in the atomic emulation code.
*/
typedef int SDL_SpinLock;
/**
* Try to lock a spin lock by setting it to a non-zero value.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
* \returns SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already
* held.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_LockSpinlock
* \sa SDL_UnlockSpinlock
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_TryLockSpinlock(SDL_SpinLock *lock);
/**
* Lock a spin lock by setting it to a non-zero value.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_TryLockSpinlock
* \sa SDL_UnlockSpinlock
*/
extern SDL_DECLSPEC void SDLCALL SDL_LockSpinlock(SDL_SpinLock *lock);
/**
* Unlock a spin lock by setting it to 0.
*
* Always returns immediately.
*
* ***Please note that spinlocks are dangerous if you don't know what you're
* doing. Please be careful using any sort of spinlock!***
*
* \param lock a pointer to a lock variable.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_LockSpinlock
* \sa SDL_TryLockSpinlock
*/
extern SDL_DECLSPEC void SDLCALL SDL_UnlockSpinlock(SDL_SpinLock *lock);
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* Mark a compiler barrier.
*
* A compiler barrier prevents the compiler from reordering reads and writes
* to globally visible variables across the call.
*
* This macro only prevents the compiler from reordering reads and writes, it
* does not prevent the CPU from reordering reads and writes. However, all of
* the atomic operations that modify memory are full memory barriers.
*
* \threadsafety Obviously this macro is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_CompilerBarrier() DoCompilerSpecificReadWriteBarrier()
#elif defined(_MSC_VER) && (_MSC_VER > 1200) && !defined(__clang__)
void _ReadWriteBarrier(void);
#pragma intrinsic(_ReadWriteBarrier)
#define SDL_CompilerBarrier() _ReadWriteBarrier()
#elif (defined(__GNUC__) && !defined(SDL_PLATFORM_EMSCRIPTEN)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
/* This is correct for all CPUs when using GCC or Solaris Studio 12.1+. */
#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory")
#elif defined(__WATCOMC__)
extern __inline void SDL_CompilerBarrier(void);
#pragma aux SDL_CompilerBarrier = "" parm [] modify exact [];
#else
#define SDL_CompilerBarrier() \
{ SDL_SpinLock _tmp = 0; SDL_LockSpinlock(&_tmp); SDL_UnlockSpinlock(&_tmp); }
#endif
/**
* Insert a memory release barrier.
*
* Memory barriers are designed to prevent reads and writes from being
* reordered by the compiler and being seen out of order on multi-core CPUs.
*
* A typical pattern would be for thread A to write some data and a flag, and
* for thread B to read the flag and get the data. In this case you would
* insert a release barrier between writing the data and the flag,
* guaranteeing that the data write completes no later than the flag is
* written, and you would insert an acquire barrier between reading the flag
* and reading the data, to ensure that all the reads associated with the flag
* have completed.
*
* In this pattern you should always see a release barrier paired with an
* acquire barrier and you should gate the data reads/writes with a single
* flag variable.
*
* For more information on these semantics, take a look at the blog post:
* http://preshing.com/20120913/acquire-and-release-semantics
*
* \threadsafety Obviously this macro is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierReleaseFunction(void);
/**
* Insert a memory acquire barrier.
*
* Please refer to SDL_MemoryBarrierReleaseFunction for the details!
*
* \threadsafety Obviously this function is safe to use from any thread at any
* time, but if you find yourself needing this, you are probably
* dealing with some very sensitive code; be careful!
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_MemoryBarrierReleaseFunction
*/
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void);
/* !!! FIXME: this should have documentation! */
#if defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("lwsync" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("lwsync" : : : "memory")
#elif defined(__GNUC__) && defined(__aarch64__)
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory")
#elif defined(__GNUC__) && defined(__arm__)
#if 0 /* defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) */
/* Information from:
https://chromium.googlesource.com/chromium/chromium/+/trunk/base/atomicops_internals_arm_gcc.h#19
The Linux kernel provides a helper function which provides the right code for a memory barrier,
hard-coded at address 0xffff0fa0
*/
typedef void (*SDL_KernelMemoryBarrierFunc)();
#define SDL_MemoryBarrierRelease() ((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
#define SDL_MemoryBarrierAcquire() ((SDL_KernelMemoryBarrierFunc)0xffff0fa0)()
#else
#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) || defined(__ARM_ARCH_8A__)
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("dmb ish" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("dmb ish" : : : "memory")
#elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__)
#ifdef __thumb__
/* The mcr instruction isn't available in thumb mode, use real functions */
#define SDL_MEMORY_BARRIER_USES_FUNCTION
#define SDL_MemoryBarrierRelease() SDL_MemoryBarrierReleaseFunction()
#define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction()
#else
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r"(0) : "memory")
#endif /* __thumb__ */
#else
#define SDL_MemoryBarrierRelease() __asm__ __volatile__ ("" : : : "memory")
#define SDL_MemoryBarrierAcquire() __asm__ __volatile__ ("" : : : "memory")
#endif /* SDL_PLATFORM_LINUX || SDL_PLATFORM_ANDROID */
#endif /* __GNUC__ && __arm__ */
#else
#if (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x5120))
/* This is correct for all CPUs on Solaris when using Solaris Studio 12.1+. */
#include <mbarrier.h>
#define SDL_MemoryBarrierRelease() __machine_rel_barrier()
#define SDL_MemoryBarrierAcquire() __machine_acq_barrier()
#else
/* This is correct for the x86 and x64 CPUs, and we'll expand this over time. */
#define SDL_MemoryBarrierRelease() SDL_CompilerBarrier()
#define SDL_MemoryBarrierAcquire() SDL_CompilerBarrier()
#endif
#endif
/* "REP NOP" is PAUSE, coded for tools that don't know it by that name. */
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
/**
* A macro to insert a CPU-specific "pause" instruction into the program.
*
* This can be useful in busy-wait loops, as it serves as a hint to the CPU as
* to the program's intent; some CPUs can use this to do more efficient
* processing. On some platforms, this doesn't do anything, so using this
* macro might just be a harmless no-op.
*
* Note that if you are busy-waiting, there are often more-efficient
* approaches with other synchronization primitives: mutexes, semaphores,
* condition variables, etc.
*
* \threadsafety This macro is safe to use from any thread.
*
* \since This macro is available since SDL 3.0.0.
*/
#define SDL_CPUPauseInstruction() DoACPUPauseInACompilerAndArchitectureSpecificWay
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__i386__) || defined(__x86_64__))
#define SDL_CPUPauseInstruction() __asm__ __volatile__("pause\n") /* Some assemblers can't do REP NOP, so go with PAUSE. */
#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(__aarch64__)
#define SDL_CPUPauseInstruction() __asm__ __volatile__("yield" ::: "memory")
#elif (defined(__powerpc__) || defined(__powerpc64__))
#define SDL_CPUPauseInstruction() __asm__ __volatile__("or 27,27,27");
#elif (defined(__riscv) && __riscv_xlen == 64)
#define SDL_CPUPauseInstruction() __asm__ __volatile__(".insn i 0x0F, 0, x0, x0, 0x010");
#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
#define SDL_CPUPauseInstruction() _mm_pause() /* this is actually "rep nop" and not a SIMD instruction. No inline asm in MSVC x86-64! */
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
#define SDL_CPUPauseInstruction() __yield()
#elif defined(__WATCOMC__) && defined(__386__)
extern __inline void SDL_CPUPauseInstruction(void);
#pragma aux SDL_CPUPauseInstruction = ".686p" ".xmm2" "pause"
#else
#define SDL_CPUPauseInstruction()
#endif
/**
* A type representing an atomic integer value.
*
* This can be used to manage a value that is synchronized across multiple
* CPUs without a race condition; when an app sets a value with SDL_AtomicSet
* all other threads, regardless of the CPU it is running on, will see that
* value when retrieved with SDL_AtomicGet, regardless of CPU caches, etc.
*
* This is also useful for atomic compare-and-swap operations: a thread can
* change the value as long as its current value matches expectations. When
* done in a loop, one can guarantee data consistency across threads without a
* lock (but the usual warnings apply: if you don't know what you're doing, or
* you don't do it carefully, you can confidently cause any number of
* disasters with this, so in most cases, you _should_ use a mutex instead of
* this!).
*
* This is a struct so people don't accidentally use numeric operations on it
* directly. You have to use SDL_Atomic* functions.
*
* \since This struct is available since SDL 3.0.0.
*
* \sa SDL_AtomicCompareAndSwap
* \sa SDL_AtomicGet
* \sa SDL_AtomicSet
* \sa SDL_AtomicAdd
*/
typedef struct SDL_AtomicInt { int value; } SDL_AtomicInt;
/**
* Set an atomic variable to a new value if it is currently an old value.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param oldval the old value.
* \param newval the new value.
* \returns SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicCompareAndSwapPointer
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_AtomicCompareAndSwap(SDL_AtomicInt *a, int oldval, int newval);
/**
* Set an atomic variable to a value.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param v the desired value.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicGet
*/
extern SDL_DECLSPEC int SDLCALL SDL_AtomicSet(SDL_AtomicInt *a, int v);
/**
* Get the value of an atomic variable.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable.
* \returns the current value of an atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicSet
*/
extern SDL_DECLSPEC int SDLCALL SDL_AtomicGet(SDL_AtomicInt *a);
/**
* Add to an atomic variable.
*
* This function also acts as a full memory barrier.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to an SDL_AtomicInt variable to be modified.
* \param v the desired value to add.
* \returns the previous value of the atomic variable.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicDecRef
* \sa SDL_AtomicIncRef
*/
extern SDL_DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_AtomicInt *a, int v);
#ifndef SDL_AtomicIncRef
/**
* Increment an atomic variable used as a reference count.
*
* ***Note: If you don't know what this macro is for, you shouldn't use it!***
*
* \param a a pointer to an SDL_AtomicInt to increment.
* \returns the previous value of the atomic variable.
*
* \since This macro is available since SDL 3.0.0.
*
* \sa SDL_AtomicDecRef
*/
#define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1)
#endif
#ifndef SDL_AtomicDecRef
/**
* Decrement an atomic variable used as a reference count.
*
* ***Note: If you don't know what this macro is for, you shouldn't use it!***
*
* \param a a pointer to an SDL_AtomicInt to increment.
* \returns SDL_TRUE if the variable reached zero after decrementing,
* SDL_FALSE otherwise.
*
* \since This macro is available since SDL 3.0.0.
*
* \sa SDL_AtomicIncRef
*/
#define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1)
#endif
/**
* Set a pointer to a new value if it is currently an old value.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \param oldval the old pointer value.
* \param newval the new pointer value.
* \returns SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicCompareAndSwap
* \sa SDL_AtomicGetPtr
* \sa SDL_AtomicSetPtr
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_AtomicCompareAndSwapPointer(void **a, void *oldval, void *newval);
/**
* Set a pointer to a value atomically.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \param v the desired pointer value.
* \returns the previous value of the pointer.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicCompareAndSwapPointer
* \sa SDL_AtomicGetPtr
*/
extern SDL_DECLSPEC void * SDLCALL SDL_AtomicSetPtr(void **a, void *v);
/**
* Get the value of a pointer atomically.
*
* ***Note: If you don't know what this function is for, you shouldn't use
* it!***
*
* \param a a pointer to a pointer.
* \returns the current value of a pointer.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AtomicCompareAndSwapPointer
* \sa SDL_AtomicSetPtr
*/
extern SDL_DECLSPEC void * SDLCALL SDL_AtomicGetPtr(void **a);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_atomic_h_ */

2040
Source/ThirdParty/SDL/SDL3/SDL_audio.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,227 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* WIKI CATEGORY: BeginCode */
/**
* SDL_begin_code.h sets things up for C dynamic library function definitions,
* static inlined functions, and structures aligned at 4-byte alignment.
* If you don't like ugly C preprocessor code, don't look at this file. :)
*/
/* This shouldn't be nested -- included it around code only. */
#ifdef SDL_begin_code_h
#error Nested inclusion of SDL_begin_code.h
#endif
#define SDL_begin_code_h
#ifndef SDL_DEPRECATED
# if defined(__GNUC__) && (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */
# define SDL_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER)
# define SDL_DEPRECATED __declspec(deprecated)
# else
# define SDL_DEPRECATED
# endif
#endif
#ifndef SDL_UNUSED
# ifdef __GNUC__
# define SDL_UNUSED __attribute__((unused))
# else
# define SDL_UNUSED
# endif
#endif
/* Some compilers use a special export keyword */
#ifndef SDL_DECLSPEC
# if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINRT) || defined(SDL_PLATFORM_CYGWIN) || defined(SDL_PLATFORM_GDK)
# ifdef DLL_EXPORT
# define SDL_DECLSPEC __declspec(dllexport)
# else
# define SDL_DECLSPEC
# endif
# else
# if defined(__GNUC__) && __GNUC__ >= 4
# define SDL_DECLSPEC __attribute__ ((visibility("default")))
# else
# define SDL_DECLSPEC
# endif
# endif
#endif
/* By default SDL uses the C calling convention */
#ifndef SDLCALL
#if (defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINRT) || defined(SDL_PLATFORM_GDK)) && !defined(__GNUC__)
#define SDLCALL __cdecl
#else
#define SDLCALL
#endif
#endif /* SDLCALL */
/* Force structure packing at 4 byte alignment.
This is necessary if the header is included in code which has structure
packing set to an alternate value, say for loading structures from disk.
The packing is reset to the previous value in SDL_close_code.h
*/
#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__BORLANDC__)
#ifdef _MSC_VER
#pragma warning(disable: 4103)
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wpragma-pack"
#endif
#ifdef __BORLANDC__
#pragma nopackwarning
#endif
#ifdef _WIN64
/* Use 8-byte alignment on 64-bit architectures, so pointers are aligned */
#pragma pack(push,8)
#else
#pragma pack(push,4)
#endif
#endif /* Compiler needs structure packing set */
#ifndef SDL_INLINE
#ifdef __GNUC__
#define SDL_INLINE __inline__
#elif defined(_MSC_VER) || defined(__BORLANDC__) || \
defined(__DMC__) || defined(__SC__) || \
defined(__WATCOMC__) || defined(__LCC__) || \
defined(__DECC) || defined(__CC_ARM)
#define SDL_INLINE __inline
#ifndef __inline__
#define __inline__ __inline
#endif
#else
#define SDL_INLINE inline
#ifndef __inline__
#define __inline__ inline
#endif
#endif
#endif /* SDL_INLINE not defined */
#ifndef SDL_FORCE_INLINE
#ifdef _MSC_VER
#define SDL_FORCE_INLINE __forceinline
#elif ( (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) )
#define SDL_FORCE_INLINE __attribute__((always_inline)) static __inline__
#else
#define SDL_FORCE_INLINE static SDL_INLINE
#endif
#endif /* SDL_FORCE_INLINE not defined */
#ifndef SDL_NORETURN
#ifdef __GNUC__
#define SDL_NORETURN __attribute__((noreturn))
#elif defined(_MSC_VER)
#define SDL_NORETURN __declspec(noreturn)
#else
#define SDL_NORETURN
#endif
#endif /* SDL_NORETURN not defined */
#ifdef __clang__
#if __has_feature(attribute_analyzer_noreturn)
#define SDL_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
#endif
#endif
#ifndef SDL_ANALYZER_NORETURN
#define SDL_ANALYZER_NORETURN
#endif
/* Apparently this is needed by several Windows compilers */
#ifndef __MACH__
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif /* NULL */
#endif /* ! macOS - breaks precompiled headers */
#ifndef SDL_FALLTHROUGH
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L)
#define SDL_FALLTHROUGH [[fallthrough]]
#else
#if defined(__has_attribute) && !defined(__SUNPRO_C) && !defined(__SUNPRO_CC)
#define SDL_HAS_FALLTHROUGH __has_attribute(__fallthrough__)
#else
#define SDL_HAS_FALLTHROUGH 0
#endif /* __has_attribute */
#if SDL_HAS_FALLTHROUGH && \
((defined(__GNUC__) && __GNUC__ >= 7) || \
(defined(__clang_major__) && __clang_major__ >= 10))
#define SDL_FALLTHROUGH __attribute__((__fallthrough__))
#else
#define SDL_FALLTHROUGH do {} while (0) /* fallthrough */
#endif /* SDL_HAS_FALLTHROUGH */
#undef SDL_HAS_FALLTHROUGH
#endif /* C++17 or C2x */
#endif /* SDL_FALLTHROUGH not defined */
#ifndef SDL_NODISCARD
#if (defined(__cplusplus) && __cplusplus >= 201703L) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L)
#define SDL_NODISCARD [[nodiscard]]
#elif ( (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) )
#define SDL_NODISCARD __attribute__((warn_unused_result))
#elif defined(_MSC_VER) && (_MSC_VER >= 1700)
#define SDL_NODISCARD _Check_return_
#else
#define SDL_NODISCARD
#endif /* C++17 or C23 */
#endif /* SDL_NODISCARD not defined */
#ifndef SDL_MALLOC
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define SDL_MALLOC __attribute__((malloc))
/** FIXME
#elif defined(_MSC_VER)
#define SDL_MALLOC __declspec(allocator) __desclspec(restrict)
**/
#else
#define SDL_MALLOC
#endif
#endif /* SDL_MALLOC not defined */
#ifndef SDL_ALLOC_SIZE
#if (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
#define SDL_ALLOC_SIZE(p) __attribute__((alloc_size(p)))
#elif defined(_MSC_VER)
#define SDL_ALLOC_SIZE(p)
#else
#define SDL_ALLOC_SIZE(p)
#endif
#endif /* SDL_ALLOC_SIZE not defined */
#ifndef SDL_ALLOC_SIZE2
#if (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
#define SDL_ALLOC_SIZE2(p1, p2) __attribute__((alloc_size(p1, p2)))
#elif defined(_MSC_VER)
#define SDL_ALLOC_SIZE2(p1, p2)
#else
#define SDL_ALLOC_SIZE2(p1, p2)
#endif
#endif /* SDL_ALLOC_SIZE2 not defined */

152
Source/ThirdParty/SDL/SDL3/SDL_bits.h vendored Normal file
View File

@@ -0,0 +1,152 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryBits
*
* Functions for fiddling with bits and bitmasks.
*/
#ifndef SDL_bits_h_
#define SDL_bits_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \file SDL_bits.h
*/
#if defined(__WATCOMC__) && defined(__386__)
extern __inline int _SDL_bsr_watcom(Uint32);
#pragma aux _SDL_bsr_watcom = \
"bsr eax, eax" \
parm [eax] nomemory \
value [eax] \
modify exact [eax] nomemory;
#endif
/**
* Get the index of the most significant (set) bit in a 32-bit number.
*
* Result is undefined when called with 0. This operation can also be stated
* as "count leading zeroes" and "log base 2".
*
* Note that this is a forced-inline function in a header, and not a public
* API function available in the SDL library (which is to say, the code is
* embedded in the calling program and the linker and dynamic loader will not
* be able to find this function inside SDL itself).
*
* \param x the 32-bit value to examine.
* \returns the index of the most significant bit, or -1 if the value is 0.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*/
SDL_FORCE_INLINE int SDL_MostSignificantBitIndex32(Uint32 x)
{
#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
/* Count Leading Zeroes builtin in GCC.
* http://gcc.gnu.org/onlinedocs/gcc-4.3.4/gcc/Other-Builtins.html
*/
if (x == 0) {
return -1;
}
return 31 - __builtin_clz(x);
#elif defined(__WATCOMC__) && defined(__386__)
if (x == 0) {
return -1;
}
return _SDL_bsr_watcom(x);
#elif defined(_MSC_VER)
unsigned long index;
if (_BitScanReverse(&index, x)) {
return index;
}
return -1;
#else
/* Based off of Bit Twiddling Hacks by Sean Eron Anderson
* <seander@cs.stanford.edu>, released in the public domain.
* http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
*/
const Uint32 b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
const int S[] = {1, 2, 4, 8, 16};
int msbIndex = 0;
int i;
if (x == 0) {
return -1;
}
for (i = 4; i >= 0; i--)
{
if (x & b[i])
{
x >>= S[i];
msbIndex |= S[i];
}
}
return msbIndex;
#endif
}
/**
* Determine if a unsigned 32-bit value has exactly one bit set.
*
* If there are no bits set (`x` is zero), or more than one bit set, this
* returns SDL_FALSE. If any one bit is exclusively set, this returns
* SDL_TRUE.
*
* Note that this is a forced-inline function in a header, and not a public
* API function available in the SDL library (which is to say, the code is
* embedded in the calling program and the linker and dynamic loader will not
* be able to find this function inside SDL itself).
*
* \param x the 32-bit value to examine.
* \returns SDL_TRUE if exactly one bit is set in `x`, SDL_FALSE otherwise.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*/
SDL_FORCE_INLINE SDL_bool SDL_HasExactlyOneBitSet32(Uint32 x)
{
if (x && !(x & (x - 1))) {
return SDL_TRUE;
}
return SDL_FALSE;
}
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_bits_h_ */

View File

@@ -0,0 +1,200 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryBlendmode
*
* Blend modes decide how two colors will mix together. There are both
* standard modes for basic needs and a means to create custom modes,
* dictating what sort of math to do what on what color components.
*/
#ifndef SDL_blendmode_h_
#define SDL_blendmode_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* A set of blend modes used in drawing operations.
*
* These predefined blend modes are supported everywhere.
*
* Additional values may be obtained from SDL_ComposeCustomBlendMode.
*
* \since This datatype is available since SDL 3.0.0.
*
* \sa SDL_ComposeCustomBlendMode
*/
typedef Uint32 SDL_BlendMode;
#define SDL_BLENDMODE_NONE 0x00000000u /**< no blending: dstRGBA = srcRGBA */
#define SDL_BLENDMODE_BLEND 0x00000001u /**< alpha blending: dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)), dstA = srcA + (dstA * (1-srcA)) */
#define SDL_BLENDMODE_BLEND_PREMULTIPLIED 0x00000010u /**< pre-multiplied alpha blending: dstRGBA = srcRGBA + (dstRGBA * (1-srcA)) */
#define SDL_BLENDMODE_ADD 0x00000002u /**< additive blending: dstRGB = (srcRGB * srcA) + dstRGB, dstA = dstA */
#define SDL_BLENDMODE_ADD_PREMULTIPLIED 0x00000020u /**< pre-multiplied additive blending: dstRGB = srcRGB + dstRGB, dstA = dstA */
#define SDL_BLENDMODE_MOD 0x00000004u /**< color modulate: dstRGB = srcRGB * dstRGB, dstA = dstA */
#define SDL_BLENDMODE_MUL 0x00000008u /**< color multiply: dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA)), dstA = dstA */
#define SDL_BLENDMODE_INVALID 0x7FFFFFFFu
/**
* The blend operation used when combining source and destination pixel
* components.
*
* \since This enum is available since SDL 3.0.0.
*/
typedef enum SDL_BlendOperation
{
SDL_BLENDOPERATION_ADD = 0x1, /**< dst + src: supported by all renderers */
SDL_BLENDOPERATION_SUBTRACT = 0x2, /**< src - dst : supported by D3D, OpenGL, OpenGLES, and Vulkan */
SDL_BLENDOPERATION_REV_SUBTRACT = 0x3, /**< dst - src : supported by D3D, OpenGL, OpenGLES, and Vulkan */
SDL_BLENDOPERATION_MINIMUM = 0x4, /**< min(dst, src) : supported by D3D, OpenGL, OpenGLES, and Vulkan */
SDL_BLENDOPERATION_MAXIMUM = 0x5 /**< max(dst, src) : supported by D3D, OpenGL, OpenGLES, and Vulkan */
} SDL_BlendOperation;
/**
* The normalized factor used to multiply pixel components.
*
* The blend factors are multiplied with the pixels from a drawing operation
* (src) and the pixels from the render target (dst) before the blend
* operation. The comma-separated factors listed above are always applied in
* the component order red, green, blue, and alpha.
*
* \since This enum is available since SDL 3.0.0.
*/
typedef enum SDL_BlendFactor
{
SDL_BLENDFACTOR_ZERO = 0x1, /**< 0, 0, 0, 0 */
SDL_BLENDFACTOR_ONE = 0x2, /**< 1, 1, 1, 1 */
SDL_BLENDFACTOR_SRC_COLOR = 0x3, /**< srcR, srcG, srcB, srcA */
SDL_BLENDFACTOR_ONE_MINUS_SRC_COLOR = 0x4, /**< 1-srcR, 1-srcG, 1-srcB, 1-srcA */
SDL_BLENDFACTOR_SRC_ALPHA = 0x5, /**< srcA, srcA, srcA, srcA */
SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA = 0x6, /**< 1-srcA, 1-srcA, 1-srcA, 1-srcA */
SDL_BLENDFACTOR_DST_COLOR = 0x7, /**< dstR, dstG, dstB, dstA */
SDL_BLENDFACTOR_ONE_MINUS_DST_COLOR = 0x8, /**< 1-dstR, 1-dstG, 1-dstB, 1-dstA */
SDL_BLENDFACTOR_DST_ALPHA = 0x9, /**< dstA, dstA, dstA, dstA */
SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA = 0xA /**< 1-dstA, 1-dstA, 1-dstA, 1-dstA */
} SDL_BlendFactor;
/**
* Compose a custom blend mode for renderers.
*
* The functions SDL_SetRenderDrawBlendMode and SDL_SetTextureBlendMode accept
* the SDL_BlendMode returned by this function if the renderer supports it.
*
* A blend mode controls how the pixels from a drawing operation (source) get
* combined with the pixels from the render target (destination). First, the
* components of the source and destination pixels get multiplied with their
* blend factors. Then, the blend operation takes the two products and
* calculates the result that will get stored in the render target.
*
* Expressed in pseudocode, it would look like this:
*
* ```c
* dstRGB = colorOperation(srcRGB * srcColorFactor, dstRGB * dstColorFactor);
* dstA = alphaOperation(srcA * srcAlphaFactor, dstA * dstAlphaFactor);
* ```
*
* Where the functions `colorOperation(src, dst)` and `alphaOperation(src,
* dst)` can return one of the following:
*
* - `src + dst`
* - `src - dst`
* - `dst - src`
* - `min(src, dst)`
* - `max(src, dst)`
*
* The red, green, and blue components are always multiplied with the first,
* second, and third components of the SDL_BlendFactor, respectively. The
* fourth component is not used.
*
* The alpha component is always multiplied with the fourth component of the
* SDL_BlendFactor. The other components are not used in the alpha
* calculation.
*
* Support for these blend modes varies for each renderer. To check if a
* specific SDL_BlendMode is supported, create a renderer and pass it to
* either SDL_SetRenderDrawBlendMode or SDL_SetTextureBlendMode. They will
* return with an error if the blend mode is not supported.
*
* This list describes the support of custom blend modes for each renderer.
* All renderers support the four blend modes listed in the SDL_BlendMode
* enumeration.
*
* - **direct3d**: Supports all operations with all factors. However, some
* factors produce unexpected results with `SDL_BLENDOPERATION_MINIMUM` and
* `SDL_BLENDOPERATION_MAXIMUM`.
* - **direct3d11**: Same as Direct3D 9.
* - **opengl**: Supports the `SDL_BLENDOPERATION_ADD` operation with all
* factors. OpenGL versions 1.1, 1.2, and 1.3 do not work correctly here.
* - **opengles2**: Supports the `SDL_BLENDOPERATION_ADD`,
* `SDL_BLENDOPERATION_SUBTRACT`, `SDL_BLENDOPERATION_REV_SUBTRACT`
* operations with all factors.
* - **psp**: No custom blend mode support.
* - **software**: No custom blend mode support.
*
* Some renderers do not provide an alpha component for the default render
* target. The `SDL_BLENDFACTOR_DST_ALPHA` and
* `SDL_BLENDFACTOR_ONE_MINUS_DST_ALPHA` factors do not have an effect in this
* case.
*
* \param srcColorFactor the SDL_BlendFactor applied to the red, green, and
* blue components of the source pixels.
* \param dstColorFactor the SDL_BlendFactor applied to the red, green, and
* blue components of the destination pixels.
* \param colorOperation the SDL_BlendOperation used to combine the red,
* green, and blue components of the source and
* destination pixels.
* \param srcAlphaFactor the SDL_BlendFactor applied to the alpha component of
* the source pixels.
* \param dstAlphaFactor the SDL_BlendFactor applied to the alpha component of
* the destination pixels.
* \param alphaOperation the SDL_BlendOperation used to combine the alpha
* component of the source and destination pixels.
* \returns an SDL_BlendMode that represents the chosen factors and
* operations.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetRenderDrawBlendMode
* \sa SDL_GetRenderDrawBlendMode
* \sa SDL_SetTextureBlendMode
* \sa SDL_GetTextureBlendMode
*/
extern SDL_DECLSPEC SDL_BlendMode SDLCALL SDL_ComposeCustomBlendMode(SDL_BlendFactor srcColorFactor,
SDL_BlendFactor dstColorFactor,
SDL_BlendOperation colorOperation,
SDL_BlendFactor srcAlphaFactor,
SDL_BlendFactor dstAlphaFactor,
SDL_BlendOperation alphaOperation);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_blendmode_h_ */

495
Source/ThirdParty/SDL/SDL3/SDL_camera.h vendored Normal file
View File

@@ -0,0 +1,495 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryCamera
*
* Video capture for the SDL library.
*
* This API lets apps read input from video sources, like webcams. Camera
* devices can be enumerated, queried, and opened. Once opened, it will
* provide SDL_Surface objects as new frames of video come in. These surfaces
* can be uploaded to an SDL_Texture or processed as pixels in memory.
*/
#ifndef SDL_camera_h_
#define SDL_camera_h_
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_video.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/**
* This is a unique ID for a camera device for the time it is connected to the
* system, and is never reused for the lifetime of the application.
*
* If the device is disconnected and reconnected, it will get a new ID.
*
* The ID value starts at 1 and increments from there. The value 0 is an
* invalid ID.
*
* \since This datatype is available since SDL 3.0.0.
*
* \sa SDL_GetCameras
*/
typedef Uint32 SDL_CameraID;
/**
* The opaque structure used to identify an opened SDL camera.
*
* \since This struct is available since SDL 3.0.0.
*/
typedef struct SDL_Camera SDL_Camera;
/**
* The details of an output format for a camera device.
*
* Cameras often support multiple formats; each one will be encapsulated in
* this struct.
*
* \since This struct is available since SDL 3.0.0.
*
* \sa SDL_GetCameraSupportedFormats
* \sa SDL_GetCameraFormat
*/
typedef struct SDL_CameraSpec
{
SDL_PixelFormat format; /**< Frame format */
SDL_Colorspace colorspace; /**< Frame colorspace */
int width; /**< Frame width */
int height; /**< Frame height */
int framerate_numerator; /**< Frame rate numerator ((num / denom) == FPS, (denom / num) == duration in seconds) */
int framerate_denominator; /**< Frame rate demoninator ((num / denom) == FPS, (denom / num) == duration in seconds) */
} SDL_CameraSpec;
/**
* The position of camera in relation to system device.
*
* \since This enum is available since SDL 3.0.0.
*
* \sa SDL_GetCameraPosition
*/
typedef enum SDL_CameraPosition
{
SDL_CAMERA_POSITION_UNKNOWN,
SDL_CAMERA_POSITION_FRONT_FACING,
SDL_CAMERA_POSITION_BACK_FACING
} SDL_CameraPosition;
/**
* Use this function to get the number of built-in camera drivers.
*
* This function returns a hardcoded number. This never returns a negative
* value; if there are no drivers compiled into this build of SDL, this
* function returns zero. The presence of a driver in this list does not mean
* it will function, it just means SDL is capable of interacting with that
* interface. For example, a build of SDL might have v4l2 support, but if
* there's no kernel support available, SDL's v4l2 driver would fail if used.
*
* By default, SDL tries all drivers, in its preferred order, until one is
* found to be usable.
*
* \returns the number of built-in camera drivers.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameraDriver
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetNumCameraDrivers(void);
/**
* Use this function to get the name of a built in camera driver.
*
* The list of camera drivers is given in the order that they are normally
* initialized by default; the drivers that seem more reasonable to choose
* first (as far as the SDL developers believe) are earlier in the list.
*
* The names of drivers are all simple, low-ASCII identifiers, like "v4l2",
* "coremedia" or "android". These never have Unicode characters, and are not
* meant to be proper names.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \param index the index of the camera driver; the value ranges from 0 to
* SDL_GetNumCameraDrivers() - 1.
* \returns the name of the camera driver at the requested index, or NULL if
* an invalid index was specified.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetNumCameraDrivers
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_GetCameraDriver(int index);
/**
* Get the name of the current camera driver.
*
* The names of drivers are all simple, low-ASCII identifiers, like "v4l2",
* "coremedia" or "android". These never have Unicode characters, and are not
* meant to be proper names.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \returns the name of the current camera driver or NULL if no driver has
* been initialized.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_GetCurrentCameraDriver(void);
/**
* Get a list of currently connected camera devices.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \param count a pointer filled in with the number of cameras returned, may
* be NULL.
* \returns a 0 terminated array of camera instance IDs or NULL on failure;
* call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
*/
extern SDL_DECLSPEC const SDL_CameraID * SDLCALL SDL_GetCameras(int *count);
/**
* Get the list of native formats/sizes a camera supports.
*
* This returns a list of all formats and frame sizes that a specific camera
* can offer. This is useful if your app can accept a variety of image formats
* and sizes and so want to find the optimal spec that doesn't require
* conversion.
*
* This function isn't strictly required; if you call SDL_OpenCamera with a
* NULL spec, SDL will choose a native format for you, and if you instead
* specify a desired format, it will transparently convert to the requested
* format on your behalf.
*
* If `count` is not NULL, it will be filled with the number of elements in
* the returned array.
*
* Note that it's legal for a camera to supply an empty list. This is what
* will happen on Emscripten builds, since that platform won't tell _anything_
* about available cameras until you've opened one, and won't even tell if
* there _is_ a camera until the user has given you permission to check
* through a scary warning popup.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \param devid the camera device instance ID to query.
* \param count a pointer filled in with the number of elements in the list,
* may be NULL.
* \returns a NULL terminated array of pointers to SDL_CameraSpec or NULL on
* failure; call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameras
* \sa SDL_OpenCamera
*/
extern SDL_DECLSPEC const SDL_CameraSpec * const * SDLCALL SDL_GetCameraSupportedFormats(SDL_CameraID devid, int *count);
/**
* Get the human-readable device name for a camera.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \param instance_id the camera device instance ID.
* \returns a human-readable device name or NULL on failure; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameras
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_GetCameraName(SDL_CameraID instance_id);
/**
* Get the position of the camera in relation to the system.
*
* Most platforms will report UNKNOWN, but mobile devices, like phones, can
* often make a distinction between cameras on the front of the device (that
* points towards the user, for taking "selfies") and cameras on the back (for
* filming in the direction the user is facing).
*
* \param instance_id the camera device instance ID.
* \returns the position of the camera on the system hardware.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameras
*/
extern SDL_DECLSPEC SDL_CameraPosition SDLCALL SDL_GetCameraPosition(SDL_CameraID instance_id);
/**
* Open a video recording device (a "camera").
*
* You can open the device with any reasonable spec, and if the hardware can't
* directly support it, it will convert data seamlessly to the requested
* format. This might incur overhead, including scaling of image data.
*
* If you would rather accept whatever format the device offers, you can pass
* a NULL spec here and it will choose one for you (and you can use
* SDL_Surface's conversion/scaling functions directly if necessary).
*
* You can call SDL_GetCameraFormat() to get the actual data format if passing
* a NULL spec here. You can see the exact specs a device can support without
* conversion with SDL_GetCameraSupportedSpecs().
*
* SDL will not attempt to emulate framerate; it will try to set the hardware
* to the rate closest to the requested speed, but it won't attempt to limit
* or duplicate frames artificially; call SDL_GetCameraFormat() to see the
* actual framerate of the opened the device, and check your timestamps if
* this is crucial to your app!
*
* Note that the camera is not usable until the user approves its use! On some
* platforms, the operating system will prompt the user to permit access to
* the camera, and they can choose Yes or No at that point. Until they do, the
* camera will not be usable. The app should either wait for an
* SDL_EVENT_CAMERA_DEVICE_APPROVED (or SDL_EVENT_CAMERA_DEVICE_DENIED) event,
* or poll SDL_IsCameraApproved() occasionally until it returns non-zero. On
* platforms that don't require explicit user approval (and perhaps in places
* where the user previously permitted access), the approval event might come
* immediately, but it might come seconds, minutes, or hours later!
*
* \param instance_id the camera device instance ID.
* \param spec the desired format for data the device will provide. Can be
* NULL.
* \returns an SDL_Camera object or NULL on failure; call SDL_GetError() for
* more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetCameras
* \sa SDL_GetCameraFormat
*/
extern SDL_DECLSPEC SDL_Camera * SDLCALL SDL_OpenCamera(SDL_CameraID instance_id, const SDL_CameraSpec *spec);
/**
* Query if camera access has been approved by the user.
*
* Cameras will not function between when the device is opened by the app and
* when the user permits access to the hardware. On some platforms, this
* presents as a popup dialog where the user has to explicitly approve access;
* on others the approval might be implicit and not alert the user at all.
*
* This function can be used to check the status of that approval. It will
* return 0 if still waiting for user response, 1 if the camera is approved
* for use, and -1 if the user denied access.
*
* Instead of polling with this function, you can wait for a
* SDL_EVENT_CAMERA_DEVICE_APPROVED (or SDL_EVENT_CAMERA_DEVICE_DENIED) event
* in the standard SDL event loop, which is guaranteed to be sent once when
* permission to use the camera is decided.
*
* If a camera is declined, there's nothing to be done but call
* SDL_CloseCamera() to dispose of it.
*
* \param camera the opened camera device to query.
* \returns -1 if user denied access to the camera, 1 if user approved access,
* 0 if no decision has been made yet.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
* \sa SDL_CloseCamera
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetCameraPermissionState(SDL_Camera *camera);
/**
* Get the instance ID of an opened camera.
*
* \param camera an SDL_Camera to query.
* \returns the instance ID of the specified camera on success or 0 on
* failure; call SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
*/
extern SDL_DECLSPEC SDL_CameraID SDLCALL SDL_GetCameraID(SDL_Camera *camera);
/**
* Get the properties associated with an opened camera.
*
* \param camera the SDL_Camera obtained from SDL_OpenCamera().
* \returns a valid property ID on success or 0 on failure; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera);
/**
* Get the spec that a camera is using when generating images.
*
* Note that this might not be the native format of the hardware, as SDL might
* be converting to this format behind the scenes.
*
* If the system is waiting for the user to approve access to the camera, as
* some platforms require, this will return -1, but this isn't necessarily a
* fatal error; you should either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED
* (or SDL_EVENT_CAMERA_DEVICE_DENIED) event, or poll SDL_IsCameraApproved()
* occasionally until it returns non-zero.
*
* \param camera opened camera device.
* \param spec the SDL_CameraSpec to be initialized by this function.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCamera
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec);
/**
* Acquire a frame.
*
* The frame is a memory pointer to the image data, whose size and format are
* given by the spec requested when opening the device.
*
* This is a non blocking API. If there is a frame available, a non-NULL
* surface is returned, and timestampNS will be filled with a non-zero value.
*
* Note that an error case can also return NULL, but a NULL by itself is
* normal and just signifies that a new frame is not yet available. Note that
* even if a camera device fails outright (a USB camera is unplugged while in
* use, etc), SDL will send an event separately to notify the app, but
* continue to provide blank frames at ongoing intervals until
* SDL_CloseCamera() is called, so real failure here is almost always an out
* of memory condition.
*
* After use, the frame should be released with SDL_ReleaseCameraFrame(). If
* you don't do this, the system may stop providing more video!
*
* Do not call SDL_FreeSurface() on the returned surface! It must be given
* back to the camera subsystem with SDL_ReleaseCameraFrame!
*
* If the system is waiting for the user to approve access to the camera, as
* some platforms require, this will return NULL (no frames available); you
* should either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED (or
* SDL_EVENT_CAMERA_DEVICE_DENIED) event, or poll SDL_IsCameraApproved()
* occasionally until it returns non-zero.
*
* \param camera opened camera device.
* \param timestampNS a pointer filled in with the frame's timestamp, or 0 on
* error. Can be NULL.
* \returns a new frame of video on success, NULL if none is currently
* available.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_ReleaseCameraFrame
*/
extern SDL_DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS);
/**
* Release a frame of video acquired from a camera.
*
* Let the back-end re-use the internal buffer for camera.
*
* This function _must_ be called only on surface objects returned by
* SDL_AcquireCameraFrame(). This function should be called as quickly as
* possible after acquisition, as SDL keeps a small FIFO queue of surfaces for
* video frames; if surfaces aren't released in a timely manner, SDL may drop
* upcoming video frames from the camera.
*
* If the app needs to keep the surface for a significant time, they should
* make a copy of it and release the original.
*
* The app should not use the surface again after calling this function;
* assume the surface is freed and the pointer is invalid.
*
* \param camera opened camera device.
* \param frame the video frame surface to release.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_AcquireCameraFrame
*/
extern SDL_DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame);
/**
* Use this function to shut down camera processing and close the camera
* device.
*
* \param camera opened camera device.
*
* \threadsafety It is safe to call this function from any thread, but no
* thread may reference `device` once this function is called.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_OpenCameraWithSpec
* \sa SDL_OpenCamera
*/
extern SDL_DECLSPEC void SDLCALL SDL_CloseCamera(SDL_Camera *camera);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_camera_h_ */

View File

@@ -0,0 +1,256 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/**
* # CategoryClipboard
*
* SDL provides access to the system clipboard, both for reading information
* from other processes and publishing information of its own.
*
* This is not just text! SDL apps can access and publish data by mimetype.
*/
#ifndef SDL_clipboard_h_
#define SDL_clipboard_h_
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_begin_code.h>
/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* Function prototypes */
/**
* Put UTF-8 text into the clipboard.
*
* \param text the text to store in the clipboard.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetClipboardText
* \sa SDL_HasClipboardText
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetClipboardText(const char *text);
/**
* Get UTF-8 text from the clipboard.
*
* This functions returns empty string if there was not enough memory left for
* a copy of the clipboard's content.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \returns the clipboard text on success or an empty string on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_HasClipboardText
* \sa SDL_SetClipboardText
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_GetClipboardText(void);
/**
* Query whether the clipboard exists and contains a non-empty text string.
*
* \returns SDL_TRUE if the clipboard has text, or SDL_FALSE if it does not.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetClipboardText
* \sa SDL_SetClipboardText
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_HasClipboardText(void);
/**
* Put UTF-8 text into the primary selection.
*
* \param text the text to store in the primary selection.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetPrimarySelectionText
* \sa SDL_HasPrimarySelectionText
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetPrimarySelectionText(const char *text);
/**
* Get UTF-8 text from the primary selection.
*
* This functions returns empty string if there was not enough memory left for
* a copy of the primary selection's content.
*
* This returns temporary memory which will be automatically freed later, and
* can be claimed with SDL_ClaimTemporaryMemory().
*
* \returns the primary selection text on success or an empty string on
* failure; call SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_HasPrimarySelectionText
* \sa SDL_SetPrimarySelectionText
*/
extern SDL_DECLSPEC const char * SDLCALL SDL_GetPrimarySelectionText(void);
/**
* Query whether the primary selection exists and contains a non-empty text
* string.
*
* \returns SDL_TRUE if the primary selection has text, or SDL_FALSE if it
* does not.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_GetPrimarySelectionText
* \sa SDL_SetPrimarySelectionText
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_HasPrimarySelectionText(void);
/**
* Callback function that will be called when data for the specified mime-type
* is requested by the OS.
*
* The callback function is called with NULL as the mime_type when the
* clipboard is cleared or new data is set. The clipboard is automatically
* cleared in SDL_Quit().
*
* \param userdata a pointer to provided user data.
* \param mime_type the requested mime-type.
* \param size a pointer filled in with the length of the returned data.
* \returns a pointer to the data for the provided mime-type. Returning NULL
* or setting length to 0 will cause no data to be sent to the
* "receiver". It is up to the receiver to handle this. Essentially
* returning no data is more or less undefined behavior and may cause
* breakage in receiving applications. The returned data will not be
* freed so it needs to be retained and dealt with internally.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetClipboardData
*/
typedef const void *(SDLCALL *SDL_ClipboardDataCallback)(void *userdata, const char *mime_type, size_t *size);
/**
* Callback function that will be called when the clipboard is cleared, or new
* data is set.
*
* \param userdata a pointer to provided user data.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetClipboardData
*/
typedef void (SDLCALL *SDL_ClipboardCleanupCallback)(void *userdata);
/**
* Offer clipboard data to the OS.
*
* Tell the operating system that the application is offering clipboard data
* for each of the proivded mime-types. Once another application requests the
* data the callback function will be called allowing it to generate and
* respond with the data for the requested mime-type.
*
* The size of text data does not include any terminator, and the text does
* not need to be null terminated (e.g. you can directly copy a portion of a
* document)
*
* \param callback a function pointer to the function that provides the
* clipboard data.
* \param cleanup a function pointer to the function that cleans up the
* clipboard data.
* \param userdata an opaque pointer that will be forwarded to the callbacks.
* \param mime_types a list of mime-types that are being offered.
* \param num_mime_types the number of mime-types in the mime_types list.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_ClearClipboardData
* \sa SDL_GetClipboardData
* \sa SDL_HasClipboardData
*/
extern SDL_DECLSPEC int SDLCALL SDL_SetClipboardData(SDL_ClipboardDataCallback callback, SDL_ClipboardCleanupCallback cleanup, void *userdata, const char **mime_types, size_t num_mime_types);
/**
* Clear the clipboard data.
*
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetClipboardData
*/
extern SDL_DECLSPEC int SDLCALL SDL_ClearClipboardData(void);
/**
* Get the data from clipboard for a given mime type.
*
* The size of text data does not include the terminator, but the text is
* guaranteed to be null terminated.
*
* \param mime_type the mime type to read from the clipboard.
* \param size a pointer filled in with the length of the returned data.
* \returns the retrieved data buffer or NULL on failure; call SDL_GetError()
* for more information.
*
* This returns temporary memory which will be automatically freed
* later, and can be claimed with SDL_ClaimTemporaryMemory().
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_HasClipboardData
* \sa SDL_SetClipboardData
*/
extern SDL_DECLSPEC const void * SDLCALL SDL_GetClipboardData(const char *mime_type, size_t *size);
/**
* Query whether there is data in the clipboard for the provided mime type.
*
* \param mime_type the mime type to check for data for.
* \returns SDL_TRUE if there exists data in clipboard for the provided mime
* type, SDL_FALSE if it does not.
*
* \since This function is available since SDL 3.0.0.
*
* \sa SDL_SetClipboardData
* \sa SDL_GetClipboardData
*/
extern SDL_DECLSPEC SDL_bool SDLCALL SDL_HasClipboardData(const char *mime_type);
/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif
#include <SDL3/SDL_close_code.h>
#endif /* SDL_clipboard_h_ */

View File

@@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* This file reverses the effects of SDL_begin_code.h and should be included
* after you finish any function and structure declarations in your headers
*/
#ifndef SDL_begin_code_h
#error SDL_close_code.h included without matching SDL_begin_code.h
#endif
#undef SDL_begin_code_h
/* Reset structure packing at previous byte alignment */
#if defined(_MSC_VER) || defined(__MWERKS__) || defined(__BORLANDC__)
#ifdef __BORLANDC__
#pragma nopackwarning
#endif
#pragma pack(pop)
#endif /* Compiler needs structure packing set */

View File

@@ -0,0 +1,22 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* Header file containing SDL's license. */

Some files were not shown because too many files have changed in this diff Show More