87 Commits

Author SHA1 Message Date
a45aa74aca Revert "Build Editor bindings only when generating editor project files"
This reverts commit 55ad5ae367.
2025-03-18 01:16:53 +02:00
dbf5dd9e92 _sdl binary 2025-03-07 16:45:33 +02:00
b0ae2b4cd2 Update SDL3 to 3.2.6 2025-03-07 16:45:28 +02:00
ed8001ff8b Fix missing NETX_0_OR_GREATER definitions for previous versions 2025-02-26 22:37:18 +02:00
6e2ed2bdbd Fix TypeDescriptor hack for latest runtime versions 2025-02-26 22:07:29 +02:00
ba88bf8b0f Merge branch 'unload_alc_fix' into wip
# Conflicts:
#	Source/Editor/Modules/WindowsModule.cs
2025-02-26 20:31:07 +02:00
afbb8225d8 Avoid showing tooltips in inactive windows 2025-02-26 20:09:32 +02:00
35678b06a8 Merge branch 'api_macro_consistency' into signalgame2 2025-02-26 20:09:31 +02:00
96d39ecb30 _xaudio2 2025-02-26 20:09:31 +02:00
2c7b11a691 Remove texturereferences in default materials 2025-02-26 20:09:30 +02:00
83c288d739 Disable LFS override 2025-02-26 20:09:30 +02:00
8c6b853ee5 Update to enet 2.3.6 2025-02-26 20:09:29 +02:00
b414d7d026 Merge branch 'fixed_update_deltatime' into signalgame2 2025-02-26 20:09:28 +02:00
ae6ec809b8 Merge branch 'richtextbox_optimize' into signalgame2 2025-02-26 20:09:28 +02:00
f47214ae0a Merge branch 'unload_alc_fix' into signalgame2 2025-02-26 20:09:27 +02:00
bddd1b0984 Merge branch 'sdl_platform' into signalgame2 2025-02-26 20:09:27 +02:00
e2e77385b1 Fix missing semicolons 2025-02-26 20:09:26 +02:00
f4c13032a2 Require semicolon after most engine API macros
# Conflicts:
#	Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp
2025-02-26 20:09:26 +02:00
43801d0824 Always run fixed update ticks at fixed deltatime
The random variance in fixed updates makes it impossible to do anything deterministic during physics ticks.
2025-02-26 20:09:25 +02:00
16cd027bf3 Optimize RichTextBox rendering with long text 2025-02-26 20:09:25 +02:00
f361c4bbdb Refocus editor after restoring windows 2025-02-26 20:09:24 +02:00
c4f5777536 Fix stack overflow when restoring tab in floating window 2025-02-26 20:09:24 +02:00
c740d27b0e Close and restore AssetEditorWindows on scripting reload 2025-02-26 20:09:23 +02:00
800b163de9 Fix ContentDatabase not being loaded at the end of editor initialization 2025-02-26 20:09:22 +02:00
7f317d0fb6 Fix custom content and importers blocking scripting reload 2025-02-26 20:09:22 +02:00
714cdd84ad Fix error when ContentDatabase gets rebuilt while initializing editor 2025-02-26 20:09:22 +02:00
26b5172aea Clear VisjectSurface node caches and context menus during scripts reload 2025-02-26 20:09:21 +02:00
47b95f5650 Close and restore Prefab windows during scripting reload 2025-02-26 20:09:21 +02:00
989a5441f3 Clear references holding on to types in game assemblies 2025-02-26 20:09:20 +02:00
6394a7aae0 Fix wrong prefab event unregistration 2025-02-26 20:09:19 +02:00
beff9d5241 Fix scripting AssemblyLoadContext not getting unloaded 2025-02-26 20:09:19 +02:00
16be2fc857 Update SDL3 to 3.2.4 2025-02-13 18:05:09 +02:00
561239a4b2 Fix window dragging when not supported by Wayland compositor
(cherry picked from commit 3554747a67)
2025-02-02 18:38:06 +02:00
389b2e6148 Show current display server in Editor window tooltip
(cherry picked from commit 62968dd437)
2025-02-02 18:38:06 +02:00
9403b87788 Properly mark floating windows with transparency support
(cherry picked from commit c660fac524)
2025-02-02 18:38:06 +02:00
64cceac913 Enable transparency support in Vulkan swapchains
(cherry picked from commit 431a69e357)
2025-02-02 18:38:06 +02:00
c523079f0e Fix compilation for game builds
(cherry picked from commit f4fcc07288)
2025-02-02 18:38:06 +02:00
942124fdc3 Fix cloning SDL repository 2025-01-28 22:24:21 +02:00
9085874d4e Fix text input not working on X11 2025-01-28 22:10:46 +02:00
2f5562f8eb Fix button latching on Windows after drag and drop operation 2025-01-28 22:10:45 +02:00
6733e45729 Implement new window dragging system 2025-01-28 22:10:44 +02:00
37438afbf6 Fix mouse resetting issues after ending relative mode 2025-01-28 22:10:44 +02:00
7c34b1c855 Fix frame stutter when window is focused 2025-01-28 22:10:43 +02:00
3dc2e55627 Fix error when docking to sides of tabbed panel 2025-01-28 22:10:43 +02:00
c658dd72e7 Cleanup Linux SDL implementation 2025-01-28 22:10:43 +02:00
5b8c81dfac Support compiling third party library C files as C code 2025-01-28 22:10:42 +02:00
bf4ec5f91a Implement Wayland protocols module and file generation 2025-01-28 22:10:42 +02:00
15024b38b9 Fix mouse warping after ending relative mode 2025-01-28 22:10:42 +02:00
71ec415cc4 Add git fetch method for dependencies 2025-01-28 22:10:41 +02:00
81b6f47d8a Fix window ShowInTaskbar setting 2025-01-28 22:10:41 +02:00
e55fd18771 Fix various issues with child window positioning 2025-01-28 22:10:41 +02:00
fd97c2bdf2 Add Window.IsAlwaysOnTop property 2025-01-28 22:10:40 +02:00
6ca71a872d Use SDL locale 2025-01-28 22:10:40 +02:00
18e92425bb Allow window with single tab to be dragged from tab area 2025-01-28 22:10:40 +02:00
3feebe8910 Fix ValueBox mouse position resetting after releasing the button 2025-01-28 22:10:39 +02:00
9fc7fc7a2e Fix SDL build process on Linux 2025-01-28 22:10:39 +02:00
642d90a293 Update SDL to 3.2.0 2025-01-28 22:10:39 +02:00
f5313b9ffd Force cursor to center of Game Window when tab handle is clicked 2025-01-28 22:10:38 +02:00
Chandler Cox
7abed39473 Fix rotation using SDL 2025-01-28 22:10:38 +02:00
ffc40ba634 Fix Linux compilation without SDL 2025-01-28 22:09:14 +02:00
84f3cda190 Fix compilation 2025-01-28 22:09:14 +02:00
0f20f387fc Update SDL3 2025-01-28 22:09:13 +02:00
67093a0d28 Fix compilation issues 2025-01-28 22:09:13 +02:00
63eaf1adf2 Fix windows not being hidden initially 2025-01-28 22:09:13 +02:00
28d167b4f8 Fix parent window position handling with popup/tooltip windows 2025-01-28 22:09:12 +02:00
7bff09eeb9 Fix compilation errors in other platforms 2025-01-28 22:09:12 +02:00
5afde966ef Fix CI for Linux 2025-01-28 22:09:12 +02:00
5df1f32305 Prevent building with SDL in unsupported platforms 2025-01-28 22:09:11 +02:00
670f2ee2b0 Fallback to X11 message box implementation when SDL fails 2025-01-28 22:09:11 +02:00
5abf336696 Fix popup and context menus not working on Wayland 2025-01-28 22:09:11 +02:00
7ae0a65c0e Hide warnings for unsupported SDL operations on Wayland 2025-01-28 22:09:10 +02:00
9e88233957 Log a warning for not implemented Wayland functionality 2025-01-28 22:09:10 +02:00
d0a4213538 Fix compilation in Linux 2025-01-28 22:09:10 +02:00
b2467edc0f Enable warning sound in question dialogs 2025-01-28 22:09:09 +02:00
94e398a6b3 Enable modern Windows dialog boxes 2025-01-28 22:09:09 +02:00
e5ca67618f Implement relative mouse mode (raw input) for SDL platform 2025-01-28 22:09:09 +02:00
2da908d9b8 Add flag for Window types 2025-01-28 22:09:09 +02:00
34f187161c Enable native windowing system settings with SDL platform 2025-01-28 22:09:08 +02:00
0e5e0169bf Add command-line switches to force X11 and Wayland SDL drivers 2025-01-28 22:09:08 +02:00
f318d3aadc Implement SDL platform, windowing and input handling 2025-01-28 22:09:08 +02:00
43e38df8b0 Refactor application window class name 2025-01-28 22:09:07 +02:00
86b999f6a4 Move Window related enums to separate header file 2025-01-28 22:09:07 +02:00
553ca7d71c Refactor Windows drag and drop implementation 2025-01-28 22:09:06 +02:00
2594cc4546 Refactor ScreenUtilities 2025-01-28 22:09:06 +02:00
2cd03eceaa Add more helper methods for managing Git repos 2025-01-28 22:09:06 +02:00
d8feec325a Fix centered window location on X11 2025-01-28 22:09:05 +02:00
71b7f110e6 Fix initial position of Tooltips 2025-01-28 22:09:05 +02:00
239 changed files with 90599 additions and 2180 deletions

View File

@@ -16,7 +16,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: | 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 - name: Setup Vulkan
uses: ./.github/actions/vulkan uses: ./.github/actions/vulkan
- name: Setup .NET - name: Setup .NET
@@ -44,7 +44,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Install dependencies - name: Install dependencies
run: | 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 - name: Setup Vulkan
uses: ./.github/actions/vulkan uses: ./.github/actions/vulkan
- name: Setup .NET - name: Setup .NET

View File

@@ -28,7 +28,7 @@ jobs:
git lfs pull git lfs pull
- name: Install dependencies - name: Install dependencies
run: | 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 - name: Build
run: | run: |
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8 ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ if errorlevel 1 goto BuildToolFailed
:: Build bindings for all editor configurations :: Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor Binaries\Tools\Flax.Build.exe -build -BuildBindingsOnly -arch=x64 -platform=Windows --buildTargets=FlaxEditor,FlaxGame
popd popd
echo Done! echo Done!

View File

@@ -14,4 +14,4 @@ bash ./Development/Scripts/Mac/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations # Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
# TODO: Detect the correct architecture here # TODO: Detect the correct architecture here
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=ARM64 -platform=Mac --buildTargets=FlaxEditor,FlaxGame

View File

@@ -14,4 +14,4 @@ bash ./Development/Scripts/Linux/CallBuildTool.sh --genproject "$@"
# Build bindings for all editor configurations # Build bindings for all editor configurations
echo Building C# bindings... echo Building C# bindings...
# TODO: Detect the correct architecture here # TODO: Detect the correct architecture here
Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor Binaries/Tools/Flax.Build -build -BuildBindingsOnly -arch=x64 -platform=Linux --buildTargets=FlaxEditor,FlaxGame

View File

@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
} }
/// <inheritdoc /> /// <inheritdoc />
public override string TypeDescription => Path.EndsWith(".h") ? "C++ Header File" : "C++ Source Code"; public override string TypeDescription => Path.EndsWith(".h") || Path.EndsWith(".hpp") ? "C++ Header File" : "C++ Source Code";
/// <inheritdoc /> /// <inheritdoc />
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128; public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;

View File

@@ -1277,6 +1277,8 @@ bool CookAssetsStep::Perform(CookingData& data)
assetStats.Count++; assetStats.Count++;
assetStats.ContentSize += FileSystem::GetFileSize(cookedFilePath); assetStats.ContentSize += FileSystem::GetFileSize(cookedFilePath);
LOG(Info, "Cooked size of {0}: {1}KB", assetId, (FileSystem::GetFileSize(cookedFilePath) / (1024)));
if (packageBuilder.Add(data, i->Value, cookedFilePath)) if (packageBuilder.Add(data, i->Value, cookedFilePath))
return true; return true;
} }

View File

@@ -415,9 +415,9 @@ bool DeployDataStep::Perform(CookingData& data)
data.AddRootEngineAsset(TEXT("Engine/Textures/NormalTexture")); data.AddRootEngineAsset(TEXT("Engine/Textures/NormalTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/BlackTexture")); data.AddRootEngineAsset(TEXT("Engine/Textures/BlackTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/WhiteTexture")); data.AddRootEngineAsset(TEXT("Engine/Textures/WhiteTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensStarburst")); //data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensStarburst"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensColor")); //data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensColor"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensDirt")); //data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensDirt"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Circle")); data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Circle"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Hexagon")); data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Hexagon"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Octagon")); data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Octagon"));

View File

@@ -186,12 +186,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
foreach (var file in files) foreach (var file in files)
FindNewKeysCSharp(file, newKeys, allKeys); FindNewKeysCSharp(file, newKeys, allKeys);
// C++ // C/C++
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories); files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
filesCount += files.Length; filesCount += files.Length;
foreach (var file in files) foreach (var file in files)
FindNewKeysCpp(file, newKeys, allKeys); FindNewKeysCpp(file, newKeys, allKeys);
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories); files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.h", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.hpp", SearchOption.AllDirectories)).ToArray();;
filesCount += files.Length; filesCount += files.Length;
foreach (var file in files) foreach (var file in files)
FindNewKeysCpp(file, newKeys, allKeys); FindNewKeysCpp(file, newKeys, allKeys);

View File

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

View File

@@ -1,529 +0,0 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Docking
{
/// <summary>
/// Helper class used to handle docking windows dragging and docking.
/// </summary>
public class DockHintWindow
{
private FloatWindowDockPanel _toMove;
private Float2 _dragOffset;
private Float2 _defaultWindowSize;
private Rectangle _rectDock;
private Rectangle _rectWindow;
private Float2 _mouse;
private DockState _toSet;
private DockPanel _toDock;
private bool _lateDragOffsetUpdate;
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
private DockHintWindow(FloatWindowDockPanel toMove)
{
_toMove = toMove;
_toSet = DockState.Float;
var window = toMove.Window.Window;
// Remove focus from drag target
_toMove.Focus();
_toMove.Defocus();
// Focus window
window.Focus();
// Check if window is maximized and restore window.
if (window.IsMaximized)
{
// Restore window and set position to mouse.
var mousePos = window.MousePosition;
var previousSize = window.Size;
window.Restore();
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
}
// Calculate dragging offset and move window to the destination position
var mouseScreenPosition = Platform.MousePosition;
// If the _toMove window was not focused when initializing this window, the result vector only contains zeros
// and to prevent a failure, we need to perform an update for the drag offset at later time which will be done in the OnMouseMove event handler.
if (mouseScreenPosition != Float2.Zero)
CalculateDragOffset(mouseScreenPosition);
else
_lateDragOffsetUpdate = true;
// Get initial size
_defaultWindowSize = window.Size;
// Init proxy window
Proxy.Init(ref _defaultWindowSize);
// Bind events
Proxy.Window.MouseUp += OnMouseUp;
Proxy.Window.MouseMove += OnMouseMove;
Proxy.Window.LostFocus += OnLostFocus;
// Start tracking mouse
Proxy.Window.StartTrackingMouse(false);
// Update window GUI
Proxy.Window.GUI.PerformLayout();
// Update rectangles
UpdateRects();
// Hide base window
window.Hide();
// Enable hit window presentation
Proxy.Window.RenderingEnabled = true;
Proxy.Window.Show();
Proxy.Window.Focus();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
// End tracking mouse
Proxy.Window.EndTrackingMouse();
// Disable rendering
Proxy.Window.RenderingEnabled = false;
// Unbind events
Proxy.Window.MouseUp -= OnMouseUp;
Proxy.Window.MouseMove -= OnMouseMove;
Proxy.Window.LostFocus -= OnLostFocus;
// Hide the proxy
Proxy.Hide();
if (_toMove == null)
return;
// Check if window won't be docked
if (_toSet == DockState.Float)
{
var window = _toMove.Window?.Window;
if (window == null)
return;
var mouse = Platform.MousePosition;
// Move base window
window.Position = mouse - _dragOffset;
// Show base window
window.Show();
}
else
{
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
// Check if window has only single tab
if (hasNoChildPanels && _toMove.TabsCount == 1)
{
// Dock window
_toMove.GetTab(0).Show(_toSet, _toDock);
}
// Check if dock as tab and has no child panels
else if (hasNoChildPanels && _toSet == DockState.DockFill)
{
// Dock all tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
}
}
else
{
var selectedTab = _toMove.SelectedTab;
// Dock the first tab into the target location
var firstTab = _toMove.GetTab(0);
firstTab.Show(_toSet, _toDock);
// Dock rest of the tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
}
// Keep selected tab being selected
selectedTab?.SelectTab();
}
// Focus target window
_toDock.Root.Focus();
}
_toMove = null;
}
/// <summary>
/// Creates the new dragging hit window.
/// </summary>
/// <param name="toMove">Floating dock panel to move.</param>
/// <returns>The dock hint window object.</returns>
public static DockHintWindow Create(FloatWindowDockPanel toMove)
{
if (toMove == null)
throw new ArgumentNullException();
return new DockHintWindow(toMove);
}
/// <summary>
/// Creates the new dragging hit window.
/// </summary>
/// <param name="toMove">Dock window to move.</param>
/// <returns>The dock hint window object.</returns>
public static DockHintWindow Create(DockWindow toMove)
{
if (toMove == null)
throw new ArgumentNullException();
// Show floating
toMove.ShowFloating();
// Move window to the mouse position (with some offset for caption bar)
var window = (WindowRootControl)toMove.Root;
var mouse = Platform.MousePosition;
window.Window.Position = mouse - new Float2(8, 8);
// Get floating panel
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
return new DockHintWindow(floatingPanelToMove);
}
/// <summary>
/// Calculates window rectangle in the dock window.
/// </summary>
/// <param name="state">Window dock state.</param>
/// <param name="rect">Dock panel rectangle.</param>
/// <returns>Calculated window rectangle.</returns>
public static Rectangle CalculateDockRect(DockState state, ref Rectangle rect)
{
Rectangle result = rect;
switch (state)
{
case DockState.DockFill:
result.Location.Y += DockPanel.DefaultHeaderHeight;
result.Size.Y -= DockPanel.DefaultHeaderHeight;
break;
case DockState.DockTop:
result.Size.Y *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockLeft:
result.Size.X *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockBottom:
result.Location.Y += result.Size.Y * (1 - DockPanel.DefaultSplitterValue);
result.Size.Y *= DockPanel.DefaultSplitterValue;
break;
case DockState.DockRight:
result.Location.X += result.Size.X * (1 - DockPanel.DefaultSplitterValue);
result.Size.X *= DockPanel.DefaultSplitterValue;
break;
}
return result;
}
private void CalculateDragOffset(Float2 mouseScreenPosition)
{
var baseWinPos = _toMove.Window.Window.Position;
_dragOffset = mouseScreenPosition - baseWinPos;
}
private void UpdateRects()
{
// Cache mouse position
_mouse = Platform.MousePosition;
// Check intersection with any dock panel
var uiMouse = _mouse;
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
// Check dock state to use
bool showProxyHints = _toDock != null;
bool showBorderHints = showProxyHints;
bool showCenterHint = showProxyHints;
if (showProxyHints)
{
// If moved window has not only tabs but also child panels disable docking as tab
if (_toMove.ChildPanelsCount > 0)
showCenterHint = false;
// Disable docking windows with one or more dock panels inside
if (_toMove.ChildPanelsCount > 0)
showBorderHints = false;
// Get dock area
_rectDock = _toDock.DockAreaBounds;
// Cache dock rectangles
var size = _rectDock.Size;
var offset = _rectDock.Location;
var borderMargin = 4.0f;
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
var hintWindowsSize2 = hintWindowsSize * 0.5f;
var centerX = size.X * 0.5f;
var centerY = size.Y * 0.5f;
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
// Hit test
DockState toSet = DockState.Float;
if (showBorderHints)
{
if (_rUpper.Contains(_mouse))
toSet = DockState.DockTop;
else if (_rBottom.Contains(_mouse))
toSet = DockState.DockBottom;
else if (_rLeft.Contains(_mouse))
toSet = DockState.DockLeft;
else if (_rRight.Contains(_mouse))
toSet = DockState.DockRight;
}
if (showCenterHint && _rCenter.Contains(_mouse))
toSet = DockState.DockFill;
_toSet = toSet;
// Show proxy hint windows
Proxy.Down.Position = _rBottom.Location;
Proxy.Left.Position = _rLeft.Location;
Proxy.Right.Position = _rRight.Location;
Proxy.Up.Position = _rUpper.Location;
Proxy.Center.Position = _rCenter.Location;
}
else
{
_toSet = DockState.Float;
}
// Update proxy hint windows visibility
Proxy.Down.IsVisible = showProxyHints & showBorderHints;
Proxy.Left.IsVisible = showProxyHints & showBorderHints;
Proxy.Right.IsVisible = showProxyHints & showBorderHints;
Proxy.Up.IsVisible = showProxyHints & showBorderHints;
Proxy.Center.IsVisible = showProxyHints & showCenterHint;
// Calculate proxy/dock/window rectangles
if (_toDock == null)
{
// Floating window over nothing
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
}
else
{
if (_toSet == DockState.Float)
{
// Floating window over dock panel
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
}
else
{
// Use only part of the dock panel to show hint
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
}
}
// Update proxy window
Proxy.Window.ClientBounds = _rectWindow;
}
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
{
if (button == MouseButton.Left)
{
Dispose();
}
}
private void OnMouseMove(ref Float2 mousePos)
{
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
if (_lateDragOffsetUpdate)
{
// Calculate dragging offset and move window to the destination position
CalculateDragOffset(mousePos);
// Reset state
_lateDragOffsetUpdate = false;
}
UpdateRects();
}
private void OnLostFocus()
{
Dispose();
}
/// <summary>
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
/// </summary>
public static class Proxy
{
/// <summary>
/// The drag proxy window.
/// </summary>
public static Window Window;
/// <summary>
/// The left hint proxy window.
/// </summary>
public static Window Left;
/// <summary>
/// The right hint proxy window.
/// </summary>
public static Window Right;
/// <summary>
/// The up hint proxy window.
/// </summary>
public static Window Up;
/// <summary>
/// The down hint proxy window.
/// </summary>
public static Window Down;
/// <summary>
/// The center hint proxy window.
/// </summary>
public static Window Center;
/// <summary>
/// The hint windows size.
/// </summary>
public const float HintWindowsSize = 32.0f;
/// <summary>
/// Initializes the hit proxy windows. Those windows are used to indicate drag target areas (left, right, top, bottom, etc.).
/// </summary>
public static void InitHitProxy()
{
CreateProxy(ref Left, "DockHint.Left");
CreateProxy(ref Right, "DockHint.Right");
CreateProxy(ref Up, "DockHint.Up");
CreateProxy(ref Down, "DockHint.Down");
CreateProxy(ref Center, "DockHint.Center");
}
/// <summary>
/// Initializes the hint window.
/// </summary>
/// <param name="initSize">Initial size of the proxy window.</param>
public static void Init(ref Float2 initSize)
{
if (Window == null)
{
var settings = CreateWindowSettings.Default;
settings.Title = "DockHint.Window";
settings.Size = initSize;
settings.AllowInput = true;
settings.AllowMaximize = false;
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ShowAfterFirstPaint = false;
settings.IsTopmost = true;
Window = Platform.CreateWindow(ref settings);
Window.Opacity = 0.6f;
Window.GUI.BackgroundColor = Style.Current.DragWindow;
}
else
{
// Resize proxy
Window.ClientSize = initSize;
}
InitHitProxy();
}
private static void CreateProxy(ref Window win, string name)
{
if (win != null)
return;
var settings = CreateWindowSettings.Default;
settings.Title = name;
settings.Size = new Float2(HintWindowsSize * Platform.DpiScale);
settings.AllowInput = false;
settings.AllowMaximize = false;
settings.AllowMinimize = false;
settings.HasBorder = false;
settings.HasSizingFrame = false;
settings.IsRegularWindow = false;
settings.SupportsTransparency = true;
settings.ShowInTaskbar = false;
settings.ActivateWhenFirstShown = false;
settings.IsTopmost = true;
settings.ShowAfterFirstPaint = false;
win = Platform.CreateWindow(ref settings);
win.Opacity = 0.6f;
win.GUI.BackgroundColor = Style.Current.DragWindow;
}
/// <summary>
/// Hides proxy windows.
/// </summary>
public static void Hide()
{
HideProxy(ref Window);
HideProxy(ref Left);
HideProxy(ref Right);
HideProxy(ref Up);
HideProxy(ref Down);
HideProxy(ref Center);
}
private static void HideProxy(ref Window win)
{
if (win)
{
win.Hide();
}
}
/// <summary>
/// Releases proxy data and windows.
/// </summary>
public static void Dispose()
{
DisposeProxy(ref Window);
DisposeProxy(ref Left);
DisposeProxy(ref Right);
DisposeProxy(ref Up);
DisposeProxy(ref Down);
DisposeProxy(ref Center);
}
private static void DisposeProxy(ref Window win)
{
if (win)
{
win.Close(ClosingReason.User);
win = null;
}
}
}
}
}

View File

@@ -50,6 +50,11 @@ namespace FlaxEditor.GUI.Docking
/// </summary> /// </summary>
public Float2 MousePosition = Float2.Minimum; public Float2 MousePosition = Float2.Minimum;
/// <summary>
/// The mouse position.
/// </summary>
public Float2 MouseStartPosition = Float2.Minimum;
/// <summary> /// <summary>
/// The start drag asynchronous window. /// The start drag asynchronous window.
/// </summary> /// </summary>
@@ -165,7 +170,7 @@ namespace FlaxEditor.GUI.Docking
if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating) if (_panel.ChildPanelsCount == 0 && _panel.TabsCount == 1 && _panel.IsFloating)
{ {
// Create docking hint window but in an async manner // Create docking hint window but in an async manner
DockHintWindow.Create(_panel as FloatWindowDockPanel); WindowDragHelper.StartDragging(_panel as FloatWindowDockPanel);
} }
else else
{ {
@@ -176,7 +181,7 @@ namespace FlaxEditor.GUI.Docking
_panel.SelectTab(index - 1); _panel.SelectTab(index - 1);
// Create docking hint window // Create docking hint window
DockHintWindow.Create(win); WindowDragHelper.StartDragging(win, _panel.RootWindow.Window);
} }
} }
} }
@@ -355,6 +360,7 @@ namespace FlaxEditor.GUI.Docking
if (IsSingleFloatingWindow) if (IsSingleFloatingWindow)
return base.OnMouseDown(location, button); return base.OnMouseDown(location, button);
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross); MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
MouseStartPosition = location;
// Check buttons // Check buttons
if (button == MouseButton.Left) if (button == MouseButton.Left)
@@ -441,6 +447,20 @@ namespace FlaxEditor.GUI.Docking
StartDrag(MouseDownWindow); StartDrag(MouseDownWindow);
MouseDownWindow = null; MouseDownWindow = null;
} }
// Check if single tab is tried to be moved
else if (MouseDownWindow != null && _panel.TabsCount <= 1)
{
if ((MousePosition - MouseStartPosition).Length > 3)
{
// Clear flag
IsMouseLeftButtonDown = false;
// Check tab under the mouse
if (!IsMouseDownOverCross && MouseDownWindow != null)
StartDrag(MouseDownWindow);
MouseDownWindow = null;
}
}
// Check if has more than one tab to change order // Check if has more than one tab to change order
else if (MouseDownWindow != null && _panel.TabsCount > 1) else if (MouseDownWindow != null && _panel.TabsCount > 1)
{ {

View File

@@ -182,6 +182,25 @@ namespace FlaxEditor.GUI.Docking
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param> /// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param> /// <param name="position">Window location.</param>
public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent) public void ShowFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent)
{
CreateFloating(location, size, position, true);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
public void CreateFloating()
{
CreateFloating(Float2.Zero, Float2.Zero);
}
/// <summary>
/// Creates the window in a floating state.
/// </summary>
/// <param name="location">Window location.</param>
/// <param name="size">Window size, set <see cref="Float2.Zero"/> to use default.</param>
/// <param name="position">Window location.</param>
/// <param name="showWindow">Window visibility.</param>
public void CreateFloating(Float2 location, Float2 size, WindowStartPosition position = WindowStartPosition.CenterParent, bool showWindow = false)
{ {
Undock(); Undock();
@@ -199,6 +218,8 @@ namespace FlaxEditor.GUI.Docking
windowGUI.UnlockChildrenRecursive(); windowGUI.UnlockChildrenRecursive();
windowGUI.PerformLayout(); windowGUI.PerformLayout();
if (showWindow)
{
// Show // Show
window.Show(); window.Show();
window.BringToFront(); window.BringToFront();
@@ -208,6 +229,7 @@ namespace FlaxEditor.GUI.Docking
// Perform layout again // Perform layout again
windowGUI.PerformLayout(); windowGUI.PerformLayout();
} }
}
/// <summary> /// <summary>
/// Shows the window. /// Shows the window.

View File

@@ -52,7 +52,7 @@ namespace FlaxEditor.GUI.Docking
return; return;
// Create docking hint window // Create docking hint window
DockHintWindow.Create(this); WindowDragHelper.StartDragging(this);
} }
/// <summary> /// <summary>
@@ -75,14 +75,14 @@ namespace FlaxEditor.GUI.Docking
settings.MaximumSize = Float2.Zero; // Unlimited size settings.MaximumSize = Float2.Zero; // Unlimited size
settings.Fullscreen = false; settings.Fullscreen = false;
settings.HasBorder = true; settings.HasBorder = true;
settings.SupportsTransparency = false; settings.SupportsTransparency = true;
settings.ActivateWhenFirstShown = true; settings.ActivateWhenFirstShown = true;
settings.AllowInput = true; settings.AllowInput = true;
settings.AllowMinimize = true; settings.AllowMinimize = true;
settings.AllowMaximize = true; settings.AllowMaximize = true;
settings.AllowDragAndDrop = true; settings.AllowDragAndDrop = true;
settings.IsTopmost = false; settings.IsTopmost = false;
settings.IsRegularWindow = true; settings.Type = WindowType.Regular;
settings.HasSizingFrame = true; settings.HasSizingFrame = true;
settings.ShowAfterFirstPaint = false; settings.ShowAfterFirstPaint = false;
settings.ShowInTaskbar = true; settings.ShowInTaskbar = true;

View File

@@ -81,7 +81,6 @@ namespace FlaxEditor.GUI.Docking
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded) public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
{ {
// Check all floating windows // Check all floating windows
// TODO: gather windows order and take it into account when performing test
for (int i = 0; i < FloatingPanels.Count; i++) for (int i = 0; i < FloatingPanels.Count; i++)
{ {
var win = FloatingPanels[i]; var win = FloatingPanels[i];
@@ -94,9 +93,44 @@ namespace FlaxEditor.GUI.Docking
} }
// Base // Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
return base.HitTest(ref position); return base.HitTest(ref position);
} }
/// <summary>
/// Performs hit test over dock panel.
/// </summary>
/// <param name="position">Window space position to test.</param>
/// <param name="excluded">Floating window to omit during searching (and all docked to that one).</param>
/// <param name="hitResults">Results of the hit test</param>
/// <returns>True if any dock panels were hit, otherwise false.</returns>
public bool HitTest(ref Float2 position, FloatWindowDockPanel excluded, out DockPanel[] hitResults)
{
// Check all floating windows
List<DockPanel> results = new(FloatingPanels.Count);
for (int i = 0; i < FloatingPanels.Count; i++)
{
var win = FloatingPanels[i];
if (win.Visible && win != excluded)
{
var result = win.HitTest(ref position);
if (result != null)
results.Add(result);
}
}
// Base
//if (!Root?.RootWindow.Window.IsFocused ?? false)
// return null;
var baseResult = base.HitTest(ref position);
if (baseResult != null)
results.Add(baseResult);
hitResults = results.ToArray();
return hitResults.Length > 0;
}
internal void LinkWindow(DockWindow window) internal void LinkWindow(DockWindow window)
{ {
// Add to the windows list // Add to the windows list

View File

@@ -0,0 +1,448 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Docking
{
/// <summary>
/// Helper class used to handle docking windows dragging and docking.
/// </summary>
public class WindowDragHelper
{
private FloatWindowDockPanel _toMove;
private Float2 _dragOffset;
private Rectangle _rectDock;
private Float2 _mouse;
private DockState _toSet;
private DockPanel _toDock;
private Window _dragSourceWindow;
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
private Control _dockHintDown, _dockHintUp, _dockHintLeft, _dockHintRight, _dockHintCenter;
/// <summary>
/// The hint control size.
/// </summary>
public const float HintControlSize = 32.0f;
/// <summary>
/// The opacity of the dragged window when hint controls are shown.
/// </summary>
public const float DragWindowOpacity = 0.4f;
private WindowDragHelper(FloatWindowDockPanel toMove, Window dragSourceWindow)
{
_toMove = toMove;
_toSet = DockState.Float;
var window = toMove.Window.Window;
// Check if window is maximized and restore window.
if (window.IsMaximized)
{
// Restore window and set position to mouse.
var mousePos = window.MousePosition;
var previousSize = window.Size;
window.Restore();
window.Position = Platform.MousePosition - mousePos * window.Size / previousSize;
}
// Bind events
FlaxEngine.Scripting.Update += OnUpdate;
window.MouseUp += OnMouseUp;
// Update rectangles
UpdateRects(Platform.MousePosition);
_dragSourceWindow = dragSourceWindow;
if (_dragSourceWindow != null) // Detaching a tab from existing window
{
_dragOffset = new Float2(window.Size.X / 2, 10.0f);
// TODO: when detaching tab in floating window (not main window), the drag source window is still main window?
var dragSourceWindowWayland = toMove.MasterPanel?.RootWindow.Window ?? Editor.Instance.Windows.MainWindow;
window.DoDragDrop(window.Title, _dragOffset, dragSourceWindowWayland);
_dragSourceWindow.MouseUp += OnMouseUp; // The mouse up event is sent to the source window on Windows
}
else
{
_dragOffset = window.MousePosition;
window.DoDragDrop(window.Title, _dragOffset, window);
}
// Ensure the dragged window stays on top of every other window
window.IsAlwaysOnTop = true;
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
var window = _toMove?.Window?.Window;
// Unbind events
FlaxEngine.Scripting.Update -= OnUpdate;
if (window != null)
window.MouseUp -= OnMouseUp;
if (_dragSourceWindow != null)
_dragSourceWindow.MouseUp -= OnMouseUp;
RemoveDockHints();
if (_toMove == null)
return;
if (window != null)
{
window.Opacity = 1.0f;
window.IsAlwaysOnTop = false;
window.BringToFront();
}
// Check if window won't be docked
if (_toSet == DockState.Float)
{
if (window == null)
return;
// Show base window
window.Show();
}
else
{
bool hasNoChildPanels = _toMove.ChildPanelsCount == 0;
// Check if window has only single tab
if (hasNoChildPanels && _toMove.TabsCount == 1)
{
// Dock window
_toMove.GetTab(0).Show(_toSet, _toDock);
}
// Check if dock as tab and has no child panels
else if (hasNoChildPanels && _toSet == DockState.DockFill)
{
// Dock all tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, _toDock);
}
}
else
{
var selectedTab = _toMove.SelectedTab;
// Dock the first tab into the target location
if (_toMove.TabsCount > 0)
{
var firstTab = _toMove.GetTab(0);
firstTab.Show(_toSet, _toDock);
// Dock rest of the tabs
while (_toMove.TabsCount > 0)
{
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
}
}
// Keep selected tab being selected
selectedTab?.SelectTab();
}
// Focus target window
_toDock.Root.Focus();
}
_toMove = null;
}
/// <summary>
/// Start dragging a floating dock panel.
/// </summary>
/// <param name="toMove">Floating dock panel to move.</param>
/// <returns>The window drag helper object.</returns>
public static WindowDragHelper StartDragging(FloatWindowDockPanel toMove)
{
if (toMove == null)
throw new ArgumentNullException();
return new WindowDragHelper(toMove, null);
}
/// <summary>
/// Start dragging a docked panel into a floating window.
/// </summary>
/// <param name="toMove">Dock window to move.</param>
/// <param name="dragSourceWindow">The window where dragging started from.</param>
/// <returns>The window drag helper object.</returns>
public static WindowDragHelper StartDragging(DockWindow toMove, Window dragSourceWindow)
{
if (toMove == null)
throw new ArgumentNullException();
// Create floating window
toMove.CreateFloating();
// Get floating panel
var window = (WindowRootControl)toMove.Root;
var floatingPanelToMove = window.GetChild(0) as FloatWindowDockPanel;
return new WindowDragHelper(floatingPanelToMove, dragSourceWindow);
}
private void AddDockHints()
{
if (_toDock == null)
return;
if (_toDock.RootWindow.Window != _dragSourceWindow)
_toDock.RootWindow.Window.MouseUp += OnMouseUp;
_dockHintDown = AddHintControl(new Float2(0.5f, 1));
_dockHintUp = AddHintControl(new Float2(0.5f, 0));
_dockHintLeft = AddHintControl(new Float2(0, 0.5f));
_dockHintRight = AddHintControl(new Float2(1, 0.5f));
_dockHintCenter = AddHintControl(new Float2(0.5f, 0.5f));
Control AddHintControl(Float2 pivot)
{
Control hintControl = _toDock.AddChild<Control>();
hintControl.AnchorPreset = AnchorPresets.StretchAll;
hintControl.Offsets = Margin.Zero;
hintControl.Size = new Float2(HintControlSize);
hintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
hintControl.Pivot = pivot;
hintControl.PivotRelative = true;
return hintControl;
}
}
private void RemoveDockHints()
{
if (_toDock == null)
return;
if (_toDock.RootWindow.Window != _dragSourceWindow)
_toDock.RootWindow.Window.MouseUp -= OnMouseUp;
_dockHintDown?.Parent.RemoveChild(_dockHintDown);
_dockHintUp?.Parent.RemoveChild(_dockHintUp);
_dockHintLeft?.Parent.RemoveChild(_dockHintLeft);
_dockHintRight?.Parent.RemoveChild(_dockHintRight);
_dockHintCenter?.Parent.RemoveChild(_dockHintCenter);
_dockHintDown = _dockHintUp = _dockHintLeft = _dockHintRight = _dockHintCenter = null;
}
private void UpdateRects(Float2 mousePos)
{
// Cache mouse position
_mouse = mousePos;
// Check intersection with any dock panel
DockPanel dockPanel = null;
if (_toMove.MasterPanel.HitTest(ref _mouse, _toMove, out var hitResults))
{
dockPanel = hitResults[0];
// Prefer panel which currently has focus
foreach (var hit in hitResults)
{
if (hit.RootWindow.Window.IsFocused)
{
dockPanel = hit;
break;
}
}
// Prefer panel in the same window we hit earlier
if (dockPanel?.RootWindow != _toDock?.RootWindow)
{
foreach (var hit in hitResults)
{
if (hit.RootWindow == _toDock?.RootWindow)
{
dockPanel = _toDock;
break;
}
}
}
}
if (dockPanel != _toDock)
{
RemoveDockHints();
_toDock = dockPanel;
AddDockHints();
// Make sure the all the dock hint areas are not under other windows
_toDock?.RootWindow.Window.BringToFront();
//_toDock?.RootWindow.Window.Focus();
// Make the dragged window transparent when dock hints are visible
_toMove.Window.Window.Opacity = _toDock == null ? 1.0f : DragWindowOpacity;
}
// Check dock state to use
bool showProxyHints = _toDock != null;
bool showBorderHints = showProxyHints;
bool showCenterHint = showProxyHints;
Control hoveredHintControl = null;
Float2 hoveredSizeOverride = Float2.Zero;
if (showProxyHints)
{
// If moved window has not only tabs but also child panels disable docking as tab
if (_toMove.ChildPanelsCount > 0)
showCenterHint = false;
// Disable docking windows with one or more dock panels inside
if (_toMove.ChildPanelsCount > 0)
showBorderHints = false;
// Get dock area
_rectDock = _toDock.DockAreaBounds;
// Cache dock rectangles
var size = _rectDock.Size / Platform.DpiScale;
var offset = _toDock.PointFromScreen(_rectDock.Location);
var borderMargin = 4.0f;
var hintWindowsSize = HintControlSize;
var hintWindowsSize2 = hintWindowsSize * 0.5f;
var hintPreviewSize = new Float2(Math.Max(HintControlSize * 2, size.X * 0.5f), Math.Max(HintControlSize * 2, size.Y * 0.5f));
var centerX = size.X * 0.5f;
var centerY = size.Y * 0.5f;
_rUpper = new Rectangle(centerX - hintWindowsSize2, borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rBottom = new Rectangle(centerX - hintWindowsSize2, size.Y - hintWindowsSize - borderMargin, hintWindowsSize, hintWindowsSize) + offset;
_rLeft = new Rectangle(borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rRight = new Rectangle(size.X - hintWindowsSize - borderMargin, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
_rCenter = new Rectangle(centerX - hintWindowsSize2, centerY - hintWindowsSize2, hintWindowsSize, hintWindowsSize) + offset;
// Hit test, and calculate the approximation for filled area when hovered over the hint
DockState toSet = DockState.Float;
if (showBorderHints)
{
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockTop;
hoveredHintControl = _dockHintUp;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockBottom;
hoveredHintControl = _dockHintDown;
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
}
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockLeft;
hoveredHintControl = _dockHintLeft;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
else if (_rRight.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockRight;
hoveredHintControl = _dockHintRight;
hoveredSizeOverride = new Float2(hintPreviewSize.X, size.Y);
}
}
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
{
toSet = DockState.DockFill;
hoveredHintControl = _dockHintCenter;
hoveredSizeOverride = new Float2(size.X, size.Y);
}
_toSet = toSet;
}
else
{
_toSet = DockState.Float;
}
// Update sizes and opacity of hint controls
if (_toDock != null)
{
if (hoveredHintControl != _dockHintDown)
{
_dockHintDown.Size = new Float2(HintControlSize);
_dockHintDown.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintLeft)
{
_dockHintLeft.Size = new Float2(HintControlSize);
_dockHintLeft.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintRight)
{
_dockHintRight.Size = new Float2(HintControlSize);
_dockHintRight.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintUp)
{
_dockHintUp.Size = new Float2(HintControlSize);
_dockHintUp.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (hoveredHintControl != _dockHintCenter)
{
_dockHintCenter.Size = new Float2(HintControlSize);
_dockHintCenter.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
}
if (_toSet != DockState.Float)
{
if (hoveredHintControl != null)
{
hoveredHintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(1.0f);
hoveredHintControl.Size = hoveredSizeOverride;
}
}
}
// Update hint controls visibility and location
if (showProxyHints)
{
if (hoveredHintControl != _dockHintDown)
_dockHintDown.Location = _rBottom.Location;
if (hoveredHintControl != _dockHintLeft)
_dockHintLeft.Location = _rLeft.Location;
if (hoveredHintControl != _dockHintRight)
_dockHintRight.Location = _rRight.Location;
if (hoveredHintControl != _dockHintUp)
_dockHintUp.Location = _rUpper.Location;
if (hoveredHintControl != _dockHintCenter)
_dockHintCenter.Location = _rCenter.Location;
_dockHintDown.Visible = showProxyHints & showBorderHints;
_dockHintLeft.Visible = showProxyHints & showBorderHints;
_dockHintRight.Visible = showProxyHints & showBorderHints;
_dockHintUp.Visible = showProxyHints & showBorderHints;
_dockHintCenter.Visible = showProxyHints & showCenterHint;
}
}
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
{
if (button == MouseButton.Left)
Dispose();
}
private void OnUpdate()
{
var mousePos = Platform.MousePosition;
if (_mouse != mousePos)
OnMouseMove(mousePos);
}
private void OnMouseMove(Float2 mousePos)
{
if (_dragSourceWindow != null)
_toMove.Window.Window.Position = mousePos - _dragOffset;
UpdateRects(mousePos);
}
}
}

View File

@@ -266,6 +266,7 @@ namespace FlaxEditor.GUI.Input
return base.OnMouseDown(location, button); return base.OnMouseDown(location, button);
} }
#if !PLATFORM_SDL
/// <inheritdoc /> /// <inheritdoc />
public override void OnMouseMove(Float2 location) public override void OnMouseMove(Float2 location)
{ {
@@ -292,13 +293,45 @@ namespace FlaxEditor.GUI.Input
base.OnMouseMove(location); 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 /> /// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button) public override bool OnMouseUp(Float2 location, MouseButton button)
{ {
if (button == MouseButton.Left && _isSliding) if (button == MouseButton.Left && _isSliding)
{ {
#if !PLATFORM_SDL
// End sliding and return mouse to original location // End sliding and return mouse to original location
RootWindow.MousePosition = _mouseClickedPosition; RootWindow.MousePosition = _mouseClickedPosition;
#endif
EndSliding(); EndSliding();
return true; return true;
} }

View File

@@ -12,7 +12,7 @@ namespace FlaxEditor.GUI
/// <seealso cref="FlaxEngine.GUI.ContainerControl" /> /// <seealso cref="FlaxEngine.GUI.ContainerControl" />
public sealed class MainMenu : ContainerControl public sealed class MainMenu : ContainerControl
{ {
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
private bool _useCustomWindowSystem; private bool _useCustomWindowSystem;
private Image _icon; private Image _icon;
private Label _title; private Label _title;
@@ -67,7 +67,7 @@ namespace FlaxEditor.GUI
AutoFocus = false; AutoFocus = false;
AnchorPreset = AnchorPresets.HorizontalStretchTop; AnchorPreset = AnchorPresets.HorizontalStretchTop;
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
_useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem; _useCustomWindowSystem = !Editor.Instance.Options.Options.Interface.UseNativeWindowSystem;
if (_useCustomWindowSystem) if (_useCustomWindowSystem)
{ {
@@ -84,13 +84,15 @@ namespace FlaxEditor.GUI
ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration); ScriptsBuilder.GetBinariesConfiguration(out _, out _, out _, out var configuration);
var driver = Platform.DisplayServer;
_icon = new Image _icon = new Image
{ {
Margin = new Margin(6, 6, 6, 6), Margin = new Margin(6, 6, 6, 6),
Brush = new TextureBrush(windowIcon), Brush = new TextureBrush(windowIcon),
Color = Style.Current.Foreground, Color = Style.Current.Foreground,
KeepAspectRatio = false, KeepAspectRatio = false,
TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration), TooltipText = string.Format("{0}\nVersion {1}\nConfiguration {3}\nGraphics {2} {4}", _window.Title, Globals.EngineVersion, GPUDevice.Instance.RendererType, configuration, driver),
Parent = this, Parent = this,
}; };
@@ -166,7 +168,7 @@ namespace FlaxEditor.GUI
} }
} }
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc /> /// <inheritdoc />
public override void Update(float deltaTime) public override void Update(float deltaTime)
{ {
@@ -291,7 +293,7 @@ namespace FlaxEditor.GUI
if (base.OnMouseDoubleClick(location, button)) if (base.OnMouseDoubleClick(location, button))
return true; return true;
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
var child = GetChildAtRecursive(location); var child = GetChildAtRecursive(location);
if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton) if (_useCustomWindowSystem && child is not Button && child is not MainMenuButton)
{ {
@@ -321,7 +323,7 @@ namespace FlaxEditor.GUI
{ {
float x = 0; float x = 0;
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem) if (_useCustomWindowSystem)
{ {
// Icon // Icon
@@ -349,7 +351,7 @@ namespace FlaxEditor.GUI
} }
} }
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
if (_useCustomWindowSystem) if (_useCustomWindowSystem)
{ {
// Buttons // Buttons
@@ -367,7 +369,7 @@ namespace FlaxEditor.GUI
#endif #endif
} }
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
/// <inheritdoc /> /// <inheritdoc />
public override void OnDestroy() public override void OnDestroy()
{ {

View File

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

View File

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

View File

@@ -1013,7 +1013,7 @@ namespace FlaxEditor.Modules
ContentItem item; ContentItem item;
if (path.EndsWith(".cs")) if (path.EndsWith(".cs"))
item = new CSharpScriptItem(path); item = new CSharpScriptItem(path);
else if (path.EndsWith(".cpp") || path.EndsWith(".h")) else if (path.EndsWith(".cpp") || path.EndsWith(".h") || path.EndsWith(".c") || path.EndsWith(".hpp"))
item = new CppScriptItem(path); item = new CppScriptItem(path);
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl")) else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
item = new ShaderSourceItem(path); item = new ShaderSourceItem(path);

View File

@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
outputExtension = extension; outputExtension = extension;
// Check if can place source files here // Check if can place source files here
if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h")) if (!targetLocation.CanHaveScripts && (extension == ".cs" || extension == ".cpp" || extension == ".h" || extension == ".c" || extension == ".hpp"))
{ {
// Error // Error
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path)); Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));

View File

@@ -16,7 +16,7 @@ using FlaxEditor.Windows;
using FlaxEngine; using FlaxEngine;
using FlaxEngine.GUI; using FlaxEngine.GUI;
using FlaxEngine.Json; using FlaxEngine.Json;
using DockHintWindow = FlaxEditor.GUI.Docking.DockHintWindow; using WindowDragHelper = FlaxEditor.GUI.Docking.WindowDragHelper;
using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel; using MasterDockPanel = FlaxEditor.GUI.Docking.MasterDockPanel;
using FlaxEditor.Content.Settings; using FlaxEditor.Content.Settings;
using FlaxEditor.Options; using FlaxEditor.Options;
@@ -381,7 +381,7 @@ namespace FlaxEditor.Modules
Editor.Options.OptionsChanged += OnOptionsChanged; Editor.Options.OptionsChanged += OnOptionsChanged;
// Add dummy control for drawing the main window borders if using a custom style // 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) if (!Editor.Options.Options.Interface.UseNativeWindowSystem)
#endif #endif
{ {
@@ -456,13 +456,6 @@ namespace FlaxEditor.Modules
UpdateToolstrip(); UpdateToolstrip();
} }
/// <inheritdoc />
public override void OnExit()
{
// Cleanup dock panel hint proxy windows (Flax will destroy them by var but it's better to clear them earlier)
DockHintWindow.Proxy.Dispose();
}
private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing) private IColorPickerDialog ShowPickColorDialog(Control targetControl, Color initialValue, ColorValueBox.ColorPickerEvent colorChanged, ColorValueBox.ColorPickerClosedEvent pickerClosed, bool useDynamicEditing)
{ {
var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing); var dialog = new ColorPickerDialog(initialValue, colorChanged, pickerClosed, useDynamicEditing);

View File

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

View File

@@ -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.")] [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; public float InterfaceScale { get; set; } = 1.0f;
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_SDL
/// <summary> /// <summary>
/// Gets or sets a value indicating whether use native window title bar. Editor restart required. /// Gets or sets a value indicating whether use native window title bar. Editor restart required.
/// </summary> /// </summary>

View File

@@ -120,9 +120,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
// Discard non-source files or generated files // Discard non-source files or generated files
if ((!path.EndsWith(TEXT(".cs")) && if ((!path.EndsWith(TEXT(".cs")) &&
!path.EndsWith(TEXT(".cpp")) && !path.EndsWith(TEXT(".cpp")) &&
!path.EndsWith(TEXT(".c")) &&
!path.EndsWith(TEXT(".hpp")) &&
!path.EndsWith(TEXT(".h"))) || !path.EndsWith(TEXT(".h"))) ||
path.EndsWith(TEXT(".Gen.cs"))) path.EndsWith(TEXT(".Gen.cs")))
{
return; return;
}
ScopeLock scopeLock(_locker); ScopeLock scopeLock(_locker);
_lastSourceCodeEdited = DateTime::Now(); _lastSourceCodeEdited = DateTime::Now();

View File

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

View File

@@ -2,32 +2,4 @@
#pragma once #pragma once
#include "Engine/Core/Types/BaseTypes.h" #include "Engine/Platform/ScreenUtilities.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;
};

View File

@@ -77,7 +77,7 @@ namespace FlaxEditor.Viewport
public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root); public bool SnapToVertex => ContainsFocus && Editor.Instance.Options.Options.Input.SnapToVertex.Process(Root);
/// <inheritdoc /> /// <inheritdoc />
public Float2 MouseDelta => _mouseDelta; public Float2 MouseDelta => FlaxEngine.Input.MousePositionDelta;
/// <inheritdoc /> /// <inheritdoc />
public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false; public bool UseSnapping => Root?.GetKey(KeyboardKeys.Control) ?? false;

View File

@@ -158,18 +158,22 @@ namespace FlaxEditor.Viewport
private float _movementSpeed; private float _movementSpeed;
private float _minMovementSpeed; private float _minMovementSpeed;
private float _maxMovementSpeed; private float _maxMovementSpeed;
#if !PLATFORM_SDL
private float _mouseAccelerationScale; private float _mouseAccelerationScale;
private bool _useMouseFiltering; private bool _useMouseFiltering;
private bool _useMouseAcceleration; private bool _useMouseAcceleration;
#endif
// Input // Input
internal bool _disableInputUpdate; internal bool _disableInputUpdate;
private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown; private bool _isControllingMouse, _isViewportControllingMouse, _wasVirtualMouseRightDown, _isVirtualMouseRightDown;
private int _deltaFilteringStep;
private Float2 _startPos; private Float2 _startPos;
#if !PLATFORM_SDL
private Float2 _mouseDeltaLast; private Float2 _mouseDeltaLast;
private int _deltaFilteringStep;
private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames]; private Float2[] _deltaFilteringBuffer = new Float2[FpsCameraFilteringFrames];
#endif
/// <summary> /// <summary>
/// The previous input (from the previous update). /// The previous input (from the previous update).
@@ -522,10 +526,11 @@ namespace FlaxEditor.Viewport
: base(task) : base(task)
{ {
_editor = Editor.Instance; _editor = Editor.Instance;
#if !PLATFORM_SDL
_mouseAccelerationScale = 0.1f; _mouseAccelerationScale = 0.1f;
_useMouseFiltering = false; _useMouseFiltering = false;
_useMouseAcceleration = false; _useMouseAcceleration = false;
#endif
_camera = camera; _camera = camera;
if (_camera != null) if (_camera != null)
_camera.Viewport = this; _camera.Viewport = this;
@@ -1452,7 +1457,9 @@ namespace FlaxEditor.Viewport
// Hide cursor and start tracking mouse movement // Hide cursor and start tracking mouse movement
win.StartTrackingMouse(false); win.StartTrackingMouse(false);
win.Cursor = CursorType.Hidden; win.Cursor = CursorType.Hidden;
win.MouseMoveRelative += OnMouseMoveRelative;
#if !PLATFORM_SDL
// Center mouse position if it's too close to the edge // Center mouse position if it's too close to the edge
var size = Size; var size = Size;
var center = Float2.Round(size * 0.5f); var center = Float2.Round(size * 0.5f);
@@ -1461,6 +1468,7 @@ namespace FlaxEditor.Viewport
_viewMousePos = center; _viewMousePos = center;
win.MousePosition = PointToWindow(_viewMousePos); win.MousePosition = PointToWindow(_viewMousePos);
} }
#endif
} }
/// <summary> /// <summary>
@@ -1472,6 +1480,7 @@ namespace FlaxEditor.Viewport
// Restore cursor and stop tracking mouse movement // Restore cursor and stop tracking mouse movement
win.Cursor = CursorType.Default; win.Cursor = CursorType.Default;
win.EndTrackingMouse(); win.EndTrackingMouse();
win.MouseMoveRelative -= OnMouseMoveRelative;
} }
/// <summary> /// <summary>
@@ -1576,6 +1585,14 @@ namespace FlaxEditor.Viewport
else else
EndMouseCapture(); 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)); bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height));
_prevInput = _input; _prevInput = _input;
var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot)); var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl) && !(c is UIEditorRoot));
@@ -1583,6 +1600,7 @@ namespace FlaxEditor.Viewport
_input.Gather(win.Window, useMouse, ref _prevInput); _input.Gather(win.Window, useMouse, ref _prevInput);
else else
_input.Clear(); _input.Clear();
#endif
// Track controlling mouse state change // Track controlling mouse state change
bool wasControllingMouse = _prevInput.IsControllingMouse; bool wasControllingMouse = _prevInput.IsControllingMouse;
@@ -1691,6 +1709,10 @@ namespace FlaxEditor.Viewport
if (_input.IsControlDown) if (_input.IsControlDown)
moveDelta *= 0.3f; moveDelta *= 0.3f;
#if PLATFORM_SDL
var mouseDelta = _mouseDelta;
_mouseDelta = Float2.Zero;
#else
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
var offset = _viewMousePos - _startPos; var offset = _viewMousePos - _startPos;
if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown) if (_input.IsZooming && !_input.IsMouseRightDown && !_input.IsMouseLeftDown && !_input.IsMouseMiddleDown && !_isOrtho && !rmbWheel && !_isVirtualMouseRightDown)
@@ -1732,6 +1754,7 @@ namespace FlaxEditor.Viewport
mouseDelta += _mouseDeltaLast * _mouseAccelerationScale; mouseDelta += _mouseDeltaLast * _mouseAccelerationScale;
_mouseDeltaLast = currentDelta; _mouseDeltaLast = currentDelta;
} }
#endif
// Update // Update
moveDelta *= dt * (60.0f * 4.0f); moveDelta *= dt * (60.0f * 4.0f);
@@ -1740,12 +1763,14 @@ namespace FlaxEditor.Viewport
mouseDelta *= new Float2(1, -1); mouseDelta *= new Float2(1, -1);
UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse); UpdateView(dt, ref moveDelta, ref mouseDelta, out var centerMouse);
#if !PLATFORM_SDL
// Move mouse back to the root position // Move mouse back to the root position
if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown)) if (centerMouse && (_input.IsMouseRightDown || _input.IsMouseLeftDown || _input.IsMouseMiddleDown || _isVirtualMouseRightDown))
{ {
var center = PointToWindow(_startPos); var center = PointToWindow(_startPos);
win.MousePosition = center; win.MousePosition = center;
} }
#endif
// Change Ortho size on mouse scroll // Change Ortho size on mouse scroll
if (_isOrtho && !rmbWheel) if (_isOrtho && !rmbWheel)
@@ -1757,6 +1782,8 @@ namespace FlaxEditor.Viewport
} }
else else
{ {
#if PLATFORM_SDL
#else
if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown) if (_input.IsMouseLeftDown || _input.IsMouseRightDown || _isVirtualMouseRightDown)
{ {
// Calculate smooth mouse delta not dependant on viewport size // Calculate smooth mouse delta not dependant on viewport size
@@ -1771,6 +1798,7 @@ namespace FlaxEditor.Viewport
_mouseDelta = Float2.Zero; _mouseDelta = Float2.Zero;
} }
_mouseDeltaLast = Float2.Zero; _mouseDeltaLast = Float2.Zero;
#endif
if (ContainsFocus) if (ContainsFocus)
{ {
@@ -1820,6 +1848,12 @@ namespace FlaxEditor.Viewport
_input.MouseWheelDelta = 0; _input.MouseWheelDelta = 0;
} }
/// <inheritdoc />
public void OnMouseMoveRelative(ref Float2 mouseMotion)
{
_mouseDelta += mouseMotion;
}
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseDown(Float2 location, MouseButton button) public override bool OnMouseDown(Float2 location, MouseButton button)
{ {

View File

@@ -1117,8 +1117,11 @@ namespace FlaxEditor.Windows
if (Editor.StateMachine.IsPlayMode && !Editor.StateMachine.PlayingState.IsPaused) 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 // Center mouse in play mode
if (CenterMouseOnFocus) if (CenterMouseOnFocus || forceCenter)
{ {
var center = PointToWindow(Size * 0.5f); var center = PointToWindow(Size * 0.5f);
Root.MousePosition = center; Root.MousePosition = center;

View File

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

View File

@@ -28,8 +28,8 @@ public class Audio : EngineModule
{ {
case TargetPlatform.Windows: case TargetPlatform.Windows:
useNone = true; useNone = true;
useOpenAL = true; //useOpenAL = true;
//useXAudio2 = true; useXAudio2 = true;
break; break;
case TargetPlatform.XboxOne: case TargetPlatform.XboxOne:
case TargetPlatform.UWP: case TargetPlatform.UWP:

View File

@@ -10,7 +10,8 @@
#define DECLARE_BINARY_ASSET_HEADER(type, serializedVersion) \ #define DECLARE_BINARY_ASSET_HEADER(type, serializedVersion) \
DECLARE_ASSET_HEADER(type); \ DECLARE_ASSET_HEADER(type); \
static const uint32 SerializedVersion = serializedVersion; \ static const uint32 SerializedVersion = serializedVersion; \
uint32 GetSerializedVersion() const override { return SerializedVersion; } uint32 GetSerializedVersion() const override { return SerializedVersion; } \
static_assert(true, "")
#define REGISTER_BINARY_ASSET_ABSTRACT(type, typeName) \ #define REGISTER_BINARY_ASSET_ABSTRACT(type, typeName) \
const String type::TypeName = TEXT(typeName) const String type::TypeName = TEXT(typeName)

View File

@@ -149,6 +149,6 @@ public:
CreateAssetFunction Callback; CreateAssetFunction Callback;
}; };
#define IMPORT_SETUP(type, serializedVersion) context.Data.Header.TypeName = type::TypeName; context.Data.SerializedVersion = serializedVersion; #define IMPORT_SETUP(type, serializedVersion) context.Data.Header.TypeName = type::TypeName; context.Data.SerializedVersion = serializedVersion
#endif #endif

View File

@@ -53,5 +53,5 @@
#define API_PARAM(...) #define API_PARAM(...)
#define API_TYPEDEF(...) #define API_TYPEDEF(...)
#define API_INJECT_CODE(...) #define API_INJECT_CODE(...)
#define API_AUTO_SERIALIZATION(...) public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; #define API_AUTO_SERIALIZATION(...) public: void Serialize(SerializeStream& stream, const void* otherObj) override; void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override
#define DECLARE_SCRIPTING_TYPE_MINIMAL(type) public: friend class type##Internal; static struct ScriptingTypeInitializer TypeInitializer; #define DECLARE_SCRIPTING_TYPE_MINIMAL(type) public: friend class type##Internal; static struct ScriptingTypeInitializer TypeInitializer

View File

@@ -45,7 +45,7 @@ public:
} \ } \
} \ } \
return result; \ return result; \
} } static_assert(true, "")
// [Deprecated on 20.01.2022, expires on 20.01.2024] // [Deprecated on 20.01.2022, expires on 20.01.2024]
#define IMPLEMENT_SETTINGS_GETTER(type, field) IMPLEMENT_ENGINE_SETTINGS_GETTER(type, field) #define IMPLEMENT_SETTINGS_GETTER(type, field) IMPLEMENT_ENGINE_SETTINGS_GETTER(type, field)
@@ -69,6 +69,6 @@ public:
} \ } \
} \ } \
return result; \ return result; \
} } static_assert(true, "")
#define DECLARE_SETTINGS_GETTER(type) static type* Get() #define DECLARE_SETTINGS_GETTER(type) static type* Get()

View File

@@ -4,9 +4,9 @@
#include "Compiler.h" #include "Compiler.h"
#define SAFE_DISPOSE(x) if(x) { (x)->Dispose(); (x) = nullptr; } #define SAFE_DISPOSE(x) if(x) { (x)->Dispose(); (x) = nullptr; } static_assert(true, "")
#define SAFE_RELEASE(x) if(x) { (x)->Release(); (x) = nullptr; } #define SAFE_RELEASE(x) if(x) { (x)->Release(); (x) = nullptr; } static_assert(true, "")
#define SAFE_DELETE(x) if(x) { ::Delete(x); (x) = nullptr; } #define SAFE_DELETE(x) if(x) { ::Delete(x); (x) = nullptr; } static_assert(true, "")
#define INVALID_INDEX (-1) #define INVALID_INDEX (-1)
#define _INTERNAL_CONCAT_MACROS(a, b) a ## b #define _INTERNAL_CONCAT_MACROS(a, b) a ## b
#define CONCAT_MACROS(a, b) _INTERNAL_CONCAT_MACROS(a, b) #define CONCAT_MACROS(a, b) _INTERNAL_CONCAT_MACROS(a, b)
@@ -22,9 +22,9 @@
} }
#define OUT_OF_MEMORY Platform::OutOfMemory(__LINE__, __FILE__) #define OUT_OF_MEMORY Platform::OutOfMemory(__LINE__, __FILE__)
#define MISSING_CODE(info) Platform::MissingCode(__LINE__, __FILE__, info) #define MISSING_CODE(info) Platform::MissingCode(__LINE__, __FILE__, info)
#define NON_COPYABLE(type) type(type&&) = delete; type(const type&) = delete; type& operator=(const type&) = delete; type& operator=(type&&) = delete; #define NON_COPYABLE(type) type(type&&) = delete; type(const type&) = delete; type& operator=(const type&) = delete; type& operator=(type&&) = delete
#define POD_COPYABLE(type) \ #define POD_COPYABLE(type) \
type(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); } \ type(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); } \
type(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); } \ type(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); } \
type& operator=(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; } \ type& operator=(const type& other) { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; } \
type& operator=(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; } type& operator=(type&& other) noexcept { Platform::MemoryCopy(this, &other, sizeof(type)); return *this; } static_assert(true, "")

View File

@@ -13,154 +13,154 @@
#define DECLARE_ENUM_1(name, t0) enum class name{t0=0};\ #define DECLARE_ENUM_1(name, t0) enum class name{t0=0};\
static const int name##_Count = 1;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 1;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0)};\ ENUM_TO_STR(t0)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_2(name, t0, t1) enum class name{t0=0,t1};\ #define DECLARE_ENUM_2(name, t0, t1) enum class name{t0=0,t1};\
static const int name##_Count = 2;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 2;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_3(name, t0, t1, t2) enum class name{t0=0,t1,t2};\ #define DECLARE_ENUM_3(name, t0, t1, t2) enum class name{t0=0,t1,t2};\
static const int name##_Count = 3;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 3;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_4(name, t0, t1, t2, t3) enum class name{t0=0,t1,t2,t3};\ #define DECLARE_ENUM_4(name, t0, t1, t2, t3) enum class name{t0=0,t1,t2,t3};\
static const int name##_Count = 4;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 4;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_5(name, t0, t1, t2, t3, t4) enum class name{t0=0,t1,t2,t3,t4};\ #define DECLARE_ENUM_5(name, t0, t1, t2, t3, t4) enum class name{t0=0,t1,t2,t3,t4};\
static const int name##_Count = 5;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 5;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_6(name, t0, t1, t2, t3, t4, t5) enum class name{t0=0,t1,t2,t3,t4,t5};\ #define DECLARE_ENUM_6(name, t0, t1, t2, t3, t4, t5) enum class name{t0=0,t1,t2,t3,t4,t5};\
static const int name##_Count = 6;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 6;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_7(name, t0, t1, t2, t3, t4, t5, t6) enum class name{t0=0,t1,t2,t3,t4,t5,t6};\ #define DECLARE_ENUM_7(name, t0, t1, t2, t3, t4, t5, t6) enum class name{t0=0,t1,t2,t3,t4,t5,t6};\
static const int name##_Count = 7;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 7;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_8(name, t0, t1, t2, t3, t4, t5, t6, t7) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7};\ #define DECLARE_ENUM_8(name, t0, t1, t2, t3, t4, t5, t6, t7) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7};\
static const int name##_Count = 8;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 8;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_9(name, t0, t1, t2, t3, t4, t5, t6, t7, t8) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8};\ #define DECLARE_ENUM_9(name, t0, t1, t2, t3, t4, t5, t6, t7, t8) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8};\
static const int name##_Count = 9;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 9;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_10(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9};\ #define DECLARE_ENUM_10(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9};\
static const int name##_Count = 10;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 10;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_11(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10};\ #define DECLARE_ENUM_11(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10};\
static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_12(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11};\ #define DECLARE_ENUM_12(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11};\
static const int name##_Count = 12;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 12;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_13(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12};\ #define DECLARE_ENUM_13(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12};\
static const int name##_Count = 13;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 13;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_14(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13};\ #define DECLARE_ENUM_14(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13};\
static const int name##_Count = 14;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 14;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_15(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14};\ #define DECLARE_ENUM_15(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14};\
static const int name##_Count = 15;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 15;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_16(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15};\ #define DECLARE_ENUM_16(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15};\
static const int name##_Count = 16;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 16;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_17(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16};\ #define DECLARE_ENUM_17(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16};\
static const int name##_Count = 17;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 17;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15),ENUM_TO_STR(t16)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15),ENUM_TO_STR(t16)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
#define DECLARE_ENUM_18(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17};\ #define DECLARE_ENUM_18(name, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) enum class name{t0=0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17};\
static const int name##_Count = 17;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 17;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15),ENUM_TO_STR(t16),ENUM_TO_STR(t17)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11),ENUM_TO_STR(t12),ENUM_TO_STR(t13),ENUM_TO_STR(t14),ENUM_TO_STR(t15),ENUM_TO_STR(t16),ENUM_TO_STR(t17)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e];}static_assert(true, "")
// ..and those with base type and base value // ..and those with base type and base value
#define DECLARE_ENUM_EX_1(name, baseType, baseVal, t0) enum class name:baseType{t0=baseVal};\ #define DECLARE_ENUM_EX_1(name, baseType, baseVal, t0) enum class name:baseType{t0=baseVal};\
static const int name##_Count = 1;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 1;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0)};\ ENUM_TO_STR(t0)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_2(name, baseType, baseVal, t0, t1) enum class name:baseType{t0=baseVal,t1};\ #define DECLARE_ENUM_EX_2(name, baseType, baseVal, t0, t1) enum class name:baseType{t0=baseVal,t1};\
static const int name##_Count = 2;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 2;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_3(name, baseType, baseVal, t0, t1, t2) enum class name:baseType{t0=baseVal,t1,t2};\ #define DECLARE_ENUM_EX_3(name, baseType, baseVal, t0, t1, t2) enum class name:baseType{t0=baseVal,t1,t2};\
static const int name##_Count = 3;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 3;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_4(name, baseType, baseVal, t0, t1, t2, t3) enum class name:baseType{t0=baseVal,t1,t2,t3};\ #define DECLARE_ENUM_EX_4(name, baseType, baseVal, t0, t1, t2, t3) enum class name:baseType{t0=baseVal,t1,t2,t3};\
static const int name##_Count = 4;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 4;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_5(name, baseType, baseVal, t0, t1, t2, t3, t4) enum class name:baseType{t0=baseVal,t1,t2,t3,t4};\ #define DECLARE_ENUM_EX_5(name, baseType, baseVal, t0, t1, t2, t3, t4) enum class name:baseType{t0=baseVal,t1,t2,t3,t4};\
static const int name##_Count = 5;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 5;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_6(name, baseType, baseVal, t0, t1, t2, t3, t4, t5) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5};\ #define DECLARE_ENUM_EX_6(name, baseType, baseVal, t0, t1, t2, t3, t4, t5) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5};\
static const int name##_Count = 6;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 6;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_7(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6};\ #define DECLARE_ENUM_EX_7(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6};\
static const int name##_Count = 7;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 7;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_8(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7};\ #define DECLARE_ENUM_EX_8(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7};\
static const int name##_Count = 8;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 8;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_9(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8};\ #define DECLARE_ENUM_EX_9(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8};\
static const int name##_Count = 9;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 9;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_10(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9};\ #define DECLARE_ENUM_EX_10(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9};\
static const int name##_Count = 10;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 10;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_11(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10};\ #define DECLARE_ENUM_EX_11(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10};\
static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
#define DECLARE_ENUM_EX_12(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11};\ #define DECLARE_ENUM_EX_12(name, baseType, baseVal, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) enum class name:baseType{t0=baseVal,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11};\
static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\ static const int name##_Count = 11;static const Char**name##_Str(){static const Char* str[]={\
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9),ENUM_TO_STR(t10),ENUM_TO_STR(t11)};\
return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];} return str;}static inline const Char*ToString(name e){return name##_Str()[(int)e - baseVal];}static_assert(true, "")
// ..and those for flags // ..and those for flags
@@ -170,7 +170,7 @@ static const int name##_Count = 1;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0)};\ ENUM_TO_STR(t0)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0)}; \ (baseType)(v0)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_2(name, baseType, t0, v0, t1, v1) \ #define DECLARE_ENUM_FLAGS_2(name, baseType, t0, v0, t1, v1) \
enum class name:baseType{t0=v0,t1=v1};\ enum class name:baseType{t0=v0,t1=v1};\
@@ -178,7 +178,7 @@ static const int name##_Count = 2;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1)}; \ (baseType)(v0), (baseType)(v1)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_3(name, baseType, t0, v0, t1, v1, t2, v2) \ #define DECLARE_ENUM_FLAGS_3(name, baseType, t0, v0, t1, v1, t2, v2) \
enum class name:baseType{t0=v0,t1=v1,t2=v2};\ enum class name:baseType{t0=v0,t1=v1,t2=v2};\
@@ -186,7 +186,7 @@ static const int name##_Count = 3;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_4(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3) \ #define DECLARE_ENUM_FLAGS_4(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3};\
@@ -194,7 +194,7 @@ static const int name##_Count = 4;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_5(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4) \ #define DECLARE_ENUM_FLAGS_5(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4};\
@@ -202,7 +202,7 @@ static const int name##_Count = 5;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_6(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) \ #define DECLARE_ENUM_FLAGS_6(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5};\
@@ -210,7 +210,7 @@ static const int name##_Count = 6;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_7(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) \ #define DECLARE_ENUM_FLAGS_7(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6};\
@@ -218,7 +218,7 @@ static const int name##_Count = 7;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_8(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) \ #define DECLARE_ENUM_FLAGS_8(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7};\
@@ -226,7 +226,7 @@ static const int name##_Count = 8;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_9(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7, t8, v8) \ #define DECLARE_ENUM_FLAGS_9(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7, t8, v8) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7,t8=v8};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7,t8=v8};\
@@ -234,7 +234,7 @@ static const int name##_Count = 9;static const Char**name##_Str(){static const C
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7), (baseType)(v8)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7), (baseType)(v8)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")
#define DECLARE_ENUM_FLAGS_10(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7, t8, v8, t9, v9) \ #define DECLARE_ENUM_FLAGS_10(name, baseType, t0, v0, t1, v1, t2, v2, t3, v3, t4, v4, t5, v5, t6, v6, t7, v7, t8, v8, t9, v9) \
enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7,t8=v8,t9=v9};\ enum class name:baseType{t0=v0,t1=v1,t2=v2,t3=v3,t4=v4,t5=v5,t6=v6,t7=v7,t8=v8,t9=v9};\
@@ -242,4 +242,4 @@ static const int name##_Count = 10;static const Char**name##_Str(){static const
ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\ ENUM_TO_STR(t0),ENUM_TO_STR(t1),ENUM_TO_STR(t2),ENUM_TO_STR(t3),ENUM_TO_STR(t4),ENUM_TO_STR(t5),ENUM_TO_STR(t6),ENUM_TO_STR(t7),ENUM_TO_STR(t8),ENUM_TO_STR(t9)};\
return str;}static inline const Char*ToString(name e){static baseType val[]={\ return str;}static inline const Char*ToString(name e){static baseType val[]={\
(baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7), (baseType)(v8), (baseType)(v9)}; \ (baseType)(v0), (baseType)(v1), (baseType)(v2), (baseType)(v3), (baseType)(v4), (baseType)(v5), (baseType)(v6), (baseType)(v7), (baseType)(v8), (baseType)(v9)}; \
for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; } for (baseType i = 0; i < name##_Count; i++) { if (val[i] == (baseType)e) return name##_Str()[(int)i]; } return ENUM_TO_STR_FALLBACK; }static_assert(true, "")

View File

@@ -39,7 +39,7 @@ namespace fmt_flax
return fmt::format_to(ctx.out(), basic_string_view<Char>(TEXT(formatText)), ##__VA_ARGS__); \ return fmt::format_to(ctx.out(), basic_string_view<Char>(TEXT(formatText)), ##__VA_ARGS__); \
} \ } \
}; \ }; \
} } static_assert(true, "")
#define DEFINE_DEFAULT_FORMATTING_VIA_TO_STRING(type) \ #define DEFINE_DEFAULT_FORMATTING_VIA_TO_STRING(type) \
namespace fmt \ namespace fmt \
@@ -59,4 +59,4 @@ namespace fmt_flax
return fmt::detail::copy_str<Char>(str.Get(), str.Get() + str.Length(), ctx.out()); \ return fmt::detail::copy_str<Char>(str.Get(), str.Get() + str.Length(), ctx.out()); \
} \ } \
}; \ }; \
} } static_assert(true, "")

View File

@@ -14,7 +14,7 @@ struct Guid;
/// </summary> /// </summary>
API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData API_STRUCT(NoDefault) struct FLAXENGINE_API LogContextData
{ {
DECLARE_SCRIPTING_TYPE_STRUCTURE(LogContextData) DECLARE_SCRIPTING_TYPE_STRUCTURE(LogContextData);
/// <summary> /// <summary>
/// A GUID for an object which this context applies to. /// A GUID for an object which this context applies to.
@@ -40,7 +40,7 @@ struct TIsPODType<LogContextData>
/// </summary> /// </summary>
API_CLASS(Static) class FLAXENGINE_API LogContext API_CLASS(Static) class FLAXENGINE_API LogContext
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(LogContext) DECLARE_SCRIPTING_TYPE_MINIMAL(LogContext);
/// <summary> /// <summary>
/// Adds a log context element to the stack to be displayed in warning and error logs. /// Adds a log context element to the stack to be displayed in warning and error logs.

View File

@@ -154,7 +154,8 @@ typedef Matrix Real4x4;
inline constexpr T operator^(T a, T b) { return (T)((__underlying_type(T))a ^ (__underlying_type(T))b); } \ inline constexpr T operator^(T a, T b) { return (T)((__underlying_type(T))a ^ (__underlying_type(T))b); } \
inline T& operator|=(T& a, T b) { return a = (T)((__underlying_type(T))a | (__underlying_type(T))b); } \ inline T& operator|=(T& a, T b) { return a = (T)((__underlying_type(T))a | (__underlying_type(T))b); } \
inline T& operator&=(T& a, T b) { return a = (T)((__underlying_type(T))a & (__underlying_type(T))b); } \ inline T& operator&=(T& a, T b) { return a = (T)((__underlying_type(T))a & (__underlying_type(T))b); } \
inline T& operator^=(T& a, T b) { return a = (T)((__underlying_type(T))a ^ (__underlying_type(T))b); } inline T& operator^=(T& a, T b) { return a = (T)((__underlying_type(T))a ^ (__underlying_type(T))b); } \
static_assert(true, "")
// Returns true if given enum value has one or more enum flags set // Returns true if given enum value has one or more enum flags set
template<typename T> template<typename T>

View File

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

View File

@@ -127,6 +127,20 @@ public:
/// </summary> /// </summary>
Nullable<bool> LowDPI; 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 #if USE_EDITOR
/// <summary> /// <summary>
/// -project !path! (Startup project path) /// -project !path! (Startup project path)

View File

@@ -95,13 +95,14 @@ int32 Engine::Main(const Char* cmdLine)
CommandLine::Options.Std = true; CommandLine::Options.Std = true;
#endif #endif
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
if (Platform::Init()) if (Platform::Init())
{ {
Platform::Fatal(TEXT("Cannot init platform.")); Platform::Fatal(TEXT("Cannot init platform."));
return -1; return -1;
} }
Platform::SetHighDpiAwarenessEnabled(!CommandLine::Options.LowDPI.IsTrue());
Time::StartupTime = DateTime::Now(); Time::StartupTime = DateTime::Now();
Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory(); Globals::StartupFolder = Globals::BinariesFolder = Platform::GetMainDirectory();
#if USE_EDITOR #if USE_EDITOR

View File

@@ -39,7 +39,7 @@ public:
const Char* Name; const Char* Name;
int32 Order; int32 Order;
#define DECLARE_ENGINE_SERVICE_EVENT(result, name) virtual result name(); static void On##name(); #define DECLARE_ENGINE_SERVICE_EVENT(result, name) virtual result name(); static void On##name()
DECLARE_ENGINE_SERVICE_EVENT(bool, Init); DECLARE_ENGINE_SERVICE_EVENT(bool, Init);
DECLARE_ENGINE_SERVICE_EVENT(void, FixedUpdate); DECLARE_ENGINE_SERVICE_EVENT(void, FixedUpdate);
DECLARE_ENGINE_SERVICE_EVENT(void, Update); DECLARE_ENGINE_SERVICE_EVENT(void, Update);

View File

@@ -1148,24 +1148,44 @@ namespace FlaxEngine.Interop
clearCacheMethod.Invoke(null, new object[] { null }); clearCacheMethod.Invoke(null, new object[] { null });
Type TypeDescriptorType = typeof(System.ComponentModel.TypeDescriptor); Type TypeDescriptorType = typeof(System.ComponentModel.TypeDescriptor);
object s_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
// Added in .NET runtime 8.0.10, used as the main locking object
object s_commonSyncObject = TypeDescriptorType?.GetField("s_commonSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_commonSyncObject == null)
s_commonSyncObject = s_providerTable;
// Removed in .NET runtime 8.0.7
object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null); object s_internalSyncObject = TypeDescriptorType?.GetField("s_internalSyncObject", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
System.Collections.Hashtable s_defaultProviders = (System.Collections.Hashtable)TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null); object s_defaultProviders = TypeDescriptorType?.GetField("s_defaultProviders", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_internalSyncObject != null && s_defaultProviders != null) if (s_internalSyncObject != null && s_defaultProviders != null)
{ {
lock (s_internalSyncObject) lock (s_internalSyncObject)
s_defaultProviders.Clear(); InvokeClear(s_defaultProviders);
} }
object s_providerTable = TypeDescriptorType?.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null); // Replaces s_defaultProviders in 8.0.7
System.Collections.Hashtable s_providerTypeTable = (System.Collections.Hashtable)TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null); object s_defaultProviderInitialized = TypeDescriptorType?.GetField("s_defaultProviderInitialized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_commonSyncObject != null && s_defaultProviderInitialized != null)
{
lock (s_commonSyncObject)
InvokeClear(s_defaultProviderInitialized);
}
object s_providerTypeTable = TypeDescriptorType?.GetField("s_providerTypeTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)?.GetValue(null);
if (s_providerTable != null && s_providerTypeTable != null) if (s_providerTable != null && s_providerTypeTable != null)
{ {
lock (s_providerTable) lock (s_commonSyncObject)
s_providerTypeTable.Clear(); InvokeClear(s_providerTypeTable);
TypeDescriptorType.GetField("s_providerTable", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic) InvokeClear(s_providerTable);
?.FieldType.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear") }
?.Invoke(s_providerTable, new object[] { });
static void InvokeClear(object instance)
{
Type type = instance.GetType();
FlaxEngine.Assertions.Assert.IsTrue(type.Name == "ConcurrentDictionary`2" || type.Name == "Hashtable" || type.Name == "WeakHashtable");
type.GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(x => x.Name == "Clear")
?.Invoke(instance, new object[] { });
} }
} }

View File

@@ -6,6 +6,8 @@
#include "Engine/Core/Types/Nullable.h" #include "Engine/Core/Types/Nullable.h"
#include "Engine/Platform/Window.h" #include "Engine/Platform/Window.h"
#include "Engine/Engine/EngineService.h" #include "Engine/Engine/EngineService.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#if USE_EDITOR #if USE_EDITOR
#include "Editor/Editor.h" #include "Editor/Editor.h"
#include "Editor/Managed/ManagedEditor.h" #include "Editor/Managed/ManagedEditor.h"
@@ -13,10 +15,14 @@
#include "Engine/Engine/Engine.h" #include "Engine/Engine/Engine.h"
#endif #endif
Nullable<bool> Fullscreen; namespace
Nullable<Float2> Size; {
bool CursorVisible = true; Nullable<bool> Fullscreen;
CursorLockMode CursorLock = CursorLockMode::None; Nullable<Float2> Size;
bool CursorVisible = true;
CursorLockMode CursorLock = CursorLockMode::None;
bool LastGameViewportFocus = false;
}
class ScreenService : public EngineService class ScreenService : public EngineService
{ {
@@ -100,11 +106,25 @@ void Screen::SetCursorVisible(const bool value)
#else #else
const auto win = Engine::MainWindow; const auto win = Engine::MainWindow;
#endif #endif
bool focused = false;
if (win && Engine::HasGameViewportFocus()) if (win && Engine::HasGameViewportFocus())
{ {
win->SetCursor(value ? CursorType::Default : CursorType::Hidden); win->SetCursor(value ? CursorType::Default : CursorType::Hidden);
focused = true;
} }
else if (win)
win->SetCursor(CursorType::Default);
CursorVisible = value; 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() CursorLockMode Screen::GetCursorLock()
@@ -133,6 +153,17 @@ void Screen::SetCursorLock(CursorLockMode mode)
win->EndClippingCursor(); win->EndClippingCursor();
} }
CursorLock = mode; 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() GameWindowMode Screen::GetGameWindowMode()
@@ -190,7 +221,11 @@ void ScreenService::Update()
{ {
#if USE_EDITOR #if USE_EDITOR
// Sync current cursor state in Editor (eg. when viewport focus can change) // Sync current cursor state in Editor (eg. when viewport focus can change)
const auto win = Editor::Managed->GetGameWindow(true);
bool gameViewportFocus = win && Engine::HasGameViewportFocus();
if (gameViewportFocus != LastGameViewportFocus)
Screen::SetCursorVisible(CursorVisible); Screen::SetCursorVisible(CursorVisible);
LastGameViewportFocus = gameViewportFocus;
#endif #endif
} }

View File

@@ -94,7 +94,8 @@ bool Time::TickData::OnTickBegin(double time, float targetFps, float maxDeltaTim
if (targetFps > ZeroTolerance) if (targetFps > ZeroTolerance)
{ {
int skip = (int)(1 + (time - NextBegin) * targetFps); //int skip = (int)(1 + (time - NextBegin) * targetFps);
const int skip = (int)(1 + (time - NextBegin) / (1.0 / targetFps));
NextBegin += (1.0 / targetFps) * skip; NextBegin += (1.0 / targetFps) * skip;
} }
} }
@@ -128,18 +129,16 @@ void Time::TickData::Advance(double time, double deltaTime)
bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float maxDeltaTime) bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float maxDeltaTime)
{ {
// Check if can perform a tick // Check if can perform a tick
double deltaTime, minDeltaTime; double deltaTime;
if (FixedDeltaTimeEnable) if (FixedDeltaTimeEnable)
{ {
deltaTime = (double)FixedDeltaTimeValue; deltaTime = (double)FixedDeltaTimeValue;
minDeltaTime = deltaTime;
} }
else else
{ {
if (time < NextBegin) if (time < NextBegin)
return false; return false;
minDeltaTime = targetFps > ZeroTolerance ? 1.0 / targetFps : 0.0;
deltaTime = Math::Max((time - LastBegin), 0.0); deltaTime = Math::Max((time - LastBegin), 0.0);
if (deltaTime > maxDeltaTime) if (deltaTime > maxDeltaTime)
{ {
@@ -149,21 +148,12 @@ bool Time::FixedStepTickData::OnTickBegin(double time, float targetFps, float ma
if (targetFps > ZeroTolerance) if (targetFps > ZeroTolerance)
{ {
int skip = (int)(1 + (time - NextBegin) * targetFps); //int skip = (int)(1 + (time - NextBegin) * targetFps);
NextBegin += (1.0 / targetFps) * skip; //NextBegin += (1.0 / targetFps) * skip;
deltaTime = 1.0 / targetFps;
NextBegin += 1.0 / targetFps;
} }
} }
Samples.Add(deltaTime);
// Check if last few ticks were not taking too long so it's running slowly
const bool isRunningSlowly = Samples.Average() > 1.5 * minDeltaTime;
if (!isRunningSlowly)
{
// Make steps fixed size
const double diff = deltaTime - minDeltaTime;
time -= diff;
deltaTime = minDeltaTime;
}
// Update data // Update data
Advance(time, deltaTime); Advance(time, deltaTime);

View File

@@ -5,7 +5,6 @@
#include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/DateTime.h"
#include "Engine/Scripting/ScriptingType.h" #include "Engine/Scripting/ScriptingType.h"
#include "Engine/Core/Collections/SamplesBuffer.h"
/// <summary> /// <summary>
/// Game ticking and timing system. /// Game ticking and timing system.
@@ -16,6 +15,7 @@ API_CLASS(Static) class FLAXENGINE_API Time
friend class Engine; friend class Engine;
friend class TimeService; friend class TimeService;
friend class PhysicsSettings; friend class PhysicsSettings;
friend Window;
public: public:
/// <summary> /// <summary>
@@ -89,12 +89,7 @@ public:
class FixedStepTickData : public TickData class FixedStepTickData : public TickData
{ {
public: public:
/// <summary>
/// The last few ticks delta times. Used to check if can use fixed steps or whenever is running slowly so should use normal stepping.
/// </summary>
SamplesBuffer<double, 4> Samples;
public:
// [TickData] // [TickData]
bool OnTickBegin(double time, float targetFps, float maxDeltaTime) override; bool OnTickBegin(double time, float targetFps, float maxDeltaTime) override;
}; };

View File

@@ -543,7 +543,7 @@ int32 Foliage::GetFoliageTypesCount() const
FoliageType* Foliage::GetFoliageType(int32 index) FoliageType* Foliage::GetFoliageType(int32 index)
{ {
CHECK_RETURN(index >= 0 && index < FoliageTypes.Count(), nullptr) CHECK_RETURN(index >= 0 && index < FoliageTypes.Count(), nullptr);
return &FoliageTypes[index]; return &FoliageTypes[index];
} }

View File

@@ -4,6 +4,7 @@
#include "AndroidVulkanPlatform.h" #include "AndroidVulkanPlatform.h"
#include "../RenderToolsVulkan.h" #include "../RenderToolsVulkan.h"
#include "Engine/Platform/Window.h"
void AndroidVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers) 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); 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); ASSERT(windowHandle);
VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR); RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR);

View File

@@ -17,7 +17,7 @@ class AndroidVulkanPlatform : public VulkanPlatformBase
public: public:
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers); static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers);
static void GetDeviceExtensions(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; typedef AndroidVulkanPlatform VulkanPlatform;

View File

@@ -280,7 +280,7 @@ void CmdBufferManagerVulkan::WaitForCmdBuffer(CmdBufferVulkan* cmdBuffer, float
void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer() void CmdBufferManagerVulkan::PrepareForNewActiveCommandBuffer()
{ {
PROFILE_CPU(); PROFILE_CPU();
ASSERT_LOW_LAYER(_activeCmdBuffer == nullptr) ASSERT_LOW_LAYER(_activeCmdBuffer == nullptr);
for (int32 i = 0; i < _pool._cmdBuffers.Count(); i++) for (int32 i = 0; i < _pool._cmdBuffers.Count(); i++)
{ {
auto cmdBuffer = _pool._cmdBuffers.Get()[i]; auto cmdBuffer = _pool._cmdBuffers.Get()[i];

View File

@@ -192,7 +192,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
ASSERT_LOW_LAYER(_backBuffers.Count() == 0); ASSERT_LOW_LAYER(_backBuffers.Count() == 0);
// Create platform-dependent surface // Create platform-dependent surface
VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface); VulkanPlatform::CreateSurface(_window, GPUDeviceVulkan::Instance, &_surface);
if (_surface == VK_NULL_HANDLE) if (_surface == VK_NULL_HANDLE)
{ {
LOG(Warning, "Failed to create Vulkan surface."); LOG(Warning, "Failed to create Vulkan surface.");
@@ -374,7 +374,9 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height)
swapChainInfo.presentMode = presentMode; swapChainInfo.presentMode = presentMode;
swapChainInfo.clipped = VK_TRUE; swapChainInfo.clipped = VK_TRUE;
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
if (surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) if (_window->GetSettings().SupportsTransparency && surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
else if (surfProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; swapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
// Create swap chain // Create swap chain

View File

@@ -4,62 +4,62 @@
#include "LinuxVulkanPlatform.h" #include "LinuxVulkanPlatform.h"
#include "../RenderToolsVulkan.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" #include "Engine/Platform/Linux/IncludeX11.h"
#ifdef __cplusplus #define Display X11::Display
extern "C" { #define Window X11::Window
#endif #define VisualID X11::VisualID
#define VK_KHR_xlib_surface 1 #include "vulkan/vulkan_xlib.h"
#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 #undef Display
#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface" #undef Window
typedef VkFlags VkXlibSurfaceCreateFlagsKHR; #undef VisualID
typedef struct VkXlibSurfaceCreateInfoKHR #include "vulkan/vulkan_wayland.h"
{
VkStructureType sType;
const void* pNext;
VkXlibSurfaceCreateFlagsKHR flags;
X11::Display* dpy;
X11::Window window;
} VkXlibSurfaceCreateInfoKHR;
typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); // Export extension from volk
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
extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; extern PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR; extern PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR;
extern PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR;
extern PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR;
void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers) void LinuxVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers)
{ {
extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME); extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME);
extensions.Add(VK_KHR_XLIB_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; VkXlibSurfaceCreateInfoKHR surfaceCreateInfo;
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR); 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; surfaceCreateInfo.window = (X11::Window)windowHandle;
VALIDATE_VULKAN_RESULT(vkCreateXlibSurfaceKHR(instance, &surfaceCreateInfo, nullptr, surface)); 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 #endif

View File

@@ -19,7 +19,7 @@ class LinuxVulkanPlatform : public VulkanPlatformBase
{ {
public: public:
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers); 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; typedef LinuxVulkanPlatform VulkanPlatform;

View File

@@ -4,6 +4,7 @@
#include "MacVulkanPlatform.h" #include "MacVulkanPlatform.h"
#include "../RenderToolsVulkan.h" #include "../RenderToolsVulkan.h"
#include "Engine/Platform/Window.h"
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
void MacVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers) 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); 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; VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo;
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK); 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)); VALIDATE_VULKAN_RESULT(vkCreateMacOSSurfaceMVK(instance, &surfaceCreateInfo, nullptr, surface));
} }

View File

@@ -18,7 +18,7 @@ class MacVulkanPlatform : public VulkanPlatformBase
{ {
public: public:
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers); 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; typedef MacVulkanPlatform VulkanPlatform;

View File

@@ -6,6 +6,7 @@
#include "../RenderToolsVulkan.h" #include "../RenderToolsVulkan.h"
#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUDevice.h"
#include "Engine/Platform/Window.h"
void Win32VulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers) 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); 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; VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR); RenderToolsVulkan::ZeroStruct(surfaceCreateInfo, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
surfaceCreateInfo.hinstance = GetModuleHandle(nullptr); surfaceCreateInfo.hinstance = GetModuleHandle(nullptr);

View File

@@ -17,7 +17,7 @@ class Win32VulkanPlatform : public VulkanPlatformBase
{ {
public: public:
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers); 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; typedef Win32VulkanPlatform VulkanPlatform;

View File

@@ -5,6 +5,7 @@
#include "iOSVulkanPlatform.h" #include "iOSVulkanPlatform.h"
#include "../RenderToolsVulkan.h" #include "../RenderToolsVulkan.h"
#include "Engine/Core/Delegate.h" #include "Engine/Core/Delegate.h"
#include "Engine/Platform/Window.h"
#include <UIKit/UIKit.h> #include <UIKit/UIKit.h>
void iOSVulkanPlatform::GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers) 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); 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 // Create surface on a main UI Thread
Function<void()> func = [&windowHandle, &instance, &surface]() Function<void()> func = [&windowHandle, &instance, &surface]()
{ {

View File

@@ -18,7 +18,7 @@ class iOSVulkanPlatform : public VulkanPlatformBase
{ {
public: public:
static void GetInstanceExtensions(Array<const char*>& extensions, Array<const char*>& layers); 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; typedef iOSVulkanPlatform VulkanPlatform;

View File

@@ -79,6 +79,7 @@ Delegate<const Float2&, MouseButton> Input::MouseUp;
Delegate<const Float2&, MouseButton> Input::MouseDoubleClick; Delegate<const Float2&, MouseButton> Input::MouseDoubleClick;
Delegate<const Float2&, float> Input::MouseWheel; Delegate<const Float2&, float> Input::MouseWheel;
Delegate<const Float2&> Input::MouseMove; Delegate<const Float2&> Input::MouseMove;
Delegate<const Float2&> Input::MouseMoveRelative;
Action Input::MouseLeave; Action Input::MouseLeave;
Delegate<const Float2&, int32> Input::TouchDown; Delegate<const Float2&, int32> Input::TouchDown;
Delegate<const Float2&, int32> Input::TouchMove; Delegate<const Float2&, int32> Input::TouchMove;
@@ -209,6 +210,14 @@ void Mouse::OnMouseMove(const Float2& position, Window* target)
e.MouseData.Position = position; 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) void Mouse::OnMouseLeave(Window* target)
{ {
Event& e = _queue.AddOne(); Event& e = _queue.AddOne();
@@ -274,6 +283,11 @@ bool Mouse::Update(EventQueue& queue)
_state.MousePosition = e.MouseData.Position; _state.MousePosition = e.MouseData.Position;
break; break;
} }
case EventType::MouseMoveRelative:
{
_state.MousePosition += e.MouseMovementData.PositionRelative;
break;
}
case EventType::MouseLeave: case EventType::MouseLeave:
{ {
break; break;
@@ -731,6 +745,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position)); window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break; break;
case InputDevice::EventType::MouseMoveRelative:
window->OnMouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
window->OnMouseLeave(); window->OnMouseLeave();
break; break;
@@ -787,6 +804,9 @@ void InputService::Update()
case InputDevice::EventType::MouseMove: case InputDevice::EventType::MouseMove:
Input::MouseMove(e.MouseData.Position); Input::MouseMove(e.MouseData.Position);
break; break;
case InputDevice::EventType::MouseMoveRelative:
Input::MouseMoveRelative(e.MouseMovementData.PositionRelative);
break;
case InputDevice::EventType::MouseLeave: case InputDevice::EventType::MouseLeave:
Input::MouseLeave(); Input::MouseLeave();
break; break;
@@ -1000,12 +1020,14 @@ void InputService::Update()
} }
} }
#if !PLATFORM_SDL
// Lock mouse if need to // Lock mouse if need to
const auto lockMode = Screen::GetCursorLock(); const auto lockMode = Screen::GetCursorLock();
if (lockMode == CursorLockMode::Locked) if (lockMode == CursorLockMode::Locked)
{ {
Input::SetMousePosition(Screen::GetSize() * 0.5f); Input::SetMousePosition(Screen::GetSize() * 0.5f);
} }
#endif
// Send events for the active actions and axes (send events only in play mode) // Send events for the active actions and axes (send events only in play mode)
if (!Time::GetGamePaused()) if (!Time::GetGamePaused())

View File

@@ -107,6 +107,11 @@ public:
/// </summary> /// </summary>
API_EVENT() static Delegate<const Float2&> MouseMove; 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> /// <summary>
/// Event fired when mouse leaves window. /// Event fired when mouse leaves window.
/// </summary> /// </summary>

View File

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

View File

@@ -46,12 +46,14 @@ public:
protected: protected:
State _state; State _state;
State _prevState; State _prevState;
bool _relativeMode;
explicit Mouse() explicit Mouse()
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse")) : InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse"))
{ {
_state.Clear(); _state.Clear();
_prevState.Clear(); _prevState.Clear();
_relativeMode = false;
} }
public: public:
@@ -114,6 +116,14 @@ public:
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)]; 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: public:
/// <summary> /// <summary>
/// Sets the mouse position. /// Sets the mouse position.
@@ -121,6 +131,17 @@ public:
/// <param name="newPosition">The new position.</param> /// <param name="newPosition">The new position.</param>
virtual void SetMousePosition(const Float2& newPosition) = 0; 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> /// <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. /// 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> /// </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> /// <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); 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> /// <summary>
/// Called when mouse leaves the input source area. /// Called when mouse leaves the input source area.
/// </summary> /// </summary>

View File

@@ -101,25 +101,25 @@ Vector3 Spline::GetSplineLocalDirection(float time) const
Vector3 Spline::GetSplinePoint(int32 index) const Vector3 Spline::GetSplinePoint(int32 index) const
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero);
return _transform.LocalToWorld(Curve[index].Value.Translation); return _transform.LocalToWorld(Curve[index].Value.Translation);
} }
Vector3 Spline::GetSplineLocalPoint(int32 index) const Vector3 Spline::GetSplineLocalPoint(int32 index) const
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Vector3::Zero);
return Curve[index].Value.Translation; return Curve[index].Value.Translation;
} }
Transform Spline::GetSplineTransform(int32 index) const Transform Spline::GetSplineTransform(int32 index) const
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity);
return _transform.LocalToWorld(Curve[index].Value); return _transform.LocalToWorld(Curve[index].Value);
} }
Transform Spline::GetSplineLocalTransform(int32 index) const Transform Spline::GetSplineLocalTransform(int32 index) const
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity);
return Curve[index].Value; return Curve[index].Value;
} }
@@ -130,7 +130,7 @@ Transform Spline::GetSplineTangent(int32 index, bool isIn)
Transform Spline::GetSplineLocalTangent(int32 index, bool isIn) Transform Spline::GetSplineLocalTangent(int32 index, bool isIn)
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), Transform::Identity);
const auto& k = Curve[index]; const auto& k = Curve[index];
const auto& tangent = isIn ? k.TangentIn : k.TangentOut; const auto& tangent = isIn ? k.TangentIn : k.TangentOut;
return tangent + k.Value; return tangent + k.Value;
@@ -209,7 +209,7 @@ float Spline::GetSplineSegmentLength(int32 index) const
float Spline::GetSplineTime(int32 index) const float Spline::GetSplineTime(int32 index) const
{ {
CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f) CHECK_RETURN(index >= 0 && index < GetSplinePointsCount(), 0.0f);
return Curve[index].Time; return Curve[index].Time;
} }

View File

@@ -489,7 +489,7 @@ void SplineModel::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(BoundsScale, _boundsScale); SERIALIZE_MEMBER(BoundsScale, _boundsScale);
SERIALIZE_MEMBER(LODBias, _lodBias); SERIALIZE_MEMBER(LODBias, _lodBias);
SERIALIZE_MEMBER(ForcedLOD, _forcedLod); SERIALIZE_MEMBER(ForcedLOD, _forcedLod);
SERIALIZE_MEMBER(PreTransform, _preTransform) SERIALIZE_MEMBER(PreTransform, _preTransform);
SERIALIZE(Model); SERIALIZE(Model);
SERIALIZE(DrawModes); SERIALIZE(DrawModes);

View File

@@ -2193,7 +2193,7 @@ bool PhysicsBackend::CapsuleCastAll(void* scene, const Vector3& center, const fl
bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{ {
CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false) CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false);
SCENE_QUERY_SETUP_SWEEP_1(); SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation)); const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale))); const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
@@ -2202,7 +2202,7 @@ bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const Collis
bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, RayCastHit& hitInfo, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{ {
CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false) CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false);
SCENE_QUERY_SETUP_SWEEP_1(); SCENE_QUERY_SETUP_SWEEP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation)); const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale))); const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
@@ -2214,7 +2214,7 @@ bool PhysicsBackend::ConvexCast(void* scene, const Vector3& center, const Collis
bool PhysicsBackend::ConvexCastAll(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::ConvexCastAll(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Vector3& direction, Array<RayCastHit>& results, const Quaternion& rotation, const float maxDistance, uint32 layerMask, bool hitTriggers)
{ {
CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false) CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false);
SCENE_QUERY_SETUP_SWEEP(); SCENE_QUERY_SETUP_SWEEP();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation)); const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale))); const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
@@ -2250,7 +2250,7 @@ bool PhysicsBackend::CheckCapsule(void* scene, const Vector3& center, const floa
bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{ {
CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false) CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false);
SCENE_QUERY_SETUP_OVERLAP_1(); SCENE_QUERY_SETUP_OVERLAP_1();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation)); const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale))); const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));
@@ -2292,7 +2292,7 @@ bool PhysicsBackend::OverlapCapsule(void* scene, const Vector3& center, const fl
bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array<PhysicsColliderActor*>& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers)
{ {
CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false) CHECK_RETURN(convexMesh && convexMesh->GetOptions().Type == CollisionDataType::ConvexMesh, false);
SCENE_QUERY_SETUP_OVERLAP(); SCENE_QUERY_SETUP_OVERLAP();
const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation)); const PxTransform pose(C2P(center - scenePhysX->Origin), C2P(rotation));
const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale))); const PxConvexMeshGeometry geometry((PxConvexMesh*)convexMesh->GetConvex(), PxMeshScale(C2P(scale)));

View File

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

View File

@@ -38,7 +38,7 @@ API_ENUM() enum class NetworkIPVersion
/// </summary> /// </summary>
API_STRUCT() struct FLAXENGINE_API NetworkSocket API_STRUCT() struct FLAXENGINE_API NetworkSocket
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup) DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup);
/// <summary>Socket protocol type.</summary> /// <summary>Socket protocol type.</summary>
API_FIELD() NetworkProtocol Protocol = NetworkProtocol::Undefined; API_FIELD() NetworkProtocol Protocol = NetworkProtocol::Undefined;
@@ -52,7 +52,7 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup)
/// </summary> /// </summary>
API_STRUCT() struct FLAXENGINE_API NetworkEndPoint API_STRUCT() struct FLAXENGINE_API NetworkEndPoint
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup) DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup);
/// <summary>End-point IP version.</summary> /// <summary>End-point IP version.</summary>
API_FIELD(ReadOnly) NetworkIPVersion IPVersion = NetworkIPVersion::Undefined; API_FIELD(ReadOnly) NetworkIPVersion IPVersion = NetworkIPVersion::Undefined;
@@ -126,7 +126,7 @@ DECLARE_ENUM_OPERATORS(NetworkSocketState);
/// </summary> /// </summary>
API_STRUCT() struct FLAXENGINE_API NetworkSocketGroup API_STRUCT() struct FLAXENGINE_API NetworkSocketGroup
{ {
DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup) DECLARE_SCRIPTING_TYPE_MINIMAL(NetworkSocketGroup);
/// <summary>Group size.</summary> /// <summary>Group size.</summary>
API_FIELD(ReadOnly) uint32 Count = 0; API_FIELD(ReadOnly) uint32 Count = 0;

View File

@@ -45,6 +45,7 @@ static_assert(sizeof(double) == 8, "Invalid double type size.");
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two."); static_assert((PLATFORM_THREADS_LIMIT & (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."); 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; float PlatformBase::CustomDpiScale = 1.0f;
Array<User*, FixedAllocation<8>> PlatformBase::Users; Array<User*, FixedAllocation<8>> PlatformBase::Users;
Delegate<User*> PlatformBase::UserAdded; Delegate<User*> PlatformBase::UserAdded;
@@ -248,6 +249,15 @@ PlatformType PlatformBase::GetPlatformType()
return PLATFORM_TYPE; return PLATFORM_TYPE;
} }
#if !PLATFORM_SDL
String PlatformBase::GetDisplayServer()
{
return String::Empty;
}
#endif
bool PlatformBase::Is64BitApp() bool PlatformBase::Is64BitApp()
{ {
#if PLATFORM_64BITS #if PLATFORM_64BITS

View File

@@ -166,6 +166,13 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(PlatformBase);
/// </summary> /// </summary>
static void Exit(); static void Exit();
public:
/// <summary>
/// Application windows class name.
/// </summary>
static const Char* ApplicationClassName;
public: public:
/// <summary> /// <summary>
/// Copy memory region /// Copy memory region
@@ -338,6 +345,11 @@ public:
/// </summary> /// </summary>
API_PROPERTY() static PlatformType GetPlatformType(); API_PROPERTY() static PlatformType GetPlatformType();
/// <summary>
/// Returns the display server name on Linux.
/// </summary>
API_PROPERTY() static String GetDisplayServer() = delete;
/// <summary> /// <summary>
/// Returns true if is running 64 bit application (otherwise 32 bit). It's compile-time constant. /// Returns true if is running 64 bit application (otherwise 32 bit). It's compile-time constant.
/// </summary> /// </summary>

View File

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

View File

@@ -106,7 +106,7 @@ WindowBase::WindowBase(const CreateWindowSettings& settings)
if (settings.StartPosition == WindowStartPosition::CenterParent if (settings.StartPosition == WindowStartPosition::CenterParent
|| settings.StartPosition == WindowStartPosition::CenterScreen) || 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) if (settings.Parent != nullptr && settings.StartPosition == WindowStartPosition::CenterParent)
parentBounds = settings.Parent->GetClientBounds(); parentBounds = settings.Parent->GetClientBounds();
@@ -164,6 +164,15 @@ void WindowBase::SetIsVisible(bool isVisible)
} }
} }
bool WindowBase::IsAlwaysOnTop() const
{
return false;
}
void WindowBase::SetIsAlwaysOnTop(bool isAlwaysOnTop)
{
}
String WindowBase::ToString() const String WindowBase::ToString() const
{ {
return GetTitle(); return GetTitle();
@@ -257,6 +266,13 @@ void WindowBase::OnMouseMove(const Float2& mousePosition)
INVOKE_EVENT_PARAMS_1(OnMouseMove, (void*)&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() void WindowBase::OnMouseLeave()
{ {
PROFILE_CPU_NAMED("GUI.OnMouseLeave"); PROFILE_CPU_NAMED("GUI.OnMouseLeave");

View File

@@ -8,6 +8,7 @@
#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Scripting/ScriptingObject.h"
#include "Engine/Input/KeyboardKeys.h" #include "Engine/Input/KeyboardKeys.h"
#include "Engine/Input/Enums.h" #include "Engine/Input/Enums.h"
#include "Enums.h"
class Input; class Input;
class Engine; class Engine;
@@ -17,252 +18,6 @@ class GPUSwapChain;
class TextureData; class TextureData;
class IGuiData; 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\""); API_INJECT_CODE(cpp, "#include \"Engine/Platform/Window.h\"");
/// <summary> /// <summary>
@@ -402,6 +157,17 @@ public:
return _maximized; return _maximized;
} }
/// <summary>
/// Gets a value that indicates whether a window is always on top of other windows.
/// </summary>
API_PROPERTY() virtual bool IsAlwaysOnTop() const;
/// <summary>
/// Sets a value that indicates whether a window is always on top of other windows.
/// </summary>
/// <param name="isAlwaysOnTop">True if always on top.</param>
API_PROPERTY() virtual void SetIsAlwaysOnTop(bool isAlwaysOnTop);
/// <summary> /// <summary>
/// Gets the native window handle. /// Gets the native window handle.
/// </summary> /// </summary>
@@ -663,7 +429,7 @@ public:
public: public:
/// <summary> /// <summary>
/// Starts drag and drop operation /// Starts a drag and drop operation.
/// </summary> /// </summary>
/// <param name="data">The data.</param> /// <param name="data">The data.</param>
/// <returns>The result.</returns> /// <returns>The result.</returns>
@@ -672,6 +438,18 @@ public:
return DragDropEffect::None; return DragDropEffect::None;
} }
/// <summary>
/// Starts a window drag and drop operation.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="offset">The offset for positioning the window from cursor.</param>
/// <param name="dragSourceWindow">The window where dragging started.</param>
/// <returns>The result.</returns>
API_FUNCTION() virtual DragDropEffect DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
{
return DragDropEffect::None;
}
/// <summary> /// <summary>
/// Starts the mouse tracking. /// Starts the mouse tracking.
/// </summary> /// </summary>
@@ -837,6 +615,12 @@ public:
MouseDelegate MouseMove; MouseDelegate MouseMove;
void OnMouseMove(const Float2& mousePosition); void OnMouseMove(const Float2& mousePosition);
/// <summary>
/// Event fired when mouse moves in relative mode.
/// </summary>
MouseDelegate MouseMoveRelative;
void OnMouseMoveRelative(const Float2& mousePositionRelative);
/// <summary> /// <summary>
/// Event fired when mouse leaves window. /// Event fired when mouse leaves window.
/// </summary> /// </summary>

View File

@@ -2,7 +2,9 @@
#pragma once #pragma once
#if PLATFORM_WINDOWS #if PLATFORM_SDL && PLATFORM_LINUX
#include "SDL/SDLClipboard.h"
#elif PLATFORM_WINDOWS
#include "Windows/WindowsClipboard.h" #include "Windows/WindowsClipboard.h"
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
#include "Linux/LinuxClipboard.h" #include "Linux/LinuxClipboard.h"

View File

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

View File

@@ -26,6 +26,32 @@ API_ENUM() enum class WindowStartPosition
Manual, 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> /// <summary>
/// Settings for new window. /// Settings for new window.
/// </summary> /// </summary>
@@ -119,9 +145,15 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(CreateWindowSettings);
API_FIELD() bool IsTopmost = false; API_FIELD() bool IsTopmost = false;
/// <summary> /// <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> /// </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> /// <summary>
/// Enable/disable window sizing frame. /// Enable/disable window sizing frame.

View File

@@ -140,6 +140,9 @@ API_ENUM() enum class ArchitectureType
#if !defined(PLATFORM_SWITCH) #if !defined(PLATFORM_SWITCH)
#define PLATFORM_SWITCH 0 #define PLATFORM_SWITCH 0
#endif #endif
#if !defined(PLATFORM_SDL)
#define PLATFORM_SDL 0
#endif
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
#include "Windows/WindowsDefines.h" #include "Windows/WindowsDefines.h"

View File

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

View File

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

View File

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

View File

@@ -21,6 +21,7 @@ namespace X11
#include <X11/Xresource.h> #include <X11/Xresource.h>
#include <X11/extensions/Xinerama.h> #include <X11/extensions/Xinerama.h>
#include <X11/Xcursor/Xcursor.h> #include <X11/Xcursor/Xcursor.h>
#include <X11/extensions/Xfixes.h>
} }
// Helper macros // Helper macros

View File

@@ -93,7 +93,9 @@ X11::Cursor Cursors[(int32)CursorType::MAX];
X11::XcursorImage* CursorsImg[(int32)CursorType::MAX]; X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap; Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
Array<KeyboardKeys> KeyCodeMap; Array<KeyboardKeys> KeyCodeMap;
Delegate<void*> LinuxPlatform::xEventRecieved; #if !PLATFORM_SDL
Delegate<void*> LinuxPlatform::xEventReceived;
#endif
Window* MouseTrackingWindow = nullptr; Window* MouseTrackingWindow = nullptr;
// Message boxes configuration // Message boxes configuration
@@ -651,7 +653,11 @@ static int X11_MessageBoxLoop(MessageBoxData* data)
return 0; return 0;
} }
#if !PLATFORM_SDL
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon) 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) if (CommandLine::Options.Headless)
return DialogResult::None; 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; return data.resultButtonIndex == -1 ? DialogResult::None : data.buttons[data.resultButtonIndex].result;
} }
#if !PLATFORM_SDL
int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event) int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
{ {
if (event->error_code == 5) if (event->error_code == 5)
@@ -848,6 +856,8 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
return 0; return 0;
} }
#endif
int32 CalculateDpi() int32 CalculateDpi()
{ {
int dpi = 96; int dpi = 96;
@@ -1203,17 +1213,20 @@ public:
} }
}; };
#if !PLATFORM_SDL
struct Property struct Property
{ {
unsigned char* data; unsigned char* data;
int format, nitems; int format, nitems;
X11::Atom type; X11::Atom type;
}; };
#endif
namespace Impl namespace Impl
{ {
LinuxKeyboard* Keyboard; LinuxKeyboard* Keyboard;
LinuxMouse* Mouse; LinuxMouse* Mouse;
#if !PLATFORM_SDL
StringAnsi ClipboardText; StringAnsi ClipboardText;
void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window) 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); X11::XQueryPointer(display, w, &wtmp, &child, &tmp, &tmp, &tmp, &tmp, &utmp);
return FindAppWindow(display, child); return FindAppWindow(display, child);
} }
#endif
} }
#if !PLATFORM_SDL
class LinuxDropFilesData : public IGuiData class LinuxDropFilesData : public IGuiData
{ {
public: public:
@@ -1367,7 +1382,7 @@ public:
} }
}; };
DragDropEffect LinuxWindow::DoDragDrop(const StringView& data) DragDropEffect Window::DoDragDrop(const StringView& data)
{ {
if (CommandLine::Options.Headless) if (CommandLine::Options.Headless)
return DragDropEffect::None; return DragDropEffect::None;
@@ -1381,13 +1396,14 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
StringAnsi dataAnsi(data); StringAnsi dataAnsi(data);
LinuxDropTextData dropData; LinuxDropTextData dropData;
dropData.Text = data; dropData.Text = data;
unsigned long mainWindow = _window;
// Begin dragging // Begin dragging
auto screen = X11::XDefaultScreen(xDisplay); auto screen = X11::XDefaultScreen(xDisplay);
auto rootWindow = X11::XRootWindow(xDisplay, screen); 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; return DragDropEffect::None;
X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, _window, CurrentTime); X11::XSetSelectionOwner(xDisplay, xAtomXdndSelection, mainWindow, CurrentTime);
// Process events // Process events
X11::XEvent event; X11::XEvent event;
@@ -1495,7 +1511,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
m.window = previousWindow; m.window = previousWindow;
m.message_type = xAtomXdndLeave; m.message_type = xAtomXdndLeave;
m.format = 32; m.format = 32;
m.data.l[0] = _window; m.data.l[0] = mainWindow;
m.data.l[1] = 0; m.data.l[1] = 0;
m.data.l[2] = 0; m.data.l[2] = 0;
m.data.l[3] = 0; m.data.l[3] = 0;
@@ -1524,7 +1540,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
m.window = window; m.window = window;
m.message_type = xAtomXdndEnter; m.message_type = xAtomXdndEnter;
m.format = 32; 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[1] = Math::Min(5, version) << 24 | (formats.Count() > 3);
m.data.l[2] = formats.Count() > 0 ? formats[0] : 0; m.data.l[2] = formats.Count() > 0 ? formats[0] : 0;
m.data.l[3] = formats.Count() > 1 ? formats[1] : 0; m.data.l[3] = formats.Count() > 1 ? formats[1] : 0;
@@ -1559,7 +1575,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
m.window = window; m.window = window;
m.message_type = xAtomXdndPosition; m.message_type = xAtomXdndPosition;
m.format = 32; m.format = 32;
m.data.l[0] = _window; m.data.l[0] = mainWindow;
m.data.l[1] = 0; m.data.l[1] = 0;
m.data.l[2] = (x << 16) | y; m.data.l[2] = (x << 16) | y;
m.data.l[3] = CurrentTime; m.data.l[3] = CurrentTime;
@@ -1602,7 +1618,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
m.window = previousWindow; m.window = previousWindow;
m.message_type = xAtomXdndDrop; m.message_type = xAtomXdndDrop;
m.format = 32; m.format = 32;
m.data.l[0] = _window; m.data.l[0] = mainWindow;
m.data.l[1] = 0; m.data.l[1] = 0;
m.data.l[2] = CurrentTime; m.data.l[2] = CurrentTime;
m.data.l[3] = 0; m.data.l[3] = 0;
@@ -1649,7 +1665,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
m.window = previousWindow; m.window = previousWindow;
m.message_type = xAtomXdndLeave; m.message_type = xAtomXdndLeave;
m.format = 32; m.format = 32;
m.data.l[0] = _window; m.data.l[0] = mainWindow;
m.data.l[1] = 0; m.data.l[1] = 0;
m.data.l[2] = 0; m.data.l[2] = 0;
m.data.l[3] = 0; m.data.l[3] = 0;
@@ -1675,7 +1691,7 @@ void LinuxClipboard::SetText(const StringView& text)
{ {
if (CommandLine::Options.Headless) if (CommandLine::Options.Headless)
return; return;
auto mainWindow = (LinuxWindow*)Engine::MainWindow; auto mainWindow = Engine::MainWindow;
if (!mainWindow) if (!mainWindow)
return; return;
X11::Window window = (X11::Window)mainWindow->GetNativePtr(); X11::Window window = (X11::Window)mainWindow->GetNativePtr();
@@ -1698,7 +1714,7 @@ String LinuxClipboard::GetText()
if (CommandLine::Options.Headless) if (CommandLine::Options.Headless)
return String::Empty; return String::Empty;
String result; String result;
auto mainWindow = (LinuxWindow*)Engine::MainWindow; auto mainWindow = Engine::MainWindow;
if (!mainWindow) if (!mainWindow)
return result; return result;
X11::Window window = (X11::Window)mainWindow->GetNativePtr(); X11::Window window = (X11::Window)mainWindow->GetNativePtr();
@@ -1727,6 +1743,8 @@ Array<String> LinuxClipboard::GetFiles()
return Array<String>(); return Array<String>();
} }
#endif
void* LinuxPlatform::GetXDisplay() void* LinuxPlatform::GetXDisplay()
{ {
return xDisplay; return xDisplay;
@@ -2090,6 +2108,7 @@ bool LinuxPlatform::Init()
DeviceId.D = (uint32)UnixCpu.ClockSpeed * UnixCpu.LogicalProcessorCount * UnixCpu.ProcessorCoreCount * UnixCpu.CacheLineSize; DeviceId.D = (uint32)UnixCpu.ClockSpeed * UnixCpu.LogicalProcessorCount * UnixCpu.ProcessorCoreCount * UnixCpu.CacheLineSize;
} }
#if !PLATFORM_SDL
// Get user locale string // Get user locale string
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
const char* locale = setlocale(LC_CTYPE, NULL); const char* locale = setlocale(LC_CTYPE, NULL);
@@ -2099,6 +2118,7 @@ bool LinuxPlatform::Init()
UserLocale.Replace('_', '-'); UserLocale.Replace('_', '-');
if (UserLocale == TEXT("C")) if (UserLocale == TEXT("C"))
UserLocale = TEXT("en"); UserLocale = TEXT("en");
#endif
// Get computer name string // Get computer name string
gethostname(buffer, UNIX_APP_BUFF_SIZE); gethostname(buffer, UNIX_APP_BUFF_SIZE);
@@ -2117,10 +2137,15 @@ bool LinuxPlatform::Init()
Platform::MemoryClear(Cursors, sizeof(Cursors)); Platform::MemoryClear(Cursors, sizeof(Cursors));
Platform::MemoryClear(CursorsImg, sizeof(CursorsImg)); Platform::MemoryClear(CursorsImg, sizeof(CursorsImg));
// Skip setup if running in headless mode (X11 might not be available on servers) // Skip setup if running in headless mode (X11 might not be available on servers)
if (CommandLine::Options.Headless) if (CommandLine::Options.Headless)
return false; return false;
#if PLATFORM_SDL
xDisplay = X11::XOpenDisplay(nullptr);
#endif
#if !PLATFORM_SDL
X11::XInitThreads(); X11::XInitThreads();
xDisplay = X11::XOpenDisplay(nullptr); xDisplay = X11::XOpenDisplay(nullptr);
@@ -2259,7 +2284,7 @@ bool LinuxPlatform::Init()
Input::Mouse = Impl::Mouse = New<LinuxMouse>(); Input::Mouse = Impl::Mouse = New<LinuxMouse>();
Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>(); Input::Keyboard = Impl::Keyboard = New<LinuxKeyboard>();
LinuxInput::Init(); LinuxInput::Init();
#endif
return false; return false;
} }
@@ -2269,6 +2294,7 @@ void LinuxPlatform::BeforeRun()
void LinuxPlatform::Tick() void LinuxPlatform::Tick()
{ {
#if !PLATFORM_SDL
UnixPlatform::Tick(); UnixPlatform::Tick();
LinuxInput::UpdateState(); LinuxInput::UpdateState();
@@ -2285,9 +2311,9 @@ void LinuxPlatform::Tick()
continue; continue;
// External event handling // External event handling
xEventRecieved(&event); xEventReceived(&event);
LinuxWindow* window; Window* window;
switch (event.type) switch (event.type)
{ {
case ClientMessage: case ClientMessage:
@@ -2619,6 +2645,7 @@ void LinuxPlatform::Tick()
} }
//X11::XFlush(xDisplay); //X11::XFlush(xDisplay);
#endif
} }
void LinuxPlatform::BeforeExit() void LinuxPlatform::BeforeExit()
@@ -2627,6 +2654,7 @@ void LinuxPlatform::BeforeExit()
void LinuxPlatform::Exit() void LinuxPlatform::Exit()
{ {
#if !PLATFORM_SDL
for (int32 i = 0; i < (int32)CursorType::MAX; i++) for (int32 i = 0; i < (int32)CursorType::MAX; i++)
{ {
if (Cursors[i]) if (Cursors[i])
@@ -2652,8 +2680,10 @@ void LinuxPlatform::Exit()
X11::XCloseDisplay(xDisplay); X11::XCloseDisplay(xDisplay);
xDisplay = nullptr; xDisplay = nullptr;
} }
#endif
} }
#if !PLATFORM_SDL
int32 LinuxPlatform::GetDpi() int32 LinuxPlatform::GetDpi()
{ {
return SystemDpi; return SystemDpi;
@@ -2663,6 +2693,7 @@ String LinuxPlatform::GetUserLocaleName()
{ {
return UserLocale; return UserLocale;
} }
#endif
String LinuxPlatform::GetComputerName() String LinuxPlatform::GetComputerName()
{ {
@@ -2870,10 +2901,12 @@ bool LinuxPlatform::SetWorkingDirectory(const String& path)
return chdir(StringAsANSI<>(*path).Get()) != 0; return chdir(StringAsANSI<>(*path).Get()) != 0;
} }
#if !PLATFORM_SDL
Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings) Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings)
{ {
return New<LinuxWindow>(settings); return New<LinuxWindow>(settings);
} }
#endif
extern char **environ; extern char **environ;

View File

@@ -36,7 +36,7 @@ public:
/// <summary> /// <summary>
/// An event that is fired when an XEvent is received during platform tick. /// An event that is fired when an XEvent is received during platform tick.
/// </summary> /// </summary>
static Delegate<void*> xEventRecieved; static Delegate<void*> xEventReceived;
public: public:
@@ -122,8 +122,10 @@ public:
static void Tick(); static void Tick();
static void BeforeExit(); static void BeforeExit();
static void Exit(); static void Exit();
#if !PLATFORM_SDL
static int32 GetDpi(); static int32 GetDpi();
static String GetUserLocaleName(); static String GetUserLocaleName();
#endif
static String GetComputerName(); static String GetComputerName();
static bool GetHasFocus(); static bool GetHasFocus();
static bool CanOpenUrl(const StringView& url); static bool CanOpenUrl(const StringView& url);
@@ -138,7 +140,9 @@ public:
static Guid GetUniqueDeviceId(); static Guid GetUniqueDeviceId();
static String GetWorkingDirectory(); static String GetWorkingDirectory();
static bool SetWorkingDirectory(const String& path); static bool SetWorkingDirectory(const String& path);
#if !PLATFORM_SDL
static Window* CreateWindow(const CreateWindowSettings& settings); static Window* CreateWindow(const CreateWindowSettings& settings);
#endif
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result); static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
static bool GetEnvironmentVariable(const String& name, String& value); static bool GetEnvironmentVariable(const String& name, String& value);
static bool SetEnvironmentVariable(const String& name, const String& value); static bool SetEnvironmentVariable(const String& name, const String& value);

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#if PLATFORM_LINUX #if PLATFORM_LINUX && !PLATFORM_SDL
#include "../Window.h" #include "../Window.h"
#include "Engine/Input/Input.h" #include "Engine/Input/Input.h"
@@ -617,7 +617,7 @@ void LinuxWindow::OnButtonPress(void* event)
} }
// Handle double-click // Handle double-click
if (buttonEvent->button == Button1) if (buttonEvent->button == Button1 && !Input::Mouse->IsRelative())
{ {
if ( if (
buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) && buttonEvent->time < (MouseLastButtonPressTime + MouseDoubleClickTime) &&

View File

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

View File

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

View File

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

View File

@@ -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> /// <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> /// <returns>The message box dialog result.</returns>
API_FUNCTION() static DialogResult Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon); 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
}; };

View File

@@ -89,6 +89,21 @@ public class Platform : EngineModule
break; break;
default: throw new InvalidPlatformException(options.Platform.Target); 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.Platform.Target == TargetPlatform.Linux)
options.PublicDependencies.Add("Wayland");
}
if (options.Target.IsEditor) if (options.Target.IsEditor)
{ {
// Include platform settings headers // Include platform settings headers

View File

@@ -8,7 +8,9 @@
#include "Types.h" #include "Types.h"
#include "Defines.h" #include "Defines.h"
#if PLATFORM_WINDOWS #if PLATFORM_SDL
#include "SDL/SDLPlatform.h"
#elif PLATFORM_WINDOWS
#include "Windows/WindowsPlatform.h" #include "Windows/WindowsPlatform.h"
#elif PLATFORM_UWP #elif PLATFORM_UWP
#include "UWP/UWPPlatform.h" #include "UWP/UWPPlatform.h"
@@ -44,7 +46,7 @@
PLATFORM_DEBUG_BREAK; \ PLATFORM_DEBUG_BREAK; \
} \ } \
Platform::Assert(#expression, __FILE__, __LINE__); \ Platform::Assert(#expression, __FILE__, __LINE__); \
} } static_assert(true, "")
#else #else
// Performs a hard assertion of the expression. Crashes the engine and triggers a debugger break if the expression fails. // Performs a hard assertion of the expression. Crashes the engine and triggers a debugger break if the expression fails.
#define ASSERT(expression) ((void)0) #define ASSERT(expression) ((void)0)
@@ -63,14 +65,14 @@
{ \ { \
Platform::CheckFailed(#expression, __FILE__, __LINE__); \ Platform::CheckFailed(#expression, __FILE__, __LINE__); \
return; \ return; \
} } static_assert(true, "")
// Performs a soft check of the expression. Logs the expression failure and returns from the function call using the given return value. // Performs a soft check of the expression. Logs the expression failure and returns from the function call using the given return value.
#define CHECK_RETURN(expression, returnValue) \ #define CHECK_RETURN(expression, returnValue) \
if (!(expression)) \ if (!(expression)) \
{ \ { \
Platform::CheckFailed(#expression, __FILE__, __LINE__); \ Platform::CheckFailed(#expression, __FILE__, __LINE__); \
return returnValue; \ return returnValue; \
} } static_assert(true, "")
#if ENABLE_ASSERTION #if ENABLE_ASSERTION
// Performs a soft check of the expression. Logs the expression failure and returns from the function call. // Performs a soft check of the expression. Logs the expression failure and returns from the function call.

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_SDL && PLATFORM_LINUX
#include "Engine/Platform/Base/ClipboardBase.h"
/// <summary>
/// SDL implementation of the clipboard service for Linux platform.
/// </summary>
class FLAXENGINE_API SDLClipboard : public ClipboardBase
{
public:
// [ClipboardBase]
static void Clear();
static void SetText(const StringView& text);
static void SetRawData(const Span<byte>& data);
static void SetFiles(const Array<String>& files);
static String GetText();
static Array<byte> GetRawData();
static Array<String> GetFiles();
};
#endif

View File

@@ -0,0 +1,823 @@
// 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 "Engine/Engine/Engine.h"
#include "Engine/Engine/Screen.h"
#if USE_EDITOR
#include "Editor/Editor.h"
#include "Editor/Managed/ManagedEditor.h"
#endif
#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 = Float2::Zero;
Window* relativeModeWindow = nullptr;
const SDL_Rect* oldScreenRect = nullptr;
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
{
ASSERT(relativeModeWindow != nullptr);
return relativeModeWindow->ClientToScreen(oldPosition);
}
// [Mouse]
void SetMousePosition(const Float2& screenPosition) final override
{
#if USE_EDITOR
auto window = Editor::Managed->GetGameWindow();
if (window == nullptr)
window = Engine::MainWindow;
#else
auto window = Engine::MainWindow;
#endif
Float2 position = window->ScreenToClient(screenPosition);
SDL_WarpMouseInWindow(static_cast<SDLWindow*>(window)->_window, position.X, position.Y);
OnMouseMoved(position);
}
void SetRelativeMode(bool relativeMode, Window* window) final override
{
if (relativeMode == _relativeMode)
return;
auto windowHandle = static_cast<SDLWindow*>(window)->_window;
if (relativeMode)
{
relativeModeWindow = window;
SDL_GetMouseState(&oldPosition.X, &oldPosition.Y);
if (!SDL_CursorVisible())
{
// Trap the cursor in current location
SDL_Rect clipRect = { (int)oldPosition.X, (int)oldPosition.Y, 1, 1 };
oldScreenRect = SDL_GetWindowMouseRect(windowHandle);
SDL_SetWindowMouseRect(windowHandle, &clipRect);
}
}
else
{
if (relativeModeWindow != window)
{
// FIXME: When floating game window is focused and editor viewport activated, the relative mode gets stuck
return;
}
SDL_SetWindowMouseRect(windowHandle, nullptr);//oldScreenRect);
SDL_WarpMouseInWindow(windowHandle, oldPosition.X, oldPosition.Y);
oldScreenRect = nullptr;
relativeModeWindow = nullptr;
}
Mouse::SetRelativeMode(relativeMode, window);
if (!SDL_SetWindowRelativeMouseMode(windowHandle, relativeMode))
LOG(Error, "Failed to set mouse relative mode: {0}", String(SDL_GetError()));
}
};
/// <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 });
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

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