Compare commits
51 Commits
mac_change
...
cd2e7140af
| Author | SHA1 | Date | |
|---|---|---|---|
| cd2e7140af | |||
| d5b64aece4 | |||
| 146cc13bad | |||
| 865b430ab0 | |||
| a5ad7a4398 | |||
| 89d1cc4270 | |||
| bd81e3e89e | |||
| 37acdc29ff | |||
| ccdc7a5e1d | |||
| fe313773d1 | |||
| 34346e4111 | |||
| 7cbe44c4e5 | |||
| 846f44b882 | |||
| 68764580f2 | |||
|
|
cdf0e24c12 | ||
| ac7087c4ce | |||
| 56e6e53d0c | |||
| 25b6605027 | |||
| c97f6f3b7e | |||
| bc33d1a0f5 | |||
| 5e950833ff | |||
| 9a04cee71f | |||
| b79c9402fd | |||
| b0aed96d18 | |||
| c003506906 | |||
| 473dc4a1a0 | |||
| 8d97e21b28 | |||
| ce932b1739 | |||
| 506ad85cad | |||
| e86ccefd3c | |||
| 71147cd4e9 | |||
| d6fe1f0519 | |||
| ed3d1ce366 | |||
| 37177edeaa | |||
| 7981483a5b | |||
| c3ca359b4b | |||
| 99cf7292aa | |||
| 4c6f39d489 | |||
| dcfc265433 | |||
| 4a7f6c34f3 | |||
| ba248a61bd | |||
| eacd23bdbb | |||
| 62df728452 | |||
| fe0d97aad1 | |||
| 7f7d839f23 | |||
| c445bfd5d4 | |||
| 62843476b7 | |||
| 7595c572d7 | |||
| 13ae091273 | |||
| a9dccd2949 | |||
| f8dd3f23c6 |
4
.github/workflows/build_linux.yml
vendored
4
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev libwayland-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev libwayland-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Redirect to our own Git LFS server
|
||||
[lfs]
|
||||
url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
#url="https://gitlab.flaxengine.com/flax/flaxengine.git/info/lfs"
|
||||
locksverify = false
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"Configuration": {
|
||||
"UseCSharp": true,
|
||||
"UseLargeWorlds": false,
|
||||
"UseDotNet": true
|
||||
"UseDotNet": true,
|
||||
"UseSDL": true
|
||||
}
|
||||
}
|
||||
@@ -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,21 +218,26 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
desc.AllowMaximize = false;
|
||||
desc.AllowDragAndDrop = false;
|
||||
desc.IsTopmost = true;
|
||||
desc.IsRegularWindow = false;
|
||||
desc.Type = WindowType.Popup;
|
||||
desc.Parent = parentWin.Window;
|
||||
desc.HasSizingFrame = false;
|
||||
OnWindowCreating(ref desc);
|
||||
_window = Platform.CreateWindow(ref desc);
|
||||
_window.GotFocus += OnWindowGotFocus;
|
||||
_window.LostFocus += OnWindowLostFocus;
|
||||
|
||||
#if USE_IS_FOREGROUND && USE_SDL_WORKAROUNDS
|
||||
// The focus between popup and parent windows doesn't change, force hide the popup when clicked on parent
|
||||
parentWin.Window.MouseDown += OnWindowMouseDown;
|
||||
_window.Closed += () => parentWin.Window.MouseDown -= OnWindowMouseDown;
|
||||
#endif
|
||||
|
||||
// Attach to the window
|
||||
_parentCM = parent as ContextMenuBase;
|
||||
Parent = _window.GUI;
|
||||
|
||||
// Show
|
||||
Visible = true;
|
||||
if (_window == null)
|
||||
return;
|
||||
_window.Show();
|
||||
PerformLayout();
|
||||
_previouslyFocused = parentWin.FocusedControl;
|
||||
@@ -378,6 +386,17 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_SDL_WORKAROUNDS
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnWindowMouseDown(ref Float2 mousePosition, MouseButton button, ref bool handled)
|
||||
{
|
||||
// The user clicked outside the popup window
|
||||
Hide();
|
||||
}
|
||||
#else
|
||||
private void OnWindowGotFocus()
|
||||
{
|
||||
var child = _childCM;
|
||||
@@ -391,6 +410,7 @@ namespace FlaxEditor.GUI.ContextMenu
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnWindowLostFocus()
|
||||
{
|
||||
@@ -489,7 +509,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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -435,6 +435,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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -167,7 +167,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>
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
|
||||
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Float2 MouseDelta => _mouseDelta;
|
||||
public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;
|
||||
|
||||
@@ -158,18 +158,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).
|
||||
@@ -525,10 +529,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;
|
||||
@@ -1456,7 +1461,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);
|
||||
@@ -1465,6 +1472,7 @@ namespace FlaxEditor.Viewport
|
||||
_viewMousePos = center;
|
||||
win.MousePosition = PointToWindow(_viewMousePos);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1476,6 +1484,7 @@ namespace FlaxEditor.Viewport
|
||||
// Restore cursor and stop tracking mouse movement
|
||||
win.Cursor = CursorType.Default;
|
||||
win.EndTrackingMouse();
|
||||
win.MouseMoveRelative -= OnMouseMoveRelative;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1580,6 +1589,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));
|
||||
@@ -1587,6 +1604,7 @@ namespace FlaxEditor.Viewport
|
||||
_input.Gather(win.Window, useMouse, ref _prevInput);
|
||||
else
|
||||
_input.Clear();
|
||||
#endif
|
||||
|
||||
// Track controlling mouse state change
|
||||
bool wasControllingMouse = _prevInput.IsControllingMouse;
|
||||
@@ -1695,6 +1713,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)
|
||||
@@ -1736,6 +1758,7 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
|
||||
_mouseDeltaLast = currentDelta;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update
|
||||
moveDelta *= dt * (60.0f * 4.0f);
|
||||
@@ -1744,12 +1767,14 @@ namespace FlaxEditor.Viewport
|
||||
mouseDelta *= new Float2(1, -1);
|
||||
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)
|
||||
@@ -1761,6 +1786,8 @@ namespace FlaxEditor.Viewport
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
#else
|
||||
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
|
||||
{
|
||||
// Calculate smooth mouse delta not dependant on viewport size
|
||||
@@ -1775,6 +1802,7 @@ namespace FlaxEditor.Viewport
|
||||
_mouseDelta = Float2.Zero;
|
||||
}
|
||||
_mouseDeltaLast = Float2.Zero;
|
||||
#endif
|
||||
|
||||
if (ContainsFocus)
|
||||
{
|
||||
@@ -1824,6 +1852,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)
|
||||
{
|
||||
|
||||
@@ -1068,8 +1068,11 @@ namespace FlaxEditor.Windows
|
||||
|
||||
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused)
|
||||
{
|
||||
// Make sure the cursor is always in the viewport when cursor is locked
|
||||
bool forceCenter = _cursorLockMode != CursorLockMode.None && !IsMouseOver;
|
||||
|
||||
// Center mouse in play mode
|
||||
if (CenterMouseOnFocus)
|
||||
if (CenterMouseOnFocus || forceCenter)
|
||||
{
|
||||
var center = PointToWindow(Size * 0.5f);
|
||||
Root.MousePosition = center;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace FlaxEditor.Windows.Profiler
|
||||
|
||||
private string FormatCellBytes(object x)
|
||||
{
|
||||
return Utilities.Utils.FormatBytesCount((ulong)x);
|
||||
return Utilities.Utils.FormatBytesCount((ulong)(int)x);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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"),
|
||||
@@ -164,7 +165,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;
|
||||
|
||||
@@ -145,6 +145,12 @@ bool CommandLine::Parse(const Char* cmdLine)
|
||||
PARSE_BOOL_SWITCH("-monolog ", MonoLog);
|
||||
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);
|
||||
PARSE_BOOL_SWITCH("-clearcooker ", ClearCookerCache);
|
||||
|
||||
@@ -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>
|
||||
/// -project !path! (Startup project path)
|
||||
|
||||
@@ -95,13 +95,14 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
CommandLine::Options.Std = true;
|
||||
#endif
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
|
||||
if (Platform::Init())
|
||||
{
|
||||
Platform::Fatal(TEXT("Cannot init platform."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
|
||||
Time::StartupTime = DateTime::Now();
|
||||
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Types/Nullable.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/Managed/ManagedEditor.h"
|
||||
@@ -13,10 +15,14 @@
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#endif
|
||||
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
namespace
|
||||
{
|
||||
Nullable<bool> Fullscreen;
|
||||
Nullable<Float2> Size;
|
||||
bool CursorVisible = true;
|
||||
CursorLockMode CursorLock = CursorLockMode::None;
|
||||
bool LastGameViewportFocus = false;
|
||||
}
|
||||
|
||||
class ScreenService : public EngineService
|
||||
{
|
||||
@@ -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, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
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, win);
|
||||
}
|
||||
else if (CursorLock == CursorLockMode::None || CursorVisible || !focused)
|
||||
{
|
||||
Input::Mouse->SetRelativeMode(false, win);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "AndroidVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -17,8 +18,10 @@ void AndroidVulkanPlatform::GetDeviceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void AndroidVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void AndroidVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
ASSERT(window);
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
ASSERT(windowHandle);
|
||||
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);
|
||||
|
||||
@@ -17,7 +17,7 @@ class AndroidVulkanPlatform : public VulkanPlatformBase
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void GetDeviceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef AndroidVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -192,7 +192,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
|
||||
ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
|
||||
|
||||
// Create platform-dependent surface
|
||||
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface);
|
||||
VulkanPlatform::CreateSurface(_window, GPUDeviceVulkan::Instance, &_surface);
|
||||
if (_surface == VK_NULL_HANDLE)
|
||||
{
|
||||
LOG(Warning, "Failed to create Vulkan surface.");
|
||||
|
||||
@@ -4,62 +4,62 @@
|
||||
|
||||
#include "LinuxVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
// Contents of vulkan\vulkan_xlib.h inlined here to prevent typename collisions with engine types due to X11 types
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#define VK_KHR_xlib_surface 1
|
||||
#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6
|
||||
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface"
|
||||
typedef VkFlags VkXlibSurfaceCreateFlagsKHR;
|
||||
#define Display X11::Display
|
||||
#define Window X11::Window
|
||||
#define VisualID X11::VisualID
|
||||
#include "vulkan/vulkan_xlib.h"
|
||||
#undef Display
|
||||
#undef Window
|
||||
#undef VisualID
|
||||
|
||||
typedef struct VkXlibSurfaceCreateInfoKHR
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkXlibSurfaceCreateFlagsKHR flags;
|
||||
X11::Display* dpy;
|
||||
X11::Window window;
|
||||
} VkXlibSurfaceCreateInfoKHR;
|
||||
#include "vulkan/vulkan_wayland.h"
|
||||
|
||||
typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
|
||||
typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, X11::Display* dpy, X11::VisualID visualID);
|
||||
#ifndef VK_NO_PROTOTYPES
|
||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR(
|
||||
VkInstance instance,
|
||||
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
|
||||
const VkAllocationCallbacks* pAllocator,
|
||||
VkSurfaceKHR* pSurface);
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR(
|
||||
VkPhysicalDevice physicalDevice,
|
||||
uint32_t queueFamilyIndex,
|
||||
Display* dpy,
|
||||
VisualID visualID);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
//
|
||||
|
||||
// Export X11 surface extension from volk
|
||||
// Export extension from volk
|
||||
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
|
||||
extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
|
||||
extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
|
||||
|
||||
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
extensions.Add(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void LinuxVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void LinuxVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)LinuxPlatform::GetXDisplay();
|
||||
surfaceCreateInfo.dpy = (X11::Display*)Platform::GetXDisplay();
|
||||
surfaceCreateInfo.window = (X11::Window)windowHandle;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
#else
|
||||
SDLWindow* sdlWindow = static_cast<Window*>(window);
|
||||
X11::Window x11Window = (X11::Window)sdlWindow->GetX11WindowHandle();
|
||||
wl_surface* waylandSurface = (wl_surface*)sdlWindow->GetWaylandSurfacePtr();
|
||||
if (waylandSurface != nullptr)
|
||||
{
|
||||
VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.display = (wl_display*)sdlWindow->GetWaylandDisplay();
|
||||
surfaceCreateInfo.surface = waylandSurface;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateWaylandSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
else if (x11Window != 0)
|
||||
{
|
||||
VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.dpy = (X11::Display*)sdlWindow->GetX11Display();
|
||||
surfaceCreateInfo.window = x11Window;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@ class LinuxVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef LinuxVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "MacVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -12,12 +13,13 @@ void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void MacVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void MacVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
NSWindow* window = (NSWindow*)windowHandle;
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
NSWindow* nswindow = (NSWindow*)windowHandle;
|
||||
VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK);
|
||||
surfaceCreateInfo.pView = (void*)window.contentView;
|
||||
surfaceCreateInfo.pView = (void*)nswindow.contentView;
|
||||
VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class MacVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef MacVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
|
||||
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
{
|
||||
@@ -13,8 +14,9 @@ void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions,
|
||||
extensions.Add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void Win32VulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void Win32VulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
|
||||
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
|
||||
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);
|
||||
|
||||
@@ -17,7 +17,7 @@ class Win32VulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface);
|
||||
};
|
||||
|
||||
typedef Win32VulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "iOSVulkanPlatform.h"
|
||||
#include "../RenderToolsVulkan.h"
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
|
||||
@@ -13,8 +14,9 @@ void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Ar
|
||||
extensions.Add(VK_MVK_IOS_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
void iOSVulkanPlatform::CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* surface)
|
||||
void iOSVulkanPlatform::CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* surface)
|
||||
{
|
||||
void* windowHandle = window->GetNativePtr();
|
||||
// Create surface on a main UI Thread
|
||||
Function<void()> func = [&windowHandle, &instance, &surface]()
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ class iOSVulkanPlatform : public VulkanPlatformBase
|
||||
{
|
||||
public:
|
||||
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
|
||||
static void CreateSurface(void* windowHandle, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
static void CreateSurface(Window* window, VkInstance instance, VkSurfaceKHR* outSurface);
|
||||
};
|
||||
|
||||
typedef iOSVulkanPlatform VulkanPlatform;
|
||||
|
||||
@@ -79,6 +79,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;
|
||||
@@ -209,6 +210,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();
|
||||
@@ -274,6 +283,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;
|
||||
@@ -731,6 +745,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;
|
||||
@@ -787,6 +804,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;
|
||||
@@ -1000,12 +1020,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())
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,17 @@ 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>
|
||||
/// <param name="window">The window.</param>
|
||||
virtual void SetRelativeMode(bool relativeMode, Window* window)
|
||||
{
|
||||
_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 +179,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>
|
||||
|
||||
251
Source/Engine/Platform/Base/Enums.h
Normal file
251
Source/Engine/Platform/Base/Enums.h
Normal 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,
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
42
Source/Engine/Platform/Base/ScreenUtilitiesBase.h
Normal file
42
Source/Engine/Platform/Base/ScreenUtilitiesBase.h
Normal 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;
|
||||
};
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace FlaxEngine
|
||||
AllowMinimize = true,
|
||||
AllowMaximize = true,
|
||||
AllowDragAndDrop = true,
|
||||
IsRegularWindow = true,
|
||||
Type = WindowType.Regular,
|
||||
HasSizingFrame = true,
|
||||
ShowAfterFirstPaint = true,
|
||||
};
|
||||
|
||||
@@ -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("Use Type instead") 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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -71,7 +71,7 @@ GDKWindow::GDKWindow(const CreateWindowSettings& settings)
|
||||
// Creating the window
|
||||
_handle = CreateWindowExW(
|
||||
exStyle,
|
||||
Platform::ApplicationWindowClass,
|
||||
Platform::ApplicationClassName,
|
||||
settings.Title.GetText(),
|
||||
style,
|
||||
x,
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace X11
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
}
|
||||
|
||||
// Helper macros
|
||||
|
||||
@@ -93,7 +93,9 @@ X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
Array<KeyboardKeys> KeyCodeMap;
|
||||
Delegate<void*> LinuxPlatform::xEventRecieved;
|
||||
#if !PLATFORM_SDL
|
||||
Delegate<void*> LinuxPlatform::xEventReceived;
|
||||
#endif
|
||||
Window* MouseTrackingWindow = nullptr;
|
||||
|
||||
// Message boxes configuration
|
||||
@@ -651,7 +653,11 @@ static int X11_MessageBoxLoop(MessageBoxData* data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
#else
|
||||
DialogResult MessageBox::ShowFallback(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
#endif
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return DialogResult::None;
|
||||
@@ -838,6 +844,8 @@ DialogResult MessageBox::Show(Window* parent, const StringView& text, const Stri
|
||||
return data.resultButtonIndex == -1 ? DialogResult::None : data.buttons[data.resultButtonIndex].result;
|
||||
}
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
|
||||
int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
{
|
||||
if (event->error_code == 5)
|
||||
@@ -848,6 +856,8 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 CalculateDpi()
|
||||
{
|
||||
int dpi = 96;
|
||||
@@ -1203,17 +1213,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
#if !PLATFORM_SDL
|
||||
struct Property
|
||||
{
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
unsigned char* data;
|
||||
int format, nitems;
|
||||
X11::Atom type;
|
||||
};
|
||||
#endif
|
||||
|
||||
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 +1341,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 +1382,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
|
||||
DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
{
|
||||
if (CommandLine::Options.Headless)
|
||||
return DragDropEffect::None;
|
||||
@@ -1381,13 +1396,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 +1515,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 +1544,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 +1579,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 +1622,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 +1669,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 +1695,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 +1718,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 +1747,11 @@ Array<String> LinuxClipboard::GetFiles()
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void* LinuxPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
return xDisplay;
|
||||
}
|
||||
|
||||
bool LinuxPlatform::CreateMutex(const Char* name)
|
||||
@@ -2117,10 +2139,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 +2286,7 @@ bool LinuxPlatform::Init()
|
||||
Input::Mouse = Impl::Mouse = New<LinuxMouse>();
|
||||
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
|
||||
LinuxInput::Init();
|
||||
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2269,6 +2296,7 @@ void LinuxPlatform::BeforeRun()
|
||||
|
||||
void LinuxPlatform::Tick()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
UnixPlatform::Tick();
|
||||
|
||||
LinuxInput::UpdateState();
|
||||
@@ -2285,9 +2313,9 @@ void LinuxPlatform::Tick()
|
||||
continue;
|
||||
|
||||
// External event handling
|
||||
xEventRecieved(&event);
|
||||
xEventReceived(&event);
|
||||
|
||||
LinuxWindow* window;
|
||||
Window* window;
|
||||
switch (event.type)
|
||||
{
|
||||
case ClientMessage:
|
||||
@@ -2619,6 +2647,7 @@ void LinuxPlatform::Tick()
|
||||
}
|
||||
|
||||
//X11::XFlush(xDisplay);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LinuxPlatform::BeforeExit()
|
||||
@@ -2627,6 +2656,7 @@ void LinuxPlatform::BeforeExit()
|
||||
|
||||
void LinuxPlatform::Exit()
|
||||
{
|
||||
#if !PLATFORM_SDL
|
||||
for (int32 i = 0; i < (int32)CursorType::MAX; i++)
|
||||
{
|
||||
if (Cursors[i])
|
||||
@@ -2652,6 +2682,7 @@ void LinuxPlatform::Exit()
|
||||
X11::XCloseDisplay(xDisplay);
|
||||
xDisplay = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 LinuxPlatform::GetDpi()
|
||||
@@ -2872,7 +2903,11 @@ bool LinuxPlatform::SetWorkingDirectory(const String& path)
|
||||
|
||||
Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings)
|
||||
{
|
||||
#if PLATFORM_SDL
|
||||
return New<SDLWindow>(settings);
|
||||
#else
|
||||
return New<LinuxWindow>(settings);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern char **environ;
|
||||
|
||||
@@ -36,7 +36,7 @@ public:
|
||||
/// <summary>
|
||||
/// An event that is fired when an XEvent is received during platform tick.
|
||||
/// </summary>
|
||||
static Delegate<void*> xEventRecieved;
|
||||
static Delegate<void*> xEventReceived;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
94
Source/Engine/Platform/Linux/LinuxScreenUtilities.cpp
Normal file
94
Source/Engine/Platform/Linux/LinuxScreenUtilities.cpp
Normal 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
|
||||
25
Source/Engine/Platform/Linux/LinuxScreenUtilities.h
Normal file
25
Source/Engine/Platform/Linux/LinuxScreenUtilities.h
Normal 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
|
||||
@@ -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) &&
|
||||
|
||||
29
Source/Engine/Platform/Mac/MacScreenUtilities.cpp
Normal file
29
Source/Engine/Platform/Mac/MacScreenUtilities.cpp
Normal 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
|
||||
25
Source/Engine/Platform/Mac/MacScreenUtilities.h
Normal file
25
Source/Engine/Platform/Mac/MacScreenUtilities.h
Normal 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
|
||||
@@ -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);
|
||||
|
||||
@@ -237,4 +237,9 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(MessageBox);
|
||||
/// <param name="icon">One of the MessageBoxIcon values that specifies which icon to display in the message box.</param>
|
||||
/// <returns>The message box dialog result.</returns>
|
||||
API_FUNCTION() static DialogResult Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon);
|
||||
|
||||
private:
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
static DialogResult ShowFallback(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon);
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -89,6 +89,19 @@ public class Platform : EngineModule
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (EngineConfiguration.WithSDL(options))
|
||||
{
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Mac:
|
||||
options.PublicDependencies.Add("SDL");
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (options.Target.IsEditor)
|
||||
{
|
||||
// Include platform settings headers
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "Types.h"
|
||||
#include "Defines.h"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLPlatform.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsPlatform.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPPlatform.h"
|
||||
|
||||
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
26
Source/Engine/Platform/SDL/SDLClipboard.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Base/ClipboardBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// SDL implementation of the clipboard service for Linux platform.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLClipboard : public ClipboardBase
|
||||
{
|
||||
public:
|
||||
|
||||
// [ClipboardBase]
|
||||
static void Clear();
|
||||
static void SetText(const StringView& text);
|
||||
static void SetRawData(const Span<byte>& data);
|
||||
static void SetFiles(const Array<String>& files);
|
||||
static String GetText();
|
||||
static Array<byte> GetRawData();
|
||||
static Array<String> GetFiles();
|
||||
};
|
||||
|
||||
#endif
|
||||
787
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
787
Source/Engine/Platform/SDL/SDLInput.cpp
Normal file
@@ -0,0 +1,787 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "SDLInput.h"
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Input/Input.h"
|
||||
#include "Engine/Input/Mouse.h"
|
||||
#include "Engine/Input/Keyboard.h"
|
||||
#include "Engine/Input/Gamepad.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:
|
||||
|
||||
/// <summary>
|
||||
/// Returns the previous known position of the mouse before entering relative mode.
|
||||
/// </summary>
|
||||
Float2 GetOldMousePosition() const
|
||||
{
|
||||
return oldPosition;
|
||||
}
|
||||
|
||||
// [Mouse]
|
||||
void SetMousePosition(const Float2& newPosition) final override
|
||||
{
|
||||
SDL_WarpMouseGlobal(newPosition.X, newPosition.Y);
|
||||
|
||||
OnMouseMoved(newPosition);
|
||||
}
|
||||
|
||||
void SetRelativeMode(bool relativeMode, Window* window) final override
|
||||
{
|
||||
if (relativeMode == _relativeMode)
|
||||
return;
|
||||
|
||||
if (relativeMode)
|
||||
SDL_GetGlobalMouseState(&oldPosition.X, &oldPosition.Y);
|
||||
|
||||
Mouse::SetRelativeMode(relativeMode, window);
|
||||
if (!SDL_SetWindowRelativeMouseMode(static_cast<SDLWindow*>(window)->_window, relativeMode))
|
||||
LOG(Error, "Failed to set mouse relative mode: {0}", String(SDL_GetError()));
|
||||
|
||||
if (!relativeMode)
|
||||
SetMousePosition(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, bool pressed);
|
||||
|
||||
// [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 });
|
||||
//LOG(Info, "motion {},{}, mouse: {}", event.motion.x, event.motion.y, mousePos);
|
||||
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->GetOldMousePosition();
|
||||
}
|
||||
|
||||
if (!event.button.down)
|
||||
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->GetOldMousePosition();
|
||||
}
|
||||
|
||||
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.down)
|
||||
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.down);
|
||||
|
||||
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, bool pressed)
|
||||
{
|
||||
switch (sdlButton)
|
||||
{
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_SOUTH:
|
||||
_state.Buttons[(int32)GamepadButton::A] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_EAST:
|
||||
_state.Buttons[(int32)GamepadButton::B] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_WEST:
|
||||
_state.Buttons[(int32)GamepadButton::X] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_NORTH:
|
||||
_state.Buttons[(int32)GamepadButton::Y] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::LeftShoulder] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER:
|
||||
_state.Buttons[(int32)GamepadButton::RightShoulder] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_BACK:
|
||||
_state.Buttons[(int32)GamepadButton::Back] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_START:
|
||||
_state.Buttons[(int32)GamepadButton::Start] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_LEFT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::LeftThumb] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_RIGHT_STICK:
|
||||
_state.Buttons[(int32)GamepadButton::RightThumb] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_UP:
|
||||
_state.Buttons[(int32)GamepadButton::DPadUp] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_DOWN:
|
||||
_state.Buttons[(int32)GamepadButton::DPadDown] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_LEFT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadLeft] = pressed;
|
||||
break;
|
||||
case SDL_GamepadButton::SDL_GAMEPAD_BUTTON_DPAD_RIGHT:
|
||||
_state.Buttons[(int32)GamepadButton::DPadRight] = pressed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
22
Source/Engine/Platform/SDL/SDLInput.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
/// <summary>
|
||||
/// SDL specific implementation of the input system parts.
|
||||
/// </summary>
|
||||
class SDLInput
|
||||
{
|
||||
public:
|
||||
|
||||
static void Init();
|
||||
static void Update();
|
||||
static bool HandleEvent(SDLWindow* window, SDL_Event& event);
|
||||
};
|
||||
|
||||
#endif
|
||||
937
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
937
Source/Engine/Platform/SDL/SDLPlatform.Linux.cpp
Normal file
@@ -0,0 +1,937 @@
|
||||
// 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;
|
||||
bool UseXWayland = 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
|
||||
LOG(Warning, "Wayland Drag and drop is not implemented yet.");
|
||||
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
|
||||
{
|
||||
LOG(Warning, "Wayland clipboard support is not implemented yet."); // 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
|
||||
{
|
||||
LOG(Warning, "Wayland clipboard is not implemented yet."); // TODO: Wayland
|
||||
return String::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
Array<byte> SDLClipboard::GetRawData()
|
||||
{
|
||||
return Array<byte>();
|
||||
}
|
||||
|
||||
Array<String> SDLClipboard::GetFiles()
|
||||
{
|
||||
return Array<String>();
|
||||
}
|
||||
|
||||
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 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 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 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 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 false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (event.type == SelectionRequest)
|
||||
{
|
||||
if (event.xselectionrequest.selection != xAtomClipboard)
|
||||
return 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 false;
|
||||
}
|
||||
else if (event.type == SelectionClear)
|
||||
return false;
|
||||
else if (event.type == XFixesSelectionNotifyEvent)
|
||||
return false;
|
||||
return 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;
|
||||
String waylandDisplay;
|
||||
if (/*!UseWayland &&*/ !Platform::GetEnvironmentVariable(TEXT("WAYLAND_DISPLAY"), waylandDisplay))
|
||||
UseXWayland = waylandDisplay.Length() > 1;
|
||||
}
|
||||
|
||||
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 UseXWayland;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return !UseWayland;
|
||||
}
|
||||
|
||||
#endif
|
||||
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
12
Source/Engine/Platform/SDL/SDLPlatform.Mac.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_MAC
|
||||
|
||||
static_assert(false, "TODO");
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
// TODO: This is now called before Platform::Init, ensure the scaling is changed accordingly during Platform::Init (see ApplePlatform::SetHighDpiAwarenessEnabled)
|
||||
}
|
||||
|
||||
#endif
|
||||
64
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
64
Source/Engine/Platform/SDL/SDLPlatform.Windows.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WINDOWS
|
||||
|
||||
#include "SDLPlatform.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
|
||||
// The events for releasing the mouse during window dragging are missing, handle the mouse release event here
|
||||
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 false;
|
||||
}
|
||||
return 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
|
||||
464
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
464
Source/Engine/Platform/SDL/SDLPlatform.cpp
Normal file
@@ -0,0 +1,464 @@
|
||||
// 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);
|
||||
// If the hint is not present, SDL will prefer more stable X11 driver over Wayland
|
||||
#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"); // Is this needed?
|
||||
|
||||
//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, false);
|
||||
|
||||
// Disable SDL drag and drop support
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_TEXT, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_BEGIN, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
|
||||
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
|
||||
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;
|
||||
return handled;
|
||||
}
|
||||
|
||||
void SDLPlatform::Tick()
|
||||
{
|
||||
SDLInput::Update();
|
||||
|
||||
if (DraggedWindowId != 0)
|
||||
{
|
||||
Float2 mousePos;
|
||||
auto buttons = SDL_GetGlobalMouseState(&mousePos.X, &mousePos.Y);
|
||||
if (!(buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT)))
|
||||
{
|
||||
Window* window = nullptr;
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (int32 i = 0; i < WindowsManager::Windows.Count(); i++)
|
||||
{
|
||||
if (WindowsManager::Windows[i]->_windowId == DraggedWindowId)
|
||||
{
|
||||
window = WindowsManager::Windows[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
int top, left, bottom, right;
|
||||
SDL_GetWindowBordersSize(window->_window, &top, &left, &bottom, &right);
|
||||
mousePos += Float2(static_cast<float>(left), static_cast<float>(-top));
|
||||
Input::Mouse->OnMouseUp(mousePos, MouseButton::Left, window);
|
||||
}
|
||||
DraggedWindowId = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
String dockHintWindow("DockHint.Window");
|
||||
Window* window = nullptr;
|
||||
WindowsManager::WindowsLocker.Lock();
|
||||
for (int32 i = 0; i < WindowsManager::Windows.Count(); i++)
|
||||
{
|
||||
if (WindowsManager::Windows[i]->_title.Compare(dockHintWindow) == 0)
|
||||
//if (WindowsManager::Windows[i]->_windowId == DraggedWindowId)
|
||||
{
|
||||
window = WindowsManager::Windows[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
WindowsManager::WindowsLocker.Unlock();
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
int top, left, bottom, right;
|
||||
SDL_GetWindowBordersSize(window->_window, &top, &left, &bottom, &right);
|
||||
mousePos += Float2(static_cast<float>(left), static_cast<float>(-top));
|
||||
Input::Mouse->OnMouseMove(mousePos, window);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PumpEvents();
|
||||
SDL_Event events[32];
|
||||
int 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]);
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
SDL_free((void*)displays);
|
||||
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))
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
// Fallback to native messagebox implementation in case some system fonts are missing
|
||||
if (SDLPlatform::UsesX11())
|
||||
{
|
||||
LOG(Warning, "Failed to show SDL message box: {0}", String(SDL_GetError()));
|
||||
return ShowFallback(parent, text, caption, buttons, icon);
|
||||
}
|
||||
#endif
|
||||
LOG(Error, "Failed to show SDL message box: {0}", String(SDL_GetError()));
|
||||
return DialogResult::Abort;
|
||||
}
|
||||
if (result < 0)
|
||||
return DialogResult::None;
|
||||
return (DialogResult)result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
82
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
82
Source/Engine/Platform/SDL/SDLPlatform.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_SDL
|
||||
|
||||
#include "Engine/Platform/Base/Enums.h"
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Windows/WindowsPlatform.h"
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
#else
|
||||
#endif
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
#if PLATFORM_WINDOWS
|
||||
typedef struct tagMSG MSG;
|
||||
#elif PLATFORM_LINUX
|
||||
union _XEvent;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The Windows platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SDLPlatform
|
||||
#if PLATFORM_WINDOWS
|
||||
: public WindowsPlatform
|
||||
{
|
||||
using base = WindowsPlatform;
|
||||
#elif PLATFORM_LINUX
|
||||
: public LinuxPlatform
|
||||
{
|
||||
using base = LinuxPlatform;
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
friend SDLWindow;
|
||||
|
||||
private:
|
||||
static uint32 DraggedWindowId;
|
||||
|
||||
private:
|
||||
static bool InitPlatform();
|
||||
#if PLATFORM_LINUX
|
||||
static bool InitPlatformX11(void* display);
|
||||
#endif
|
||||
static bool HandleEvent(SDL_Event& event);
|
||||
#if PLATFORM_WINDOWS
|
||||
static bool __cdecl EventMessageHook(void* userdata, MSG* msg);
|
||||
#elif PLATFORM_LINUX
|
||||
static bool __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
|
||||
1453
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
1453
Source/Engine/Platform/SDL/SDLWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
126
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
126
Source/Engine/Platform/SDL/SDLWindow.h
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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;
|
||||
#if PLATFORM_LINUX
|
||||
class MessageBox;
|
||||
#endif
|
||||
|
||||
/// <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;
|
||||
friend class SDLMouse;
|
||||
#if PLATFORM_LINUX
|
||||
friend LinuxPlatform;
|
||||
friend MessageBox;
|
||||
#endif
|
||||
|
||||
private:
|
||||
void* _handle; // Opaque, platform specific window handle
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
Windows::ULONG _refCount;
|
||||
#endif
|
||||
#if PLATFORM_LINUX
|
||||
bool _dragOver;
|
||||
bool _forcedFocus;
|
||||
#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* GetWindowWithSDLWindow(SDL_Window* window);
|
||||
void HandleEvent(SDL_Event& event);
|
||||
void CheckForWindowResize();
|
||||
void UpdateCursor();
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
DragDropEffect DoDragDropWayland(const StringView& data);
|
||||
DragDropEffect DoDragDropX11(const StringView& data);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
SDL_Window* GetSDLWindow() const;
|
||||
#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
|
||||
15
Source/Engine/Platform/ScreenUtilities.h
Normal file
15
Source/Engine/Platform/ScreenUtilities.h
Normal 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"
|
||||
@@ -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,21 +43,23 @@ 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;
|
||||
typedef Win32Network Network;
|
||||
class UserBase;
|
||||
typedef UserBase User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_LINUX
|
||||
|
||||
class LinuxClipboard;
|
||||
typedef LinuxClipboard Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixConditionVariable;
|
||||
@@ -66,21 +70,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,21 +99,23 @@ 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;
|
||||
typedef PS4Network Network;
|
||||
class PS4User;
|
||||
typedef PS4User User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_PS5
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixConditionVariable;
|
||||
@@ -116,21 +126,23 @@ 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;
|
||||
typedef PS5Network Network;
|
||||
class PS5User;
|
||||
typedef PS5User User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_XBOX_ONE
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ConditionVariable;
|
||||
@@ -141,21 +153,23 @@ 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;
|
||||
typedef Win32Network Network;
|
||||
class GDKUser;
|
||||
typedef GDKUser User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_XBOX_SCARLETT
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class Win32CriticalSection;
|
||||
typedef Win32CriticalSection CriticalSection;
|
||||
class Win32ConditionVariable;
|
||||
@@ -166,21 +180,23 @@ 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;
|
||||
typedef Win32Network Network;
|
||||
class GDKUser;
|
||||
typedef GDKUser User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_ANDROID
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixConditionVariable;
|
||||
@@ -191,21 +207,23 @@ 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;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
typedef UserBase User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_SWITCH
|
||||
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class SwitchCriticalSection;
|
||||
typedef SwitchCriticalSection CriticalSection;
|
||||
class SwitchConditionVariable;
|
||||
@@ -216,21 +234,23 @@ 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;
|
||||
typedef SwitchNetwork Network;
|
||||
class SwitchUser;
|
||||
typedef SwitchUser User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#elif PLATFORM_MAC
|
||||
|
||||
class MacClipboard;
|
||||
typedef MacClipboard Clipboard;
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixConditionVariable;
|
||||
@@ -241,21 +261,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,19 +288,34 @@ 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;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
typedef UserBase User;
|
||||
class ScreenUtilitiesBase;
|
||||
typedef ScreenUtilitiesBase ScreenUtilities;
|
||||
|
||||
#else
|
||||
|
||||
#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
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_SDL
|
||||
#include "SDL/SDLWindow.h"
|
||||
#elif PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsWindow.h"
|
||||
#elif PLATFORM_UWP
|
||||
#include "UWP/UWPWindow.h"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
#include "WindowsWindow.h"
|
||||
#include "WindowsFileSystem.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/Window.h"
|
||||
@@ -317,7 +318,7 @@ bool WindowsFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const Strin
|
||||
if (SUCCEEDED(SHCreateItemFromParsingName(initialDirectory.Get(), NULL, IID_PPV_ARGS(&defaultFolder))))
|
||||
fd->SetFolder(defaultFolder);
|
||||
|
||||
HWND hwndOwner = parentWindow ? parentWindow->GetHWND() : NULL;
|
||||
HWND hwndOwner = parentWindow ? (HWND)parentWindow->GetNativePtr() : NULL;
|
||||
if (SUCCEEDED(fd->Show(hwndOwner)))
|
||||
{
|
||||
ComPtr<IShellItem> si;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#if PLATFORM_WINDOWS && !PLATFORM_SDL
|
||||
|
||||
#include "WindowsInput.h"
|
||||
#include "WindowsWindow.h"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
54
Source/Engine/Platform/Windows/WindowsScreenUtilities.cpp
Normal file
54
Source/Engine/Platform/Windows/WindowsScreenUtilities.cpp
Normal 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
|
||||
25
Source/Engine/Platform/Windows/WindowsScreenUtilities.h
Normal file
25
Source/Engine/Platform/Windows/WindowsScreenUtilities.h
Normal 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
|
||||
661
Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp
Normal file
661
Source/Engine/Platform/Windows/WindowsWindow.DragDrop.cpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -771,6 +771,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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor;
|
||||
|
||||
namespace FlaxEngine.GUI
|
||||
{
|
||||
@@ -77,8 +78,14 @@ 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);
|
||||
var ppp = Input.MouseScreenPosition;
|
||||
//locationSS = new Float2(5, 5);
|
||||
var bef = locationSS;
|
||||
|
||||
// Create window
|
||||
var desc = CreateWindowSettings.Default;
|
||||
@@ -95,7 +102,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);
|
||||
@@ -107,6 +114,9 @@ namespace FlaxEngine.GUI
|
||||
Visible = true;
|
||||
_window.Show();
|
||||
_showTarget.OnTooltipShown(this);
|
||||
|
||||
var aff = _window.Position;
|
||||
Editor.Log($"tooltip startpos: before: {bef}, after {aff}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -222,9 +232,16 @@ namespace FlaxEngine.GUI
|
||||
else
|
||||
{
|
||||
// Position tooltip when mouse moves
|
||||
var ppp = Input.MouseScreenPosition;
|
||||
var bef = _window.Position;
|
||||
WrapPosition(ref mousePos, 10);
|
||||
mousePos += new Float2(15, 10);
|
||||
//mousePos = new Float2(5, 5);
|
||||
|
||||
if (_window)
|
||||
_window.Position = mousePos + new Float2(15, 10);
|
||||
_window.Position = mousePos;
|
||||
var aff = _window.Position;
|
||||
Editor.Log($"tooltip updatepos: before: {bef}, new {mousePos}, after {aff}");
|
||||
}
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
@@ -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/Windows/Binaries/ThirdParty/x64/SDL3.lib
(Stored with Git LFS)
vendored
Normal file
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
18
Source/ThirdParty/SDL/LICENSE.txt
vendored
Normal 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
53
Source/ThirdParty/SDL/SDL.Build.cs
vendored
Normal 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"));
|
||||
}
|
||||
}
|
||||
90
Source/ThirdParty/SDL/SDL3/SDL.h
vendored
Normal file
90
Source/ThirdParty/SDL/SDL3/SDL.h
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main include header for the SDL library, version 3.1.7
|
||||
*
|
||||
* It is almost always best to include just this one header instead of
|
||||
* picking out individual headers included here. There are exceptions to
|
||||
* this rule--SDL_main.h is special and not included here--but usually
|
||||
* letting SDL.h include the kitchen sink for you is the correct approach.
|
||||
*/
|
||||
|
||||
#ifndef SDL_h_
|
||||
#define SDL_h_
|
||||
|
||||
#include <SDL3/SDL_stdinc.h>
|
||||
#include <SDL3/SDL_assert.h>
|
||||
#include <SDL3/SDL_asyncio.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_gpu.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_process.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_tray.h>
|
||||
#include <SDL3/SDL_touch.h>
|
||||
#include <SDL3/SDL_version.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <SDL3/SDL_oldnames.h>
|
||||
|
||||
#endif /* SDL_h_ */
|
||||
658
Source/ThirdParty/SDL/SDL3/SDL_assert.h
vendored
Normal file
658
Source/ThirdParty/SDL/SDL3/SDL_assert.h
vendored
Normal file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
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.
|
||||
* - It can be used as an aid to Clang's static analysis; it will treat SDL
|
||||
* assertions as universally true (under the assumption that you are serious
|
||||
* about the asserted claims and that your debug builds will detect when
|
||||
* these claims were wrong). This can help the analyzer avoid false
|
||||
* positives.
|
||||
*
|
||||
* To use it: compile 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.1.3.
|
||||
*/
|
||||
#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 macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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
|
||||
|
||||
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
|
||||
|
||||
/**
|
||||
* A macro that reports the current function being compiled.
|
||||
*
|
||||
* If SDL can't figure how the compiler reports this, it will use "???".
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_FUNCTION __FUNCTION__
|
||||
|
||||
#elif 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
|
||||
|
||||
/**
|
||||
* A macro that reports the current file being compiled.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_FILE __FILE__
|
||||
|
||||
/**
|
||||
* A macro that reports the current line number of the file being compiled.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.
|
||||
*/
|
||||
|
||||
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
|
||||
|
||||
/**
|
||||
* A macro for wrapping code in `do {} while (0);` without compiler warnings.
|
||||
*
|
||||
* Visual Studio with really aggressive warnings enabled needs this to avoid
|
||||
* compiler complaints.
|
||||
*
|
||||
* the `do {} while (0);` trick is useful for wrapping code in a macro that
|
||||
* may or may not be a single statement, to avoid various C language
|
||||
* accidents.
|
||||
*
|
||||
* To use:
|
||||
*
|
||||
* ```c
|
||||
* do { SomethingOnce(); } while (SDL_NULL_WHILE_LOOP_CONDITION (0));
|
||||
* ```
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
|
||||
|
||||
#elif defined(_MSC_VER) /* Avoid /W4 warnings. */
|
||||
/* "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! */
|
||||
#define SDL_NULL_WHILE_LOOP_CONDITION (0,0)
|
||||
#else
|
||||
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The macro used when an assertion is disabled.
|
||||
*
|
||||
* This isn't for direct use by apps, but this is the code that is inserted
|
||||
* when an SDL_assert is disabled (perhaps in a release build).
|
||||
*
|
||||
* The code does nothing, but wraps `condition` in a sizeof operator, which
|
||||
* generates no code and has no side effects, but avoid compiler warnings
|
||||
* about unused variables.
|
||||
*
|
||||
* \param condition the condition to assert (but not actually run here).
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.1.3.
|
||||
*/
|
||||
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.1.3.
|
||||
*/
|
||||
typedef struct SDL_AssertData
|
||||
{
|
||||
bool always_ignore; /**< 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.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_AssertState SDLCALL SDL_ReportAssertion(SDL_AssertData *data,
|
||||
const char *func,
|
||||
const char *file, int line) SDL_ANALYZER_NORETURN;
|
||||
|
||||
|
||||
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
|
||||
|
||||
/**
|
||||
* The macro used when an assertion triggers a breakpoint.
|
||||
*
|
||||
* This isn't for direct use by apps; use SDL_assert or SDL_TriggerBreakpoint
|
||||
* instead.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_AssertBreakpoint() SDL_TriggerBreakpoint()
|
||||
|
||||
#elif !defined(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 macro used when an assertion is enabled.
|
||||
*
|
||||
* This isn't for direct use by apps, but this is the code that is inserted
|
||||
* when an SDL_assert is enabled.
|
||||
*
|
||||
* The `do {} while(0)` avoids dangling else problems:
|
||||
*
|
||||
* ```c
|
||||
* 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().
|
||||
*
|
||||
* \param condition the condition to assert.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.
|
||||
*
|
||||
* \param condition boolean value to test.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.
|
||||
* *
|
||||
*
|
||||
* \param condition boolean value to test.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.
|
||||
*
|
||||
* \param condition boolean value to test.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
|
||||
|
||||
/* Enable various levels of assertions. */
|
||||
#elif 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 is 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.
|
||||
*
|
||||
* \param condition boolean value to test.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#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.
|
||||
*
|
||||
* \threadsafety This callback may be called from any thread that triggers an
|
||||
* assert at any time.
|
||||
*
|
||||
* \since This datatype is available since SDL 3.1.3.
|
||||
*/
|
||||
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`.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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. This
|
||||
* pointer remains valid until the next call to SDL_Quit() or
|
||||
* SDL_ResetAssertionReport().
|
||||
*
|
||||
* \threadsafety This function is not thread safe. Other threads calling
|
||||
* SDL_ResetAssertionReport() simultaneously, may render the
|
||||
* returned pointer invalid.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \threadsafety This function is not thread safe. Other threads triggering an
|
||||
* assertion, or simultaneously calling this function may cause
|
||||
* memory leaks or crashes.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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_ */
|
||||
546
Source/ThirdParty/SDL/SDL3/SDL_asyncio.h
vendored
Normal file
546
Source/ThirdParty/SDL/SDL3/SDL_asyncio.h
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
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: AsyncIO */
|
||||
|
||||
/**
|
||||
* # CategoryAsyncIO
|
||||
*
|
||||
* SDL offers a way to perform I/O asynchronously. This allows an app to read
|
||||
* or write files without waiting for data to actually transfer; the functions
|
||||
* that request I/O never block while the request is fulfilled.
|
||||
*
|
||||
* Instead, the data moves in the background and the app can check for results
|
||||
* at their leisure.
|
||||
*
|
||||
* This is more complicated than just reading and writing files in a
|
||||
* synchronous way, but it can allow for more efficiency, and never having
|
||||
* framerate drops as the hard drive catches up, etc.
|
||||
*
|
||||
* The general usage pattern for async I/O is:
|
||||
*
|
||||
* - Create one or more SDL_AsyncIOQueue objects.
|
||||
* - Open files with SDL_AsyncIOFromFile.
|
||||
* - Start I/O tasks to the files with SDL_ReadAsyncIO or SDL_WriteAsyncIO,
|
||||
* putting those tasks into one of the queues.
|
||||
* - Later on, use SDL_GetAsyncIOResult on a queue to see if any task is
|
||||
* finished without blocking. Tasks might finish in any order with success
|
||||
* or failure.
|
||||
* - When all your tasks are done, close the file with SDL_CloseAsyncIO. This
|
||||
* also generates a task, since it might flush data to disk!
|
||||
*
|
||||
* This all works, without blocking, in a single thread, but one can also wait
|
||||
* on a queue in a background thread, sleeping until new results have arrived:
|
||||
*
|
||||
* - Call SDL_WaitAsyncIOResult from one or more threads to efficiently block
|
||||
* until new tasks complete.
|
||||
* - When shutting down, call SDL_SignalAsyncIOQueue to unblock any sleeping
|
||||
* threads despite there being no new tasks completed.
|
||||
*
|
||||
* And, of course, to match the synchronous SDL_LoadFile, we offer
|
||||
* SDL_LoadFileAsync as a convenience function. This will handle allocating a
|
||||
* buffer, slurping in the file data, and null-terminating it; you still check
|
||||
* for results later.
|
||||
*
|
||||
* Behind the scenes, SDL will use newer, efficient APIs on platforms that
|
||||
* support them: Linux's io_uring and Windows 11's IoRing, for example. If
|
||||
* those technologies aren't available, SDL will offload the work to a thread
|
||||
* pool that will manage otherwise-synchronous loads without blocking the app.
|
||||
*
|
||||
* ## Best Practices
|
||||
*
|
||||
* Simple non-blocking i/o--for an app that just wants to pick up data
|
||||
* whenever it's ready without losing framerate waiting on disks to spin--can
|
||||
* use whatever pattern works well for the program. In this case, simply call
|
||||
* SDL_ReadAsyncIO, or maybe SDL_LoadFileAsync, as needed. Once a frame, call
|
||||
* SDL_GetAsyncIOResult to check for any completed tasks and deal with the
|
||||
* data as it arrives.
|
||||
*
|
||||
* If two separate pieces of the same program need their own i/o, it is legal
|
||||
* for each to create their own queue. This will prevent either piece from
|
||||
* accidentally consuming the other's completed tasks. Each queue does require
|
||||
* some amount of resources, but it is not an overwhelming cost. Do not make a
|
||||
* queue for each task, however. It is better to put many tasks into a single
|
||||
* queue. They will be reported in order of completion, not in the order they
|
||||
* were submitted, so it doesn't generally matter what order tasks are
|
||||
* started.
|
||||
*
|
||||
* One async i/o queue can be shared by multiple threads, or one thread can
|
||||
* have more than one queue, but the most efficient way--if ruthless
|
||||
* efficiency is the goal--is to have one queue per thread, with multiple
|
||||
* threads working in parallel, and attempt to keep each queue loaded with
|
||||
* tasks that are both started by and consumed by the same thread. On modern
|
||||
* platforms that can use newer interfaces, this can keep data flowing as
|
||||
* efficiently as possible all the way from storage hardware to the app, with
|
||||
* no contention between threads for access to the same queue.
|
||||
*
|
||||
* Written data is not guaranteed to make it to physical media by the time a
|
||||
* closing task is completed, unless SDL_CloseAsyncIO is called with its
|
||||
* `flush` parameter set to true, which is to say that a successful result
|
||||
* here can still result in lost data during an unfortunately-timed power
|
||||
* outage if not flushed. However, flushing will take longer and may be
|
||||
* unnecessary, depending on the app's needs.
|
||||
*/
|
||||
|
||||
#ifndef SDL_asyncio_h_
|
||||
#define SDL_asyncio_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
|
||||
|
||||
/**
|
||||
* The asynchronous I/O operation structure.
|
||||
*
|
||||
* This operates as an opaque handle. One can then request read or write
|
||||
* operations on it.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*
|
||||
* \sa SDL_AsyncIOFromFile
|
||||
*/
|
||||
typedef struct SDL_AsyncIO SDL_AsyncIO;
|
||||
|
||||
/**
|
||||
* Types of asynchronous I/O tasks.
|
||||
*
|
||||
* \since This enum is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef enum SDL_AsyncIOTaskType
|
||||
{
|
||||
SDL_ASYNCIO_TASK_READ, /**< A read operation. */
|
||||
SDL_ASYNCIO_TASK_WRITE, /**< A write operation. */
|
||||
SDL_ASYNCIO_TASK_CLOSE /**< A close operation. */
|
||||
} SDL_AsyncIOTaskType;
|
||||
|
||||
/**
|
||||
* Possible outcomes of an asynchronous I/O task.
|
||||
*
|
||||
* \since This enum is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef enum SDL_AsyncIOResult
|
||||
{
|
||||
SDL_ASYNCIO_COMPLETE, /**< request was completed without error */
|
||||
SDL_ASYNCIO_FAILURE, /**< request failed for some reason; check SDL_GetError()! */
|
||||
SDL_ASYNCIO_CANCELLED /**< request was cancelled before completing. */
|
||||
} SDL_AsyncIOResult;
|
||||
|
||||
/**
|
||||
* Information about a completed asynchronous I/O request.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*/
|
||||
typedef struct SDL_AsyncIOOutcome
|
||||
{
|
||||
SDL_AsyncIO *asyncio; /**< what generated this task. This pointer will be invalid if it was closed! */
|
||||
SDL_AsyncIOTaskType type; /**< What sort of task was this? Read, write, etc? */
|
||||
SDL_AsyncIOResult result; /**< the result of the work (success, failure, cancellation). */
|
||||
void *buffer; /**< buffer where data was read/written. */
|
||||
Uint64 offset; /**< offset in the SDL_AsyncIO where data was read/written. */
|
||||
Uint64 bytes_requested; /**< number of bytes the task was to read/write. */
|
||||
Uint64 bytes_transferred; /**< actual number of bytes that were read/written. */
|
||||
void *userdata; /**< pointer provided by the app when starting the task */
|
||||
} SDL_AsyncIOOutcome;
|
||||
|
||||
/**
|
||||
* A queue of completed asynchronous I/O tasks.
|
||||
*
|
||||
* When starting an asynchronous operation, you specify a queue for the new
|
||||
* task. A queue can be asked later if any tasks in it have completed,
|
||||
* allowing an app to manage multiple pending tasks in one place, in whatever
|
||||
* order they complete.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
*
|
||||
* \sa SDL_CreateAsyncIOQueue
|
||||
* \sa SDL_ReadAsyncIO
|
||||
* \sa SDL_WriteAsyncIO
|
||||
* \sa SDL_GetAsyncIOResult
|
||||
* \sa SDL_WaitAsyncIOResult
|
||||
*/
|
||||
typedef struct SDL_AsyncIOQueue SDL_AsyncIOQueue;
|
||||
|
||||
/**
|
||||
* Use this function to create a new SDL_AsyncIO object for reading from
|
||||
* and/or writing to a named file.
|
||||
*
|
||||
* The `mode` string understands the following values:
|
||||
*
|
||||
* - "r": Open a file for reading only. It must exist.
|
||||
* - "w": Open a file for writing only. It will create missing files or
|
||||
* truncate existing ones.
|
||||
* - "r+": Open a file for update both reading and writing. The file must
|
||||
* exist.
|
||||
* - "w+": Create an empty file for both reading and writing. If a file with
|
||||
* the same name already exists its content is erased and the file is
|
||||
* treated as a new empty file.
|
||||
*
|
||||
* There is no "b" mode, as there is only "binary" style I/O, and no "a" mode
|
||||
* for appending, since you specify the position when starting a task.
|
||||
*
|
||||
* This function supports Unicode filenames, but they must be encoded in UTF-8
|
||||
* format, regardless of the underlying operating system.
|
||||
*
|
||||
* This call is _not_ asynchronous; it will open the file before returning,
|
||||
* under the assumption that doing so is generally a fast operation. Future
|
||||
* reads and writes to the opened file will be async, however.
|
||||
*
|
||||
* \param file a UTF-8 string representing the filename to open.
|
||||
* \param mode an ASCII string representing the mode to be used for opening
|
||||
* the file.
|
||||
* \returns a pointer to the SDL_AsyncIO structure that is created or NULL on
|
||||
* failure; call SDL_GetError() for more information.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_CloseAsyncIO
|
||||
* \sa SDL_ReadAsyncIO
|
||||
* \sa SDL_WriteAsyncIO
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_AsyncIO * SDLCALL SDL_AsyncIOFromFile(const char *file, const char *mode);
|
||||
|
||||
/**
|
||||
* Use this function to get the size of the data stream in an SDL_AsyncIO.
|
||||
*
|
||||
* This call is _not_ asynchronous; it assumes that obtaining this info is a
|
||||
* non-blocking operation in most reasonable cases.
|
||||
*
|
||||
* \param asyncio the SDL_AsyncIO to get the size of the data stream from.
|
||||
* \returns the size of the data stream in the SDL_IOStream 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.2.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC Sint64 SDLCALL SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio);
|
||||
|
||||
/**
|
||||
* Start an async read.
|
||||
*
|
||||
* This function reads up to `size` bytes from `offset` position in the data
|
||||
* source to the area pointed at by `ptr`. This function may read less bytes
|
||||
* than requested.
|
||||
*
|
||||
* This function returns as quickly as possible; it does not wait for the read
|
||||
* to complete. On a successful return, this work will continue in the
|
||||
* background. If the work begins, even failure is asynchronous: a failing
|
||||
* return value from this function only means the work couldn't start at all.
|
||||
*
|
||||
* `ptr` must remain available until the work is done, and may be accessed by
|
||||
* the system at any time until then. Do not allocate it on the stack, as this
|
||||
* might take longer than the life of the calling function to complete!
|
||||
*
|
||||
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
|
||||
* to it when it completes its work.
|
||||
*
|
||||
* \param asyncio a pointer to an SDL_AsyncIO structure.
|
||||
* \param ptr a pointer to a buffer to read data into.
|
||||
* \param offset the position to start reading in the data source.
|
||||
* \param size the number of bytes to read from the data source.
|
||||
* \param queue a queue to add the new SDL_AsyncIO to.
|
||||
* \param userdata an app-defined pointer that will be provided with the task
|
||||
* results.
|
||||
* \returns true on success or false 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.2.0.
|
||||
*
|
||||
* \sa SDL_WriteAsyncIO
|
||||
* \sa SDL_CreateAsyncIOQueue
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata);
|
||||
|
||||
/**
|
||||
* Start an async write.
|
||||
*
|
||||
* This function writes `size` bytes from `offset` position in the data source
|
||||
* to the area pointed at by `ptr`.
|
||||
*
|
||||
* This function returns as quickly as possible; it does not wait for the
|
||||
* write to complete. On a successful return, this work will continue in the
|
||||
* background. If the work begins, even failure is asynchronous: a failing
|
||||
* return value from this function only means the work couldn't start at all.
|
||||
*
|
||||
* `ptr` must remain available until the work is done, and may be accessed by
|
||||
* the system at any time until then. Do not allocate it on the stack, as this
|
||||
* might take longer than the life of the calling function to complete!
|
||||
*
|
||||
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
|
||||
* to it when it completes its work.
|
||||
*
|
||||
* \param asyncio a pointer to an SDL_AsyncIO structure.
|
||||
* \param ptr a pointer to a buffer to write data from.
|
||||
* \param offset the position to start writing to the data source.
|
||||
* \param size the number of bytes to write to the data source.
|
||||
* \param queue a queue to add the new SDL_AsyncIO to.
|
||||
* \param userdata an app-defined pointer that will be provided with the task
|
||||
* results.
|
||||
* \returns true on success or false 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.2.0.
|
||||
*
|
||||
* \sa SDL_ReadAsyncIO
|
||||
* \sa SDL_CreateAsyncIOQueue
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata);
|
||||
|
||||
/**
|
||||
* Close and free any allocated resources for an async I/O object.
|
||||
*
|
||||
* Closing a file is _also_ an asynchronous task! If a write failure were to
|
||||
* happen during the closing process, for example, the task results will
|
||||
* report it as usual.
|
||||
*
|
||||
* Closing a file that has been written to does not guarantee the data has
|
||||
* made it to physical media; it may remain in the operating system's file
|
||||
* cache, for later writing to disk. This means that a successfully-closed
|
||||
* file can be lost if the system crashes or loses power in this small window.
|
||||
* To prevent this, call this function with the `flush` parameter set to true.
|
||||
* This will make the operation take longer, and perhaps increase system load
|
||||
* in general, but a successful result guarantees that the data has made it to
|
||||
* physical storage. Don't use this for temporary files, caches, and
|
||||
* unimportant data, and definitely use it for crucial irreplaceable files,
|
||||
* like game saves.
|
||||
*
|
||||
* This function guarantees that the close will happen after any other pending
|
||||
* tasks to `asyncio`, so it's safe to open a file, start several operations,
|
||||
* close the file immediately, then check for all results later. This function
|
||||
* will not block until the tasks have completed.
|
||||
*
|
||||
* Once this function returns true, `asyncio` is no longer valid, regardless
|
||||
* of any future outcomes. Any completed tasks might still contain this
|
||||
* pointer in their SDL_AsyncIOOutcome data, in case the app was using this
|
||||
* value to track information, but it should not be used again.
|
||||
*
|
||||
* If this function returns false, the close wasn't started at all, and it's
|
||||
* safe to attempt to close again later.
|
||||
*
|
||||
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
|
||||
* to it when it completes its work.
|
||||
*
|
||||
* \param asyncio a pointer to an SDL_AsyncIO structure to close.
|
||||
* \param flush true if data should sync to disk before the task completes.
|
||||
* \param queue a queue to add the new SDL_AsyncIO to.
|
||||
* \param userdata an app-defined pointer that will be provided with the task
|
||||
* results.
|
||||
* \returns true on success or false on failure; call SDL_GetError() for more
|
||||
* information.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread, but two
|
||||
* threads should not attempt to close the same object.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata);
|
||||
|
||||
/**
|
||||
* Create a task queue for tracking multiple I/O operations.
|
||||
*
|
||||
* Async I/O operations are assigned to a queue when started. The queue can be
|
||||
* checked for completed tasks thereafter.
|
||||
*
|
||||
* \returns a new task queue object or NULL if there was an error; 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.2.0.
|
||||
*
|
||||
* \sa SDL_DestroyAsyncIOQueue
|
||||
* \sa SDL_GetAsyncIOResult
|
||||
* \sa SDL_WaitAsyncIOResult
|
||||
*/
|
||||
extern SDL_DECLSPEC SDL_AsyncIOQueue * SDLCALL SDL_CreateAsyncIOQueue(void);
|
||||
|
||||
/**
|
||||
* Destroy a previously-created async I/O task queue.
|
||||
*
|
||||
* If there are still tasks pending for this queue, this call will block until
|
||||
* those tasks are finished. All those tasks will be deallocated. Their
|
||||
* results will be lost to the app.
|
||||
*
|
||||
* Any pending reads from SDL_LoadFileAsync() that are still in this queue
|
||||
* will have their buffers deallocated by this function, to prevent a memory
|
||||
* leak.
|
||||
*
|
||||
* Once this function is called, the queue is no longer valid and should not
|
||||
* be used, including by other threads that might access it while destruction
|
||||
* is blocking on pending tasks.
|
||||
*
|
||||
* Do not destroy a queue that still has threads waiting on it through
|
||||
* SDL_WaitAsyncIOResult(). You can call SDL_SignalAsyncIOQueue() first to
|
||||
* unblock those threads, and take measures (such as SDL_WaitThread()) to make
|
||||
* sure they have finished their wait and won't wait on the queue again.
|
||||
*
|
||||
* \param queue the task queue to destroy.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread, so long as
|
||||
* no other thread is waiting on the queue with
|
||||
* SDL_WaitAsyncIOResult.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*/
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue);
|
||||
|
||||
/**
|
||||
* Query an async I/O task queue for completed tasks.
|
||||
*
|
||||
* If a task assigned to this queue has finished, this will return true and
|
||||
* fill in `outcome` with the details of the task. If no task in the queue has
|
||||
* finished, this function will return false. This function does not block.
|
||||
*
|
||||
* If a task has completed, this function will free its resources and the task
|
||||
* pointer will no longer be valid. The task will be removed from the queue.
|
||||
*
|
||||
* It is safe for multiple threads to call this function on the same queue at
|
||||
* once; a completed task will only go to one of the threads.
|
||||
*
|
||||
* \param queue the async I/O task queue to query.
|
||||
* \param outcome details of a finished task will be written here. May not be
|
||||
* NULL.
|
||||
* \returns true if a task has completed, false otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_WaitAsyncIOResult
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome);
|
||||
|
||||
/**
|
||||
* Block until an async I/O task queue has a completed task.
|
||||
*
|
||||
* This function puts the calling thread to sleep until there a task assigned
|
||||
* to the queue that has finished.
|
||||
*
|
||||
* If a task assigned to the queue has finished, this will return true and
|
||||
* fill in `outcome` with the details of the task. If no task in the queue has
|
||||
* finished, this function will return false.
|
||||
*
|
||||
* If a task has completed, this function will free its resources and the task
|
||||
* pointer will no longer be valid. The task will be removed from the queue.
|
||||
*
|
||||
* It is safe for multiple threads to call this function on the same queue at
|
||||
* once; a completed task will only go to one of the threads.
|
||||
*
|
||||
* Note that by the nature of various platforms, more than one waiting thread
|
||||
* may wake to handle a single task, but only one will obtain it, so
|
||||
* `timeoutMS` is a _maximum_ wait time, and this function may return false
|
||||
* sooner.
|
||||
*
|
||||
* This function may return false if there was a system error, the OS
|
||||
* inadvertently awoke multiple threads, or if SDL_SignalAsyncIOQueue() was
|
||||
* called to wake up all waiting threads without a finished task.
|
||||
*
|
||||
* A timeout can be used to specify a maximum wait time, but rather than
|
||||
* polling, it is possible to have a timeout of -1 to wait forever, and use
|
||||
* SDL_SignalAsyncIOQueue() to wake up the waiting threads later.
|
||||
*
|
||||
* \param queue the async I/O task queue to wait on.
|
||||
* \param outcome details of a finished task will be written here. May not be
|
||||
* NULL.
|
||||
* \param timeoutMS the maximum time to wait, in milliseconds, or -1 to wait
|
||||
* indefinitely.
|
||||
* \returns true if task has completed, false otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_SignalAsyncIOQueue
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS);
|
||||
|
||||
/**
|
||||
* Wake up any threads that are blocking in SDL_WaitAsyncIOResult().
|
||||
*
|
||||
* This will unblock any threads that are sleeping in a call to
|
||||
* SDL_WaitAsyncIOResult for the specified queue, and cause them to return
|
||||
* from that function.
|
||||
*
|
||||
* This can be useful when destroying a queue to make sure nothing is touching
|
||||
* it indefinitely. In this case, once this call completes, the caller should
|
||||
* take measures to make sure any previously-blocked threads have returned
|
||||
* from their wait and will not touch the queue again (perhaps by setting a
|
||||
* flag to tell the threads to terminate and then using SDL_WaitThread() to
|
||||
* make sure they've done so).
|
||||
*
|
||||
* \param queue the async I/O task queue to signal.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_WaitAsyncIOResult
|
||||
*/
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue);
|
||||
|
||||
/**
|
||||
* Load all the data from a file path, asynchronously.
|
||||
*
|
||||
* This function returns as quickly as possible; it does not wait for the read
|
||||
* to complete. On a successful return, this work will continue in the
|
||||
* background. If the work begins, even failure is asynchronous: a failing
|
||||
* return value from this function only means the work couldn't start at all.
|
||||
*
|
||||
* The data is allocated with a zero byte at the end (null terminated) for
|
||||
* convenience. This extra byte is not included in SDL_AsyncIOOutcome's
|
||||
* bytes_transferred value.
|
||||
*
|
||||
* This function will allocate the buffer to contain the file. It must be
|
||||
* deallocated by calling SDL_free() on SDL_AsyncIOOutcome's buffer field
|
||||
* after completion.
|
||||
*
|
||||
* An SDL_AsyncIOQueue must be specified. The newly-created task will be added
|
||||
* to it when it completes its work.
|
||||
*
|
||||
* \param file the path to read all available data from.
|
||||
* \param queue a queue to add the new SDL_AsyncIO to.
|
||||
* \param userdata an app-defined pointer that will be provided with the task
|
||||
* results.
|
||||
* \returns true on success or false on failure; call SDL_GetError() for more
|
||||
* information.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_LoadFile_IO
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_asyncio_h_ */
|
||||
664
Source/ThirdParty/SDL/SDL3/SDL_atomic.h
vendored
Normal file
664
Source/ThirdParty/SDL/SDL3/SDL_atomic.h
vendored
Normal file
@@ -0,0 +1,664 @@
|
||||
/*
|
||||
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 true if the lock succeeded, false if the lock is already held.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_LockSpinlock
|
||||
* \sa SDL_UnlockSpinlock
|
||||
*/
|
||||
extern SDL_DECLSPEC 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.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \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.1.3.
|
||||
*/
|
||||
#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 (function version).
|
||||
*
|
||||
* Please refer to SDL_MemoryBarrierRelease for details. This is a function
|
||||
* version, which might be useful if you need to use this functionality from a
|
||||
* scripting language, etc. Also, some of the macro versions call this
|
||||
* function behind the scenes, where more heavy lifting can happen inside of
|
||||
* SDL. Generally, though, an app written in C/C++/etc should use the macro
|
||||
* version, as it will be more efficient.
|
||||
*
|
||||
* \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.1.3.
|
||||
*
|
||||
* \sa SDL_MemoryBarrierRelease
|
||||
*/
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierReleaseFunction(void);
|
||||
|
||||
/**
|
||||
* Insert a memory acquire barrier (function version).
|
||||
*
|
||||
* Please refer to SDL_MemoryBarrierRelease for details. This is a function
|
||||
* version, which might be useful if you need to use this functionality from a
|
||||
* scripting language, etc. Also, some of the macro versions call this
|
||||
* function behind the scenes, where more heavy lifting can happen inside of
|
||||
* SDL. Generally, though, an app written in C/C++/etc should use the macro
|
||||
* version, as it will be more efficient.
|
||||
*
|
||||
* \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.1.3.
|
||||
*
|
||||
* \sa SDL_MemoryBarrierAcquire
|
||||
*/
|
||||
extern SDL_DECLSPEC void SDLCALL SDL_MemoryBarrierAcquireFunction(void);
|
||||
|
||||
|
||||
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
|
||||
|
||||
/**
|
||||
* Insert a memory release barrier (macro version).
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This is the macro version of this functionality; if possible, SDL will use
|
||||
* compiler intrinsics or inline assembly, but some platforms might need to
|
||||
* call the function version of this, SDL_MemoryBarrierReleaseFunction to do
|
||||
* the heavy lifting. Apps that can use the macro should favor it over the
|
||||
* function.
|
||||
*
|
||||
* \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.1.3.
|
||||
*
|
||||
* \sa SDL_MemoryBarrierAcquire
|
||||
* \sa SDL_MemoryBarrierReleaseFunction
|
||||
*/
|
||||
#define SDL_MemoryBarrierRelease() SDL_MemoryBarrierReleaseFunction()
|
||||
|
||||
/**
|
||||
* Insert a memory acquire barrier (macro version).
|
||||
*
|
||||
* Please see SDL_MemoryBarrierRelease for the details on what memory barriers
|
||||
* are and when to use them.
|
||||
*
|
||||
* This is the macro version of this functionality; if possible, SDL will use
|
||||
* compiler intrinsics or inline assembly, but some platforms might need to
|
||||
* call the function version of this, SDL_MemoryBarrierAcquireFunction, to do
|
||||
* the heavy lifting. Apps that can use the macro should favor it over the
|
||||
* function.
|
||||
*
|
||||
* \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.1.3.
|
||||
*
|
||||
* \sa SDL_MemoryBarrierRelease
|
||||
* \sa SDL_MemoryBarrierAcquireFunction
|
||||
*/
|
||||
#define SDL_MemoryBarrierAcquire() SDL_MemoryBarrierAcquireFunction()
|
||||
|
||||
#elif 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.1.3.
|
||||
*/
|
||||
#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_SetAtomicInt all other threads, regardless of the CPU it is running on,
|
||||
* will see that value when retrieved with SDL_GetAtomicInt, 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.1.3.
|
||||
*
|
||||
* \sa SDL_CompareAndSwapAtomicInt
|
||||
* \sa SDL_GetAtomicInt
|
||||
* \sa SDL_SetAtomicInt
|
||||
* \sa SDL_AddAtomicInt
|
||||
*/
|
||||
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 true if the atomic variable was set, false otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_GetAtomicInt
|
||||
* \sa SDL_SetAtomicInt
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicInt(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.1.3.
|
||||
*
|
||||
* \sa SDL_GetAtomicInt
|
||||
*/
|
||||
extern SDL_DECLSPEC int SDLCALL SDL_SetAtomicInt(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.1.3.
|
||||
*
|
||||
* \sa SDL_SetAtomicInt
|
||||
*/
|
||||
extern SDL_DECLSPEC int SDLCALL SDL_GetAtomicInt(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.1.3.
|
||||
*
|
||||
* \sa SDL_AtomicDecRef
|
||||
* \sa SDL_AtomicIncRef
|
||||
*/
|
||||
extern SDL_DECLSPEC int SDLCALL SDL_AddAtomicInt(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.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_AtomicDecRef
|
||||
*/
|
||||
#define SDL_AtomicIncRef(a) SDL_AddAtomicInt(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 true if the variable reached zero after decrementing, false
|
||||
* otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this macro from any thread.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_AtomicIncRef
|
||||
*/
|
||||
#define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) == 1)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A type representing an atomic unsigned 32-bit 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_SetAtomicU32 all other threads, regardless of the CPU it is running on,
|
||||
* will see that value when retrieved with SDL_GetAtomicU32, 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.1.3.
|
||||
*
|
||||
* \sa SDL_CompareAndSwapAtomicU32
|
||||
* \sa SDL_GetAtomicU32
|
||||
* \sa SDL_SetAtomicU32
|
||||
*/
|
||||
typedef struct SDL_AtomicU32 { Uint32 value; } SDL_AtomicU32;
|
||||
|
||||
/**
|
||||
* 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_AtomicU32 variable to be modified.
|
||||
* \param oldval the old value.
|
||||
* \param newval the new value.
|
||||
* \returns true if the atomic variable was set, false otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_GetAtomicU32
|
||||
* \sa SDL_SetAtomicU32
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 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_AtomicU32 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.1.3.
|
||||
*
|
||||
* \sa SDL_GetAtomicU32
|
||||
*/
|
||||
extern SDL_DECLSPEC Uint32 SDLCALL SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 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_AtomicU32 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.1.3.
|
||||
*
|
||||
* \sa SDL_SetAtomicU32
|
||||
*/
|
||||
extern SDL_DECLSPEC Uint32 SDLCALL SDL_GetAtomicU32(SDL_AtomicU32 *a);
|
||||
|
||||
/**
|
||||
* 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 true if the pointer was set, false otherwise.
|
||||
*
|
||||
* \threadsafety It is safe to call this function from any thread.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_CompareAndSwapAtomicInt
|
||||
* \sa SDL_GetAtomicPointer
|
||||
* \sa SDL_SetAtomicPointer
|
||||
*/
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_CompareAndSwapAtomicPointer(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.1.3.
|
||||
*
|
||||
* \sa SDL_CompareAndSwapAtomicPointer
|
||||
* \sa SDL_GetAtomicPointer
|
||||
*/
|
||||
extern SDL_DECLSPEC void * SDLCALL SDL_SetAtomicPointer(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.1.3.
|
||||
*
|
||||
* \sa SDL_CompareAndSwapAtomicPointer
|
||||
* \sa SDL_SetAtomicPointer
|
||||
*/
|
||||
extern SDL_DECLSPEC void * SDLCALL SDL_GetAtomicPointer(void **a);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL_close_code.h>
|
||||
|
||||
#endif /* SDL_atomic_h_ */
|
||||
2158
Source/ThirdParty/SDL/SDL3/SDL_audio.h
vendored
Normal file
2158
Source/ThirdParty/SDL/SDL3/SDL_audio.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
486
Source/ThirdParty/SDL/SDL3/SDL_begin_code.h
vendored
Normal file
486
Source/ThirdParty/SDL/SDL3/SDL_begin_code.h
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
/*
|
||||
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 */
|
||||
|
||||
/**
|
||||
* # CategoryBeginCode
|
||||
*
|
||||
* `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. :)
|
||||
*
|
||||
* SDL's headers use this; applications generally should not include this
|
||||
* header directly.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
|
||||
#ifdef SDL_WIKI_DOCUMENTATION_SECTION
|
||||
|
||||
/**
|
||||
* A macro to tag a symbol as deprecated.
|
||||
*
|
||||
* A function is marked deprecated by adding this macro to its declaration:
|
||||
*
|
||||
* ```c
|
||||
* extern SDL_DEPRECATED int ThisFunctionWasABadIdea(void);
|
||||
* ```
|
||||
*
|
||||
* Compilers with deprecation support can give a warning when a deprecated
|
||||
* function is used. This symbol may be used in SDL's headers, but apps are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* SDL, on occasion, might deprecate a function for various reasons. However,
|
||||
* SDL never removes symbols before major versions, so deprecated interfaces
|
||||
* in SDL3 will remain available until SDL4, where it would be expected an app
|
||||
* would have to take steps to migrate anyhow.
|
||||
*
|
||||
* On compilers without a deprecation mechanism, this is defined to nothing,
|
||||
* and using a deprecated function will not generate a warning.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_DEPRECATED __attribute__((deprecated))
|
||||
|
||||
/**
|
||||
* A macro to tag a symbol as a public API.
|
||||
*
|
||||
* SDL uses this macro for all its public functions. On some targets, it is
|
||||
* used to signal to the compiler that this function needs to be exported from
|
||||
* a shared library, but it might have other side effects.
|
||||
*
|
||||
* This symbol is used in SDL's headers, but apps and other libraries are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_DECLSPEC __attribute__ ((visibility("default")))
|
||||
|
||||
/**
|
||||
* A macro to set a function's calling conventions.
|
||||
*
|
||||
* SDL uses this macro for all its public functions, and any callbacks it
|
||||
* defines. This macro guarantees that calling conventions match between SDL
|
||||
* and the app, even if the two were built with different compilers or
|
||||
* optimization settings.
|
||||
*
|
||||
* When writing a callback function, it is very important for it to be
|
||||
* correctly tagged with SDLCALL, as mismatched calling conventions can cause
|
||||
* strange behaviors and can be difficult to diagnose. Plus, on many
|
||||
* platforms, SDLCALL is defined to nothing, so compilers won't be able to
|
||||
* warn that the tag is missing.
|
||||
*
|
||||
* This symbol is used in SDL's headers, but apps and other libraries are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDLCALL __cdecl
|
||||
|
||||
/**
|
||||
* A macro to request a function be inlined.
|
||||
*
|
||||
* This is a hint to the compiler to inline a function. The compiler is free
|
||||
* to ignore this request. On compilers without inline support, this is
|
||||
* defined to nothing.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_INLINE __inline
|
||||
|
||||
/**
|
||||
* A macro to demand a function be inlined.
|
||||
*
|
||||
* This is a command to the compiler to inline a function. SDL uses this macro
|
||||
* in its public headers for a handful of simple functions. On compilers
|
||||
* without forceinline support, this is defined to `static SDL_INLINE`, which
|
||||
* is often good enough.
|
||||
*
|
||||
* This symbol is used in SDL's headers, but apps and other libraries are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_FORCE_INLINE __forceinline
|
||||
|
||||
/**
|
||||
* A macro to tag a function as never-returning.
|
||||
*
|
||||
* This is a hint to the compiler that a function does not return. An example
|
||||
* of a function like this is the C runtime's exit() function.
|
||||
*
|
||||
* This hint can lead to code optimizations, and help analyzers understand
|
||||
* code flow better. On compilers without noreturn support, this is defined to
|
||||
* nothing.
|
||||
*
|
||||
* This symbol is used in SDL's headers, but apps and other libraries are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_NORETURN __attribute__((noreturn))
|
||||
|
||||
/**
|
||||
* A macro to tag a function as never-returning (for analysis purposes).
|
||||
*
|
||||
* This is almost identical to SDL_NORETURN, except functions marked with this
|
||||
* _can_ actually return. The difference is that this isn't used for code
|
||||
* generation, but rather static analyzers use this information to assume
|
||||
* truths about program state and available code paths. Specifically, this tag
|
||||
* is useful for writing an assertion mechanism. Indeed, SDL_assert uses this
|
||||
* tag behind the scenes. Generally, apps that don't understand the specific
|
||||
* use-case for this tag should avoid using it directly.
|
||||
*
|
||||
* On compilers without analyzer_noreturn support, this is defined to nothing.
|
||||
*
|
||||
* This symbol is used in SDL's headers, but apps and other libraries are
|
||||
* welcome to use it for their own interfaces as well.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
|
||||
|
||||
|
||||
/**
|
||||
* A macro to signal that a case statement without a `break` is intentional.
|
||||
*
|
||||
* C compilers have gotten more aggressive about warning when a switch's
|
||||
* `case` block does not end with a `break` or other flow control statement,
|
||||
* flowing into the next case's code, as this is a common accident that leads
|
||||
* to strange bugs. But sometimes falling through to the next case is the
|
||||
* correct and desired behavior. This symbol lets an app communicate this
|
||||
* intention to the compiler, so it doesn't generate a warning.
|
||||
*
|
||||
* It is used like this:
|
||||
*
|
||||
* ```c
|
||||
* switch (x) {
|
||||
* case 1:
|
||||
* DoSomethingOnlyForOne();
|
||||
* SDL_FALLTHROUGH; // tell the compiler this was intentional.
|
||||
* case 2:
|
||||
* DoSomethingForOneAndTwo();
|
||||
* break;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_FALLTHROUGH [[fallthrough]]
|
||||
|
||||
/**
|
||||
* A macro to tag a function's return value as critical.
|
||||
*
|
||||
* This is a hint to the compiler that a function's return value should not be
|
||||
* ignored.
|
||||
*
|
||||
* If an NODISCARD function's return value is thrown away (the function is
|
||||
* called as if it returns `void`), the compiler will issue a warning.
|
||||
*
|
||||
* While it's generally good practice to check return values for errors, often
|
||||
* times legitimate programs do not for good reasons. Be careful about what
|
||||
* functions are tagged as NODISCARD. It operates best when used on a function
|
||||
* that's failure is surprising and catastrophic; a good example would be a
|
||||
* program that checks the return values of all its file write function calls
|
||||
* but not the call to close the file, which it assumes incorrectly never
|
||||
* fails.
|
||||
*
|
||||
* Function callers that want to throw away a NODISCARD return value can call
|
||||
* the function with a `(void)` cast, which informs the compiler the act is
|
||||
* intentional.
|
||||
*
|
||||
* On compilers without nodiscard support, this is defined to nothing.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_NODISCARD [[nodiscard]]
|
||||
|
||||
/**
|
||||
* A macro to tag a function as an allocator.
|
||||
*
|
||||
* This is a hint to the compiler that a function is an allocator, like
|
||||
* malloc(), with certain rules. A description of how GCC treats this hint is
|
||||
* here:
|
||||
*
|
||||
* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-malloc-function-attribute
|
||||
*
|
||||
* On compilers without allocator tag support, this is defined to nothing.
|
||||
*
|
||||
* Most apps don't need to, and should not, use this directly.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_MALLOC __declspec(allocator) __desclspec(restrict)
|
||||
|
||||
/**
|
||||
* A macro to tag a function as returning a certain allocation.
|
||||
*
|
||||
* This is a hint to the compiler that a function allocates and returns a
|
||||
* specific amount of memory based on one of its arguments. For example, the C
|
||||
* runtime's malloc() function could use this macro with an argument of 1
|
||||
* (first argument to malloc is the size of the allocation).
|
||||
*
|
||||
* On compilers without alloc_size support, this is defined to nothing.
|
||||
*
|
||||
* Most apps don't need to, and should not, use this directly.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_ALLOC_SIZE(p) __attribute__((alloc_size(p)))
|
||||
|
||||
/**
|
||||
* A macro to tag a pointer variable, to help with pointer aliasing.
|
||||
*
|
||||
* A good explanation of the restrict keyword is here:
|
||||
*
|
||||
* https://en.wikipedia.org/wiki/Restrict
|
||||
*
|
||||
* On compilers without restrict support, this is defined to nothing.
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_RESTRICT __restrict__
|
||||
|
||||
/**
|
||||
* Check if the compiler supports a given builtin functionality.
|
||||
*
|
||||
* This allows preprocessor checks for things that otherwise might fail to
|
||||
* compile.
|
||||
*
|
||||
* Supported by virtually all clang versions and more-recent GCCs. Use this
|
||||
* instead of checking the clang version if possible.
|
||||
*
|
||||
* On compilers without has_builtin support, this is defined to 0 (always
|
||||
* false).
|
||||
*
|
||||
* \since This macro is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_HAS_BUILTIN(x) __has_builtin(x)
|
||||
|
||||
/* end of wiki documentation section. */
|
||||
#endif
|
||||
|
||||
#ifndef SDL_HAS_BUILTIN
|
||||
#ifdef __has_builtin
|
||||
#define SDL_HAS_BUILTIN(x) __has_builtin(x)
|
||||
#else
|
||||
#define SDL_HAS_BUILTIN(x) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#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_WINDOWS)
|
||||
# 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_WINDOWS) && !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 */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user