Compare commits
9 Commits
5b4d01c42d
...
d41f5bd3e6
| Author | SHA1 | Date | |
|---|---|---|---|
| d41f5bd3e6 | |||
| 75c44122c6 | |||
| 4204e7f571 | |||
| 945988b20c | |||
| 2a8eec93e7 | |||
| c6aa328aed | |||
| 46c2986957 | |||
| 55e756ae1a | |||
| b5ac5c5a60 |
@@ -20,7 +20,7 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <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 />
|
||||
public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.CPPScript128;
|
||||
|
||||
@@ -186,12 +186,12 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
foreach (var file in files)
|
||||
FindNewKeysCSharp(file, newKeys, allKeys);
|
||||
|
||||
// C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories);
|
||||
// C/C++
|
||||
files = Directory.GetFiles(Globals.ProjectSourceFolder, "*.cpp", SearchOption.AllDirectories).Concat(Directory.GetFiles(Globals.ProjectSourceFolder, "*.c", SearchOption.AllDirectories)).ToArray();
|
||||
filesCount += files.Length;
|
||||
foreach (var file in files)
|
||||
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;
|
||||
foreach (var file in files)
|
||||
FindNewKeysCpp(file, newKeys, allKeys);
|
||||
|
||||
@@ -21,21 +21,22 @@ namespace FlaxEditor.GUI.Docking
|
||||
private DockState _toSet;
|
||||
private DockPanel _toDock;
|
||||
private bool _lateDragOffsetUpdate;
|
||||
private float _lateDragStartTimer;
|
||||
|
||||
private Rectangle _rLeft, _rRight, _rBottom, _rUpper, _rCenter;
|
||||
|
||||
private DockHintWindow(FloatWindowDockPanel toMove)
|
||||
private DockHintWindow(FloatWindowDockPanel toMove, bool lateDragStart)
|
||||
{
|
||||
_toMove = toMove;
|
||||
_toSet = DockState.Float;
|
||||
var window = toMove.Window.Window;
|
||||
|
||||
// Remove focus from drag target
|
||||
_toMove.Focus();
|
||||
_toMove.Defocus();
|
||||
//_toMove.Focus();
|
||||
//_toMove.Defocus();
|
||||
|
||||
// Focus window
|
||||
window.Focus();
|
||||
//window.Focus();
|
||||
|
||||
// Check if window is maximized and restore window.
|
||||
if (window.IsMaximized)
|
||||
@@ -48,43 +49,62 @@ namespace FlaxEditor.GUI.Docking
|
||||
}
|
||||
|
||||
// Calculate dragging offset and move window to the destination position
|
||||
var mouseScreenPosition = Platform.MousePosition;
|
||||
var mouseClientPosition = Platform.MousePosition;
|
||||
CalculateDragOffset(mouseClientPosition);
|
||||
|
||||
// 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;
|
||||
//if (mouseScreenPosition != Float2.Zero)
|
||||
// CalculateDragOffset(mouseScreenPosition);
|
||||
//else
|
||||
// _lateDragOffsetUpdate = true;
|
||||
|
||||
|
||||
|
||||
// Get initial size
|
||||
_defaultWindowSize = window.Size;
|
||||
|
||||
// Init proxy window
|
||||
Proxy.Init(ref _defaultWindowSize);
|
||||
/*Proxy.Init(ref _defaultWindowSize);
|
||||
|
||||
// Bind events
|
||||
*/
|
||||
|
||||
//FlaxEngine.Input.MouseMove += OnMouseMove;
|
||||
FlaxEngine.Scripting.Update += OnUpdate;
|
||||
_toMove.Window.Window.MouseUp += OnMouseUp;
|
||||
/*
|
||||
Proxy.Window.MouseUp += OnMouseUp;
|
||||
Proxy.Window.MouseMove += OnMouseMove;
|
||||
Proxy.Window.LostFocus += OnLostFocus;
|
||||
_toMove.Window.Window.MouseUp += OnMouseUp; // Intercept the drag release mouse event from source window
|
||||
|
||||
*/
|
||||
// Update window GUI
|
||||
Proxy.Window.GUI.PerformLayout();
|
||||
//Proxy.Window.GUI.PerformLayout();
|
||||
|
||||
// Update rectangles
|
||||
UpdateRects();
|
||||
|
||||
// Enable hit window presentation
|
||||
Proxy.Window.RenderingEnabled = true;
|
||||
/*Proxy.Window.RenderingEnabled = true;
|
||||
Proxy.Window.Show();
|
||||
Proxy.Window.Focus();
|
||||
Proxy.Window.Focus();*/
|
||||
|
||||
// Hide base window
|
||||
window.Hide();
|
||||
//window.Hide();
|
||||
|
||||
|
||||
if (lateDragStart)
|
||||
{
|
||||
// The window needs some time to be fully ready for dragging
|
||||
//_lateDragStartTimer = 1.5f;
|
||||
window.StartDragging(_dragOffset);
|
||||
}
|
||||
else
|
||||
window.StartDragging(_dragOffset);
|
||||
|
||||
// Start tracking mouse
|
||||
Proxy.Window.StartTrackingMouse(false);
|
||||
//Proxy.Window.StartTrackingMouse(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +113,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
public void Dispose()
|
||||
{
|
||||
// End tracking mouse
|
||||
Proxy.Window.EndTrackingMouse();
|
||||
/*Proxy.Window.EndTrackingMouse();
|
||||
|
||||
// Disable rendering
|
||||
Proxy.Window.RenderingEnabled = false;
|
||||
@@ -106,11 +126,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
_toMove.Window.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
// Hide the proxy
|
||||
Proxy.Hide();
|
||||
Proxy.Hide();*/
|
||||
|
||||
RemoveDockHints();
|
||||
|
||||
//FlaxEngine.Input.MouseMove -= OnMouseMove;
|
||||
FlaxEngine.Scripting.Update -= OnUpdate;
|
||||
if (_toMove?.Window?.Window)
|
||||
_toMove.Window.Window.MouseUp -= OnMouseUp;
|
||||
|
||||
if (_toMove == null)
|
||||
return;
|
||||
|
||||
_toMove.Window?.Window.StopDragging();
|
||||
|
||||
// Check if window won't be docked
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
@@ -149,13 +178,16 @@ namespace FlaxEditor.GUI.Docking
|
||||
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)
|
||||
if (_toMove.TabsCount > 0)
|
||||
{
|
||||
_toMove.GetTab(0).Show(DockState.DockFill, firstTab);
|
||||
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
|
||||
@@ -179,7 +211,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (toMove == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return new DockHintWindow(toMove);
|
||||
return new DockHintWindow(toMove, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,13 +229,13 @@ namespace FlaxEditor.GUI.Docking
|
||||
|
||||
// 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);
|
||||
/*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);
|
||||
return new DockHintWindow(floatingPanelToMove, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -247,6 +279,47 @@ namespace FlaxEditor.GUI.Docking
|
||||
Editor.Log($"_dragOffset: {_dragOffset}, mouse: {mouseScreenPosition}, basewinpos: {baseWinPos}");
|
||||
}
|
||||
|
||||
DockHintControl _dockHintDown;
|
||||
DockHintControl _dockHintUp;
|
||||
DockHintControl _dockHintLeft;
|
||||
DockHintControl _dockHintRight;
|
||||
DockHintControl _dockHintCenter;
|
||||
private void AddDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
_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));
|
||||
|
||||
DockHintControl AddHintControl(Float2 pivot)
|
||||
{
|
||||
DockHintControl hintControl = _toDock.AddChild<DockHintControl>();
|
||||
hintControl.Size = new Float2(Proxy.HintWindowsSize);
|
||||
hintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
hintControl.Pivot = pivot;
|
||||
hintControl.PivotRelative = true;
|
||||
//.SetAnchorPreset(AnchorPresets.StretchAll, true);
|
||||
return hintControl;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveDockHints()
|
||||
{
|
||||
if (_toDock == null)
|
||||
return;
|
||||
|
||||
_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()
|
||||
{
|
||||
// Cache mouse position
|
||||
@@ -254,12 +327,26 @@ namespace FlaxEditor.GUI.Docking
|
||||
|
||||
// Check intersection with any dock panel
|
||||
var uiMouse = _mouse;
|
||||
_toDock = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
var dockPanel = _toMove.MasterPanel.HitTest(ref uiMouse, _toMove);
|
||||
|
||||
if (dockPanel != _toDock)
|
||||
{
|
||||
//Editor.Log($"UpdateRects: {_mouse}, panel: {dockPanel?.RootWindow?.Window?.Title}");
|
||||
|
||||
_toDock?.RootWindow.Window.RemoveDockHints();
|
||||
RemoveDockHints();
|
||||
|
||||
_toDock = dockPanel;
|
||||
_toDock?.RootWindow.Window.CreateDockHints();
|
||||
AddDockHints();
|
||||
}
|
||||
|
||||
// Check dock state to use
|
||||
bool showProxyHints = _toDock != null;
|
||||
bool showBorderHints = showProxyHints;
|
||||
bool showCenterHint = showProxyHints;
|
||||
DockHintControl hoveredHintControl = null;
|
||||
Float2 hoveredSizeOverride = Float2.Zero;
|
||||
if (showProxyHints)
|
||||
{
|
||||
// If moved window has not only tabs but also child panels disable docking as tab
|
||||
@@ -274,11 +361,12 @@ namespace FlaxEditor.GUI.Docking
|
||||
_rectDock = _toDock.DockAreaBounds;
|
||||
|
||||
// Cache dock rectangles
|
||||
var size = _rectDock.Size;
|
||||
var offset = _rectDock.Location;
|
||||
var size = _rectDock.Size / Platform.DpiScale;
|
||||
var offset = _toDock.PointFromScreen(_rectDock.Location);
|
||||
var borderMargin = 4.0f;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize * Platform.DpiScale;
|
||||
var hintWindowsSize = Proxy.HintWindowsSize;
|
||||
var hintWindowsSize2 = hintWindowsSize * 0.5f;
|
||||
var hintPreviewSize = new Float2(Math.Max(Proxy.HintWindowsSize * 2, size.X * 0.5f), Math.Max(Proxy.HintWindowsSize * 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;
|
||||
@@ -291,71 +379,150 @@ namespace FlaxEditor.GUI.Docking
|
||||
DockState toSet = DockState.Float;
|
||||
if (showBorderHints)
|
||||
{
|
||||
if (_rUpper.Contains(_mouse))
|
||||
if (_rUpper.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockTop;
|
||||
else if (_rBottom.Contains(_mouse))
|
||||
hoveredHintControl = _dockHintUp;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rBottom.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockBottom;
|
||||
else if (_rLeft.Contains(_mouse))
|
||||
hoveredHintControl = _dockHintDown;
|
||||
hoveredSizeOverride = new Float2(size.X, hintPreviewSize.Y);
|
||||
}
|
||||
else if (_rLeft.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockLeft;
|
||||
else if (_rRight.Contains(_mouse))
|
||||
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(_mouse))
|
||||
if (showCenterHint && _rCenter.Contains(_toDock.PointFromScreen(_mouse)))
|
||||
{
|
||||
toSet = DockState.DockFill;
|
||||
_toSet = toSet;
|
||||
hoveredHintControl = _dockHintCenter;
|
||||
hoveredSizeOverride = new Float2(size.X, size.Y);
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (toSet != DockState.Float)
|
||||
Editor.Log($"docking: {toSet}");
|
||||
|
||||
_toSet = toSet;
|
||||
}
|
||||
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);
|
||||
//_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
if (hoveredHintControl != null)
|
||||
hoveredHintControl.BackgroundColor = Color.Green;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hoveredHintControl != _dockHintDown)
|
||||
{
|
||||
_dockHintDown.Size = new Float2(Proxy.HintWindowsSize);
|
||||
_dockHintDown.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintLeft)
|
||||
{
|
||||
_dockHintLeft.Size = new Float2(Proxy.HintWindowsSize);
|
||||
_dockHintLeft.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintRight)
|
||||
{
|
||||
_dockHintRight.Size = new Float2(Proxy.HintWindowsSize);
|
||||
_dockHintRight.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintUp)
|
||||
{
|
||||
_dockHintUp.Size = new Float2(Proxy.HintWindowsSize);
|
||||
_dockHintUp.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (hoveredHintControl != _dockHintCenter)
|
||||
{
|
||||
_dockHintCenter.Size = new Float2(Proxy.HintWindowsSize);
|
||||
_dockHintCenter.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(0.6f);
|
||||
}
|
||||
if (_toSet == DockState.Float)
|
||||
{
|
||||
// Floating window over dock panel
|
||||
_rectWindow = new Rectangle(_mouse - _dragOffset, _defaultWindowSize);
|
||||
if (hoveredHintControl != null)
|
||||
hoveredHintControl.BackgroundColor = Color.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use only part of the dock panel to show hint
|
||||
_rectWindow = CalculateDockRect(_toSet, ref _rectDock);
|
||||
if (hoveredHintControl != null)
|
||||
{
|
||||
hoveredHintControl.BackgroundColor = Style.Current.DragWindow.AlphaMultiplied(1.0f);
|
||||
hoveredHintControl.Size = hoveredSizeOverride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Update proxy hint windows visibility
|
||||
_dockHintDown.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintLeft.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintRight.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintUp.Visible = showProxyHints & showBorderHints;
|
||||
_dockHintCenter.Visible = showProxyHints & showCenterHint;
|
||||
}
|
||||
|
||||
// Update proxy window
|
||||
Proxy.Window.ClientBounds = _rectWindow;
|
||||
//Proxy.Window.ClientBounds = _rectWindow;
|
||||
}
|
||||
|
||||
private void OnMouseUp(ref Float2 location, MouseButton button, ref bool handled)
|
||||
{
|
||||
Editor.Log("DockHintWindow.OnMouseUp");
|
||||
if (button == MouseButton.Left)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(ref Float2 mousePos)
|
||||
private void OnUpdate()
|
||||
{
|
||||
if (_lateDragStartTimer > 0)
|
||||
{
|
||||
_lateDragStartTimer -= Time.UnscaledDeltaTime;
|
||||
if (_lateDragStartTimer <= 0)
|
||||
_toMove.Window.Window.StartDragging(_dragOffset);
|
||||
}
|
||||
var mousePos = Platform.MousePosition;
|
||||
if (_mouse != mousePos)
|
||||
{
|
||||
OnMouseMove(mousePos);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMouseMove(Float2 mousePos)
|
||||
{
|
||||
// Recalculate the drag offset because the current mouse screen position was invalid when we initialized the window
|
||||
if (_lateDragOffsetUpdate)
|
||||
@@ -374,6 +541,15 @@ namespace FlaxEditor.GUI.Docking
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public class DockHintControl : Control
|
||||
{
|
||||
public DockHintControl()
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll;
|
||||
Offsets = Margin.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper proxy windows shared across docking panels. They are used to visualize docking window locations.
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MousePosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The mouse position.
|
||||
/// </summary>
|
||||
public Float2 MouseStartPosition = Float2.Minimum;
|
||||
|
||||
/// <summary>
|
||||
/// The start drag asynchronous window.
|
||||
@@ -355,6 +360,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (IsSingleFloatingWindow)
|
||||
return base.OnMouseDown(location, button);
|
||||
MouseDownWindow = GetTabAtPos(location, out IsMouseDownOverCross);
|
||||
MouseStartPosition = location;
|
||||
|
||||
// Check buttons
|
||||
if (button == MouseButton.Left)
|
||||
@@ -441,6 +447,20 @@ namespace FlaxEditor.GUI.Docking
|
||||
StartDrag(MouseDownWindow);
|
||||
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
|
||||
else if (MouseDownWindow != null && _panel.TabsCount > 1)
|
||||
{
|
||||
|
||||
@@ -200,13 +200,16 @@ namespace FlaxEditor.GUI.Docking
|
||||
windowGUI.PerformLayout();
|
||||
|
||||
// Show
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
window.Show();
|
||||
window.BringToFront();
|
||||
window.Focus();
|
||||
OnShow();
|
||||
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
// Perform layout again
|
||||
windowGUI.PerformLayout();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -51,8 +51,9 @@ namespace FlaxEditor.GUI.Docking
|
||||
if (_window == null)
|
||||
return;
|
||||
|
||||
_window.Window.StartDragging(Float2.Zero);
|
||||
// Create docking hint window
|
||||
DockHintWindow.Create(this);
|
||||
//DockHintWindow.Create(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
/// <summary>
|
||||
/// Performs hit test over dock panel.
|
||||
/// </summary>
|
||||
/// <param name="position">Screen space position to test.</param>
|
||||
/// <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>
|
||||
/// <returns>Dock panel that has been hit or null if nothing found.</returns>
|
||||
public DockPanel HitTest(ref Float2 position, FloatWindowDockPanel excluded)
|
||||
@@ -87,13 +87,39 @@ namespace FlaxEditor.GUI.Docking
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
return result;
|
||||
if (win.Window.Window.IsFocused) // We can't use screen space position in some platforms, only check windows under the cursor
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
{
|
||||
Editor.Log($"hit: {win.Window.Window.Title}");
|
||||
result = result;
|
||||
}
|
||||
else
|
||||
Editor.Log($"no hit: {win.Window.Window.Title}");
|
||||
}
|
||||
else
|
||||
Editor.Log($"no focus: {win.Window.Window.Title}");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < FloatingPanels.Count; i++)
|
||||
{
|
||||
var win = FloatingPanels[i];
|
||||
if (win.Visible && win != excluded)
|
||||
{
|
||||
if (win.Window.Window.IsFocused) // We can't use screen space position in some platforms, only check windows under the cursor
|
||||
{
|
||||
var result = win.HitTest(ref position);
|
||||
if (result != null)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
if (!Root?.RootWindow.Window.IsFocused ?? false)
|
||||
return null;
|
||||
return base.HitTest(ref position);
|
||||
}
|
||||
|
||||
|
||||
@@ -1008,7 +1008,7 @@ namespace FlaxEditor.Modules
|
||||
ContentItem item;
|
||||
if (path.EndsWith(".cs"))
|
||||
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);
|
||||
else if (path.EndsWith(".shader") || path.EndsWith(".hlsl"))
|
||||
item = new ShaderSourceItem(path);
|
||||
|
||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Modules
|
||||
outputExtension = extension;
|
||||
|
||||
// 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
|
||||
Editor.LogWarning(string.Format("Cannot import \'{0}\' to \'{1}\'. The target directory cannot have scripts.", inputPath, targetLocation.Node.Path));
|
||||
|
||||
@@ -120,9 +120,13 @@ void ScriptsBuilderImpl::sourceDirEvent(const String& path, FileSystemAction act
|
||||
// Discard non-source files or generated files
|
||||
if ((!path.EndsWith(TEXT(".cs")) &&
|
||||
!path.EndsWith(TEXT(".cpp")) &&
|
||||
!path.EndsWith(TEXT(".c")) &&
|
||||
!path.EndsWith(TEXT(".hpp")) &&
|
||||
!path.EndsWith(TEXT(".h"))) ||
|
||||
path.EndsWith(TEXT(".Gen.cs")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScopeLock scopeLock(_locker);
|
||||
_lastSourceCodeEdited = DateTime::Now();
|
||||
|
||||
@@ -736,6 +736,36 @@ public:
|
||||
/// <returns>True during the frame the user releases the button</returns>
|
||||
API_FUNCTION() bool GetMouseButtonUp(MouseButton button) const;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Start dragging the window.
|
||||
/// </summary>
|
||||
/// <param name="offset">The position offset for drag from top-left corner.</param>
|
||||
API_FUNCTION() virtual void StartDragging(const Float2& offset)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends dragging the window.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void StopDragging()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create docking hint points for snapping the draggable window.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void CreateDockHints()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove docking hint points.
|
||||
/// </summary>
|
||||
API_FUNCTION() virtual void RemoveDockHints()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void OnShow();
|
||||
void OnResize(int32 width, int32 height);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include <wayland/xdg-shell.h>
|
||||
#include <wayland/xdg-shell.h>
|
||||
#if PLATFORM_SDL && PLATFORM_LINUX
|
||||
|
||||
#include "Engine/Platform/Platform.h"
|
||||
@@ -10,6 +12,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Engine/CommandLine.h"
|
||||
#include "Engine/Platform/WindowsManager.h"
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
@@ -17,20 +20,32 @@
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <SDL3/SDL_system.h>
|
||||
#include <SDL3/SDL_hints.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
|
||||
#include <wayland/xdg-toplevel-drag-v1.h>
|
||||
|
||||
// Wayland
|
||||
void WaylandRegistryGlobal(void* data, wl_registry *registry, uint32 id, const char* interface, uint32 version);
|
||||
void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id);
|
||||
|
||||
Dictionary<wl_surface*, SDLWindow*> SurfaceToWindowMap;
|
||||
uint32 LastPointerSerial = 0;
|
||||
wl_display* WaylandDisplay = nullptr;
|
||||
wl_registry_listener WaylandRegistryListener = { WaylandRegistryGlobal, WaylandRegistryGlobalRemove };
|
||||
xdg_toplevel_drag_manager_v1* DragManager = nullptr;
|
||||
wl_seat* WaylandSeat = nullptr;
|
||||
wl_data_device_manager* WaylandDataDeviceManager = nullptr;
|
||||
xdg_wm_base* WaylandXdgWmBase = nullptr;
|
||||
|
||||
// X11
|
||||
Delegate<void*> LinuxPlatform::xEventReceived;
|
||||
|
||||
// Missing Wayland features:
|
||||
// - Application icon (xdg-toplevel-icon-v1) https://github.com/libsdl-org/SDL/pull/9584
|
||||
// - Window positioning and position tracking
|
||||
// - Color picker (xdg-desktop-portal?) https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Screenshot.html
|
||||
// -
|
||||
|
||||
namespace
|
||||
{
|
||||
bool UseWayland = false;
|
||||
bool UseXWayland = false;
|
||||
X11::Display* xDisplay = nullptr;
|
||||
X11::XIM IM = nullptr;
|
||||
X11::XIC IC = nullptr;
|
||||
@@ -246,7 +261,7 @@ DragDropEffect Window::DoDragDrop(const StringView& data)
|
||||
if (CommandLine::Options.Headless)
|
||||
return DragDropEffect::None;
|
||||
|
||||
if (UseWayland)
|
||||
if (SDLPlatform::UsesWayland())
|
||||
return DoDragDropWayland(data);
|
||||
else
|
||||
return DoDragDropX11(data);
|
||||
@@ -842,25 +857,60 @@ int X11ErrorHandler(X11::Display* display, X11::XErrorEvent* event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern wl_data_device_listener WaylandDataDeviceListener;
|
||||
wl_data_device* dataDevice;
|
||||
|
||||
bool SDLPlatform::InitPlatform()
|
||||
{
|
||||
if (LinuxPlatform::Init())
|
||||
return true;
|
||||
|
||||
if (!CommandLine::Options.Headless)
|
||||
#if false
|
||||
if (!CommandLine::Options.Headless && strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0)
|
||||
{
|
||||
UseWayland = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0;
|
||||
String waylandDisplay;
|
||||
if (/*!UseWayland &&*/ !Platform::GetEnvironmentVariable(TEXT("WAYLAND_DISPLAY"), waylandDisplay))
|
||||
UseXWayland = waylandDisplay.Length() > 1;
|
||||
WaylandDisplay = (wl_display*)SDL_GetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, nullptr);
|
||||
|
||||
wl_registry* registry = wl_display_get_registry(WaylandDisplay);
|
||||
wl_registry_add_listener(registry, &WaylandRegistryListener, nullptr);
|
||||
|
||||
wl_display_roundtrip(WaylandDisplay);
|
||||
}
|
||||
#else
|
||||
bool waylandRequested = !CommandLine::Options.X11 || CommandLine::Options.Wayland;
|
||||
if (!CommandLine::Options.Headless && waylandRequested)
|
||||
{
|
||||
// Ignore in X11 session
|
||||
String waylandDisplayEnv;
|
||||
if (!GetEnvironmentVariable(String("WAYLAND_DISPLAY"), waylandDisplayEnv))
|
||||
{
|
||||
WaylandDisplay = wl_display_connect(nullptr);
|
||||
if (WaylandDisplay != nullptr)
|
||||
{
|
||||
// We need to manage the wl_display and create the wl_data_device
|
||||
// before SDL so we can receive drag-and-drop related events from compositor.
|
||||
|
||||
SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, WaylandDisplay);
|
||||
|
||||
wl_registry* registry = wl_display_get_registry(WaylandDisplay);
|
||||
wl_registry_add_listener(registry, &WaylandRegistryListener, nullptr);
|
||||
|
||||
wl_display_roundtrip(WaylandDisplay);
|
||||
|
||||
dataDevice = wl_data_device_manager_get_data_device(WaylandDataDeviceManager, WaylandSeat);
|
||||
wl_data_device_add_listener(dataDevice, &WaylandDataDeviceListener, nullptr);
|
||||
|
||||
wl_display_roundtrip(WaylandDisplay);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::InitPlatformX11(void* display)
|
||||
{
|
||||
if (xDisplay || UseWayland)
|
||||
if (xDisplay || WaylandDisplay)
|
||||
return false;
|
||||
|
||||
// The Display instance must be the same one SDL uses internally
|
||||
@@ -909,6 +959,10 @@ bool SDLPlatform::InitPlatformX11(void* display)
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::Exit()
|
||||
{
|
||||
}
|
||||
|
||||
void* SDLPlatform::GetXDisplay()
|
||||
{
|
||||
return xDisplay;
|
||||
@@ -921,17 +975,179 @@ void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return UseWayland;
|
||||
return WaylandDisplay != nullptr;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesXWayland()
|
||||
{
|
||||
return UseXWayland;
|
||||
static bool foundWaylandDisplay = []
|
||||
{
|
||||
String waylandDisplay;
|
||||
return !GetEnvironmentVariable(TEXT("WAYLAND_DISPLAY"), waylandDisplay) && waylandDisplay.Length() > 0;
|
||||
}();
|
||||
return xDisplay != nullptr && foundWaylandDisplay;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return !UseWayland;
|
||||
return xDisplay != nullptr;
|
||||
}
|
||||
|
||||
void WaylandPointer_Enter(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t serial,
|
||||
struct wl_surface *surface,
|
||||
wl_fixed_t surface_x,
|
||||
wl_fixed_t surface_y)
|
||||
{
|
||||
/*SDLWindow* window;
|
||||
if (!SurfaceToWindowMap.TryGet(surface, window))
|
||||
return;
|
||||
|
||||
LastPointerWindow = window;
|
||||
LastPointerPosition = Int2(surface_x, surface_y);*/
|
||||
//LOG(Info, "WaylandPointerEnter serial:{}", serial);
|
||||
LastPointerSerial = serial;
|
||||
}
|
||||
|
||||
void WaylandPointer_Leave(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t serial,
|
||||
struct wl_surface *surface)
|
||||
{
|
||||
//LastPointerWindow = nullptr;
|
||||
//LOG(Info, "WaylandPointerLeave serial:{}", serial);
|
||||
LastPointerSerial = serial;
|
||||
}
|
||||
|
||||
void WaylandPointer_Motion(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t time,
|
||||
wl_fixed_t surface_x,
|
||||
wl_fixed_t surface_y)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerMotion time:{}", time);
|
||||
//LastPointerPosition = Int2(surface_x, surface_y);
|
||||
}
|
||||
|
||||
void WaylandPointer_Button(void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerButton serial:{}", serial);
|
||||
|
||||
// HACK: We store the serial for upcoming drag-and-drop action even though we are
|
||||
// not really performing the action during this specific button press event.
|
||||
// SDL receives the same event which actually starts the DnD process.
|
||||
LastPointerSerial = serial;
|
||||
}
|
||||
|
||||
void WaylandPointer_Axis(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t time,
|
||||
uint32_t axis,
|
||||
wl_fixed_t value)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxis time:{}", time);
|
||||
}
|
||||
|
||||
void WaylandPointer_Frame(void *data,
|
||||
struct wl_pointer *wl_pointer)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerFrame");
|
||||
}
|
||||
|
||||
void WaylandPointer_AxisSource(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t axis_source)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxisSource");
|
||||
}
|
||||
|
||||
void WaylandPointer_AxisStop(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t time,
|
||||
uint32_t axis)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxisStop time:{}", time);
|
||||
}
|
||||
|
||||
void WaylandPointer_AxisDiscrete(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t axis,
|
||||
int32_t discrete)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxisDiscrete");
|
||||
}
|
||||
|
||||
void WaylandPointer_AxisValue120(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t axis,
|
||||
int32_t value120)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxisValue120");
|
||||
}
|
||||
|
||||
void WaylandPointer_AxisRelativeDirection(void *data,
|
||||
struct wl_pointer *wl_pointer,
|
||||
uint32_t axis,
|
||||
uint32_t direction)
|
||||
{
|
||||
//LOG(Info, "WaylandPointerAxisRelativeDirection");
|
||||
}
|
||||
|
||||
|
||||
wl_pointer_listener WaylandPointerListener =
|
||||
{
|
||||
WaylandPointer_Enter,
|
||||
WaylandPointer_Leave,
|
||||
WaylandPointer_Motion,
|
||||
WaylandPointer_Button,
|
||||
WaylandPointer_Axis,
|
||||
WaylandPointer_Frame,
|
||||
WaylandPointer_AxisSource,
|
||||
WaylandPointer_AxisStop,
|
||||
WaylandPointer_AxisDiscrete,
|
||||
WaylandPointer_AxisValue120,
|
||||
WaylandPointer_AxisRelativeDirection
|
||||
};
|
||||
|
||||
wl_pointer* WaylandPointer = nullptr;
|
||||
|
||||
void SeatCapabilities(void* data, wl_seat* seat, uint32 capabilities)
|
||||
{
|
||||
if ((capabilities & wl_seat_capability::WL_SEAT_CAPABILITY_POINTER) != 0)
|
||||
{
|
||||
WaylandPointer = wl_seat_get_pointer(seat);
|
||||
wl_pointer_add_listener(WaylandPointer, &WaylandPointerListener, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void SeatName(void* data, wl_seat* seat, const char* name)
|
||||
{
|
||||
}
|
||||
|
||||
wl_seat_listener SeatListener = { SeatCapabilities, SeatName };
|
||||
|
||||
void WaylandRegistryGlobal(void* data, wl_registry *registry, uint32 id, const char* interface, uint32 version)
|
||||
{
|
||||
StringAnsi interfaceStr(interface);
|
||||
LOG(Info, "WaylandRegistryGlobal id: {}, interface: {}", id, String(interface));
|
||||
if (interfaceStr == "xdg_toplevel_drag_manager_v1")
|
||||
DragManager = (xdg_toplevel_drag_manager_v1*)wl_registry_bind(registry, id, &xdg_toplevel_drag_manager_v1_interface, 1U);
|
||||
else if (interfaceStr == "wl_seat")
|
||||
{
|
||||
WaylandSeat = (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, Math::Min(9U, version));
|
||||
wl_seat_add_listener(WaylandSeat, &SeatListener, nullptr);
|
||||
}
|
||||
else if (interfaceStr == "wl_data_device_manager")
|
||||
WaylandDataDeviceManager = (wl_data_device_manager*)wl_registry_bind(registry, id, &wl_data_device_manager_interface, Math::Min(3U, version));
|
||||
else if (interfaceStr == "xdg_wm_base")
|
||||
WaylandXdgWmBase = (xdg_wm_base*)wl_registry_bind(registry, id, &xdg_wm_base_interface, Math::Min(6U, version));
|
||||
|
||||
}
|
||||
|
||||
void WaylandRegistryGlobalRemove(void* data, wl_registry *registry, uint32 id)
|
||||
{
|
||||
LOG(Info, "WaylandRegistryGlobalRemove id:{}", id);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,8 +43,8 @@ bool SDLPlatform::Init()
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "x11", SDL_HINT_OVERRIDE);
|
||||
else if (CommandLine::Options.Wayland)
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
//SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
// If the hint is not present, SDL will prefer more stable X11 driver over Wayland
|
||||
else
|
||||
SDL_SetHintWithPriority(SDL_HINT_VIDEO_DRIVER, "wayland", SDL_HINT_OVERRIDE);
|
||||
#endif
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
@@ -85,6 +85,9 @@ bool SDLPlatform::Init()
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_COMPLETE, false);
|
||||
SDL_SetEventEnabled(SDL_EVENT_DROP_POSITION, false);
|
||||
|
||||
if (InitPlatform())
|
||||
return true;
|
||||
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
|
||||
Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError())));
|
||||
|
||||
@@ -105,8 +108,6 @@ bool SDLPlatform::Init()
|
||||
}
|
||||
SDL_free(locales);
|
||||
|
||||
if (InitPlatform())
|
||||
return true;
|
||||
|
||||
SDLInput::Init();
|
||||
|
||||
@@ -264,7 +265,16 @@ void SDLPlatform::OpenUrl(const StringView& url)
|
||||
Float2 SDLPlatform::GetMousePosition()
|
||||
{
|
||||
Float2 pos;
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
#if PLATFORM_LINUX
|
||||
if (UsesWayland())
|
||||
{
|
||||
// Wayland doesn't support reporting global mouse position.
|
||||
// Use the last known reported position we got from window events.
|
||||
pos = Input::GetMouseScreenPosition();
|
||||
}
|
||||
else
|
||||
SDL_GetGlobalMouseState(&pos.X, &pos.Y);
|
||||
#endif
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
|
||||
// [PlatformBase]
|
||||
static bool Init();
|
||||
static void Exit();
|
||||
static void LogInfo();
|
||||
static void Tick();
|
||||
static void SetHighDpiAwarenessEnabled(bool enable);
|
||||
|
||||
@@ -33,6 +33,18 @@
|
||||
#endif
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include <wayland/xdg-toplevel-drag-v1.h>
|
||||
#include <wayland/xdg-shell.h>
|
||||
|
||||
extern Dictionary<wl_surface*, SDLWindow*> SurfaceToWindowMap;
|
||||
extern SDLWindow* LastPointerWindow;
|
||||
extern Int2 LastPointerPosition;
|
||||
extern uint32 LastPointerSerial;
|
||||
extern xdg_toplevel_drag_manager_v1* DragManager;
|
||||
extern wl_seat* WaylandSeat;
|
||||
extern wl_data_device_manager* WaylandDataDeviceManager;
|
||||
extern xdg_wm_base* WaylandXdgWmBase;
|
||||
#endif
|
||||
|
||||
#define DefaultDPI 96
|
||||
@@ -185,10 +197,12 @@ SDLWindow::SDLWindow(const CreateWindowSettings& settings)
|
||||
// Input focus is not set initially for Wayland windows, assume the window is focused until a focus event is received
|
||||
if (_settings.AllowInput && (SDL_GetWindowFlags(_window) & SDL_WINDOW_INPUT_FOCUS) != 0)
|
||||
_forcedFocus = (flags & SDL_WINDOW_INPUT_FOCUS) != 0;
|
||||
|
||||
auto surface = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
SurfaceToWindowMap.Add(surface, this);
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_DisplayID display = SDL_GetDisplayForWindow(_window);
|
||||
_dpiScale = SDL_GetWindowDisplayScale(_window);
|
||||
_dpi = Math::TruncToInt(_dpiScale * DefaultDPI);
|
||||
|
||||
@@ -241,9 +255,9 @@ void* GetNativeWindowPointer(SDL_Window* window)
|
||||
#if PLATFORM_WINDOWS
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
|
||||
#elif PLATFORM_LINUX
|
||||
windowPtr = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
if (windowPtr == nullptr)
|
||||
windowPtr = (void*)SDL_GetNumberProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
windowPtr = (void*)SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
#elif PLATFORM_MAC
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_ANDROID
|
||||
@@ -293,6 +307,7 @@ SDLWindow::~SDLWindow()
|
||||
if (_window == nullptr)
|
||||
return;
|
||||
|
||||
SurfaceToWindowMap.RemoveValue(this);
|
||||
SDL_StopTextInput(_window);
|
||||
SDL_DestroyWindow(_window);
|
||||
|
||||
@@ -569,12 +584,10 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
// Check if window size has been changed
|
||||
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
|
||||
OnResize(width, height);
|
||||
LOG(Info, "Resized {}x{}", width, height);
|
||||
return;
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
{
|
||||
LOG(Info, "Focus gained {}", GetTitle());
|
||||
#if PLATFORM_LINUX
|
||||
_forcedFocus = false;
|
||||
#endif
|
||||
@@ -590,7 +603,6 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
}
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
{
|
||||
LOG(Info, "Focus lost {}", GetTitle());
|
||||
#if PLATFORM_LINUX
|
||||
_forcedFocus = false;
|
||||
#endif
|
||||
@@ -679,6 +691,17 @@ void SDLWindow::HandleEvent(SDL_Event& event)
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#if PLATFORM_LINUX
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
{
|
||||
if (SDLPlatform::UsesWayland() && _dragOver)
|
||||
{
|
||||
// We are dragging a window, keep the button held down
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -736,6 +759,9 @@ void SDLWindow::Hide()
|
||||
|
||||
SDL_HideWindow(_window);
|
||||
|
||||
if (SDLPlatform::UsesWayland() && _dragOver)
|
||||
StopDragging();
|
||||
|
||||
WindowBase::Hide();
|
||||
}
|
||||
|
||||
@@ -892,15 +918,6 @@ Int2 GetSDLWindowScreenPosition(const SDLWindow* window)
|
||||
|
||||
void SetSDLWindowScreenPosition(const SDLWindow* window, const int x, const int y)
|
||||
{
|
||||
#if PLATFORM_LINUX
|
||||
/*if (SDLPlatform::UsesWayland())
|
||||
{
|
||||
int oldX, oldY;
|
||||
SDL_GetWindowPosition(window->GetSDLWindow(), &oldX, &oldY);
|
||||
if (x == oldX && y == oldY)
|
||||
return;
|
||||
}*/
|
||||
#endif
|
||||
Int2 relativePosition(x, y);
|
||||
GetRelativeWindowOffset(window->GetSettings().Type, window->GetSettings().Parent, relativePosition);
|
||||
SDL_SetWindowPosition(window->GetSDLWindow(), relativePosition.X, relativePosition.Y);
|
||||
@@ -1000,7 +1017,8 @@ float SDLWindow::GetOpacity() const
|
||||
|
||||
void SDLWindow::SetOpacity(const float opacity)
|
||||
{
|
||||
SDL_SetWindowOpacity(_window, opacity);
|
||||
if (!SDL_SetWindowOpacity(_window, opacity))
|
||||
LOG(Warning, "SDL_SetWindowOpacity failed: {0}", String(SDL_GetError()));
|
||||
}
|
||||
|
||||
void SDLWindow::Focus()
|
||||
@@ -1189,5 +1207,331 @@ void SDLWindow::UpdateCursor()
|
||||
SDL_SetCursor(Cursors[index]);
|
||||
}
|
||||
|
||||
wl_data_offer* WaylandDataOffer = nullptr; // The last accepted offer
|
||||
uint32 WaylandDataOfferSerial = 0; // The last accepted serial for offer
|
||||
StringAnsi WaylandDataOfferMimeType;
|
||||
SDLWindow* DragTargetWindow = nullptr;
|
||||
Float2 DragTargetPosition;
|
||||
|
||||
void WaylandDataOffer_Offer(void* data, wl_data_offer* offer, const char *mime_type)
|
||||
{
|
||||
// We are being offered these types of data
|
||||
//LOG(Info, "WaylandDataOffer_Offer: {}", String(mime_type));
|
||||
|
||||
//if (WaylandDataOffer == nullptr)
|
||||
// return;
|
||||
|
||||
if (StringAnsi(mime_type) == "x.flaxengine.window.snap")
|
||||
WaylandDataOfferMimeType = StringAnsi(mime_type);
|
||||
// wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, mime_type);
|
||||
}
|
||||
|
||||
void WaylandDataOffer_SourceActions(void *data,
|
||||
struct wl_data_offer *wl_data_offer,
|
||||
uint32_t source_actions)
|
||||
{
|
||||
//
|
||||
//LOG(Info, "WaylandDataOffer_SourceActions: {}", source_actions);
|
||||
}
|
||||
|
||||
void WaylandDataOffer_Action(void *data,
|
||||
struct wl_data_offer *wl_data_offer,
|
||||
uint32_t dnd_action)
|
||||
{
|
||||
// DnD: This action will be performed if dropped
|
||||
//LOG(Info, "WaylandDataOffer_Action: {}", dnd_action);
|
||||
}
|
||||
|
||||
wl_data_offer* WaylandDataSelectionOffer = nullptr;
|
||||
wl_data_offer_listener WaylandDataOfferListener = { WaylandDataOffer_Offer, WaylandDataOffer_SourceActions, WaylandDataOffer_Action};
|
||||
|
||||
void WaylandDataDevice_DataOffer(void *data, wl_data_device *wl_data_device, wl_data_offer *id)
|
||||
{
|
||||
//LOG(Info, "WaylandDataDevice_DataOffer: {}", (uint64)id);
|
||||
int ret = wl_data_offer_add_listener(id, &WaylandDataOfferListener, nullptr);
|
||||
if (ret != 0)
|
||||
LOG(Error, "wl_data_offer_add_listener failed");
|
||||
}
|
||||
|
||||
void WaylandDataDevice_Enter(void *data, wl_data_device *wl_data_device, uint32 serial, wl_surface *surface, wl_fixed_t x, wl_fixed_t y, wl_data_offer *id)
|
||||
{
|
||||
// DnD: The cursor entered a target surface
|
||||
LOG(Info, "WaylandDataDevice_Enter serial: {}, surface: {}, pos: {}x{}, id: {}", serial, (uint64)surface, wl_fixed_to_double(x), wl_fixed_to_double(y), (uint64)id);
|
||||
WaylandDataOffer = id;
|
||||
WaylandDataOfferSerial = serial;
|
||||
|
||||
DragTargetPosition = Float2(MAX_float, MAX_float);
|
||||
SDLWindow* sourceWindow = (SDLWindow*)data;
|
||||
if (!SurfaceToWindowMap.TryGet(surface, DragTargetWindow))
|
||||
DragTargetWindow = nullptr;
|
||||
if (DragTargetWindow != nullptr)
|
||||
DragTargetWindow = DragTargetWindow;
|
||||
if (/*SurfaceToWindowMap.TryGet(surface, DragTargetWindow) && */DragTargetWindow != sourceWindow)
|
||||
{
|
||||
// Inform that we support the following action at this given point
|
||||
wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
|
||||
|
||||
if (WaylandDataOfferMimeType == "x.flaxengine.window.snap")
|
||||
wl_data_offer_accept(WaylandDataOffer, WaylandDataOfferSerial, "x.flaxengine.window.snap");
|
||||
}
|
||||
else
|
||||
{
|
||||
wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDataDevice_Leave(void *data, wl_data_device *wl_data_device)
|
||||
{
|
||||
// DnD: The cursor left the surface area
|
||||
// id from enter must be destroyed here
|
||||
LOG(Info, "WaylandDataDevice_Leave");
|
||||
|
||||
if (WaylandDataOffer != nullptr)
|
||||
wl_data_offer_destroy(WaylandDataOffer);
|
||||
WaylandDataOffer = nullptr;
|
||||
WaylandDataOfferSerial = 0;
|
||||
WaylandDataOfferMimeType = StringAnsi::Empty;
|
||||
}
|
||||
|
||||
void WaylandDataDevice_Motion(void *data, wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y)
|
||||
{
|
||||
// DnD: The cursor moves along the surface
|
||||
Float2 dragPosition(wl_fixed_to_double(x), wl_fixed_to_double(y));
|
||||
//LOG(Info, "WaylandDataDevice_Motion {},{}", (int)dragPosition.X, (int)dragPosition.Y);
|
||||
|
||||
|
||||
if (DragTargetWindow != nullptr)
|
||||
{
|
||||
Float2 mousePos = dragPosition * DragTargetWindow->GetDpiScale();
|
||||
mousePos = Float2::Floor(mousePos);
|
||||
if (DragTargetPosition != mousePos)
|
||||
{
|
||||
//LOG(Info, "{}: {}", time, mousePos);
|
||||
Input::Mouse->OnMouseMove(mousePos, DragTargetWindow);
|
||||
DragTargetPosition = mousePos;
|
||||
}
|
||||
}
|
||||
//SDLWindow* targetWindow;
|
||||
//if (SurfaceToWindowMap.TryGet(surface, targetWindow) && targetWindow == surfaceWindow)
|
||||
}
|
||||
|
||||
void WaylandDataDevice_Drop(void *data, wl_data_device *wl_data_device)
|
||||
{
|
||||
// DnD: The drop is accepted
|
||||
LOG(Info, "WaylandDataDevice_Drop");
|
||||
|
||||
/*int fds[2];
|
||||
pipe(fds);
|
||||
wl_data_offer_receive(offer, "text/plain", fds[1]);
|
||||
close(fds[1]);
|
||||
|
||||
// TODO: do something with fds[0]
|
||||
close(fds[0]);*/
|
||||
|
||||
wl_data_offer_finish(WaylandDataOffer);
|
||||
wl_data_offer_destroy(WaylandDataOffer);
|
||||
WaylandDataOffer = nullptr;
|
||||
}
|
||||
|
||||
void WaylandDataDevice_Selection(void *data, wl_data_device *wl_data_device, wl_data_offer *id)
|
||||
{
|
||||
// Clipboard: We can read the clipboard content
|
||||
/*
|
||||
int fds[2];
|
||||
pipe(fds);
|
||||
wl_data_offer_receive(offer, "text/plain", fds[1]);
|
||||
close(fds[1]);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
while (true)
|
||||
{
|
||||
char buf[1024];
|
||||
ssize_t n = read(fds[0], buf, sizeof(buf));
|
||||
if (n <= 0)
|
||||
break;
|
||||
//fwrite(buf, 1, n, stdout);
|
||||
}
|
||||
close(fds[0]);
|
||||
wl_data_offer_destroy(offer);
|
||||
*/
|
||||
|
||||
|
||||
LOG(Info, "WaylandDataDevice_Selection: {}", (uint64)id);
|
||||
|
||||
if (WaylandDataSelectionOffer != nullptr)
|
||||
wl_data_offer_destroy(WaylandDataSelectionOffer);
|
||||
WaylandDataSelectionOffer = id;
|
||||
}
|
||||
|
||||
wl_data_device_listener WaylandDataDeviceListener =
|
||||
{
|
||||
WaylandDataDevice_DataOffer,
|
||||
WaylandDataDevice_Enter,
|
||||
WaylandDataDevice_Leave,
|
||||
WaylandDataDevice_Motion,
|
||||
WaylandDataDevice_Drop,
|
||||
WaylandDataDevice_Selection
|
||||
};
|
||||
|
||||
xdg_toplevel_drag_v1* toplevelDrag = nullptr;
|
||||
|
||||
void WaylandDataSource_Target(void *data,
|
||||
struct wl_data_source *wl_data_source,
|
||||
const char *mime_type)
|
||||
{
|
||||
// The destination accepts the following types, or null if nothing
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
//LOG(Info, "WaylandDataSource_Target mime: {}", String(mime_type));
|
||||
}
|
||||
|
||||
void WaylandDataSource_Send(void *data,
|
||||
struct wl_data_source *wl_data_source,
|
||||
const char *mime_type,
|
||||
int32_t fd)
|
||||
{
|
||||
// Clipboard: The other end has accepted the data?
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
//LOG(Info, "WaylandDataSource_Send mime: {}", String(mime_type));
|
||||
|
||||
// write stuff to the file descriptor
|
||||
//write(fd, text, strlen(text));
|
||||
//close(fd);
|
||||
}
|
||||
|
||||
void WaylandDataSource_Cancelled(void* data, wl_data_source *source)
|
||||
{
|
||||
// Clipboard: other application has replaced the content in clipboard
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
LOG(Info, "WaylandDataSource_Cancelled");
|
||||
//wl_data_source_destroy(source);
|
||||
|
||||
/*if (DragTargetWindow != nullptr)
|
||||
{
|
||||
Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow);
|
||||
}
|
||||
else*/ if (window != nullptr)
|
||||
{
|
||||
Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDataSource_DnDDropPerformed(void *data,
|
||||
struct wl_data_source *wl_data_source)
|
||||
{
|
||||
// The destination is being asked to begin DnD, asking confirmation with ASK actionh
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
LOG(Info, "WaylandDataSource_DnDDropPerformed");
|
||||
}
|
||||
|
||||
void WaylandDataSource_DnDFinished(void *data,
|
||||
struct wl_data_source *wl_data_source)
|
||||
{
|
||||
// The destination has finally accepted the last given dnd_action
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
LOG(Info, "WaylandDataSource_DnDFinished");
|
||||
|
||||
/*if (DragTargetWindow != nullptr)
|
||||
{
|
||||
Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, DragTargetWindow);
|
||||
}
|
||||
else*/ if (window != nullptr)
|
||||
{
|
||||
Input::Mouse->OnMouseUp(DragTargetPosition, MouseButton::Left, window);
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandDataSource_Action(void *data,
|
||||
struct wl_data_source *wl_data_source,
|
||||
uint32_t dnd_action)
|
||||
{
|
||||
// DnD: The destination may accept the given action if confirmed
|
||||
SDLWindow* window = static_cast<SDLWindow*>(data);
|
||||
//LOG(Info, "WaylandDataSource_Action: {}", String(dnd_action == 0 ? "NONE" : dnd_action == 1 ? "COPY" : dnd_action == 2 ? "MOVE" : dnd_action == 4 ? "ASK" : ""));
|
||||
|
||||
}
|
||||
|
||||
wl_data_source_listener WaylandDataSourceListener =
|
||||
{
|
||||
WaylandDataSource_Target,
|
||||
WaylandDataSource_Send,
|
||||
WaylandDataSource_Cancelled,
|
||||
WaylandDataSource_DnDDropPerformed,
|
||||
WaylandDataSource_DnDFinished,
|
||||
WaylandDataSource_Action
|
||||
};
|
||||
|
||||
extern wl_data_device* dataDevice;
|
||||
wl_data_source* dataSource;
|
||||
|
||||
void SDLWindow::StartDragging(const Float2& offset)
|
||||
{
|
||||
LOG(Info, "StartDragging {}", offset);
|
||||
|
||||
_dragOver = true;
|
||||
|
||||
//wl_display_flush((wl_display*)GetWaylandDisplay());
|
||||
SDL_PumpEvents();
|
||||
|
||||
wl_data_device_set_user_data(dataDevice, this);
|
||||
|
||||
// We offer the following types of things for consumption:
|
||||
dataSource = wl_data_device_manager_create_data_source(WaylandDataDeviceManager);
|
||||
wl_data_source_offer(dataSource, "x.flaxengine.window.snap");
|
||||
wl_data_source_set_actions(dataSource, wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | wl_data_device_manager_dnd_action::WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
|
||||
wl_data_source_add_listener(dataSource, &WaylandDataSourceListener, this);
|
||||
|
||||
toplevelDrag = xdg_toplevel_drag_manager_v1_get_xdg_toplevel_drag(DragManager, dataSource);
|
||||
|
||||
wl_surface* origin = (wl_surface*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, nullptr);
|
||||
wl_surface* icon = nullptr;
|
||||
uint32 id = LastPointerSerial;
|
||||
wl_data_device_start_drag(dataDevice, dataSource, origin, icon, id);
|
||||
|
||||
xdg_toplevel* toplevel = (xdg_toplevel*)SDL_GetPointerProperty(SDL_GetWindowProperties(_window), SDL_PROP_WINDOW_WAYLAND_XDG_TOPLEVEL_POINTER, nullptr);
|
||||
/*if (toplevel == nullptr)
|
||||
{
|
||||
auto asdf = xdg_wm_base_get_xdg_surface(WaylandXdgWmBase, origin);
|
||||
toplevel = xdg_surface_get_toplevel(asdf);
|
||||
}*/
|
||||
Float2 scaledOffset = offset / _dpiScale;
|
||||
xdg_toplevel_drag_v1_attach(toplevelDrag, toplevel, (int32)scaledOffset.X, (int32)scaledOffset.Y);
|
||||
}
|
||||
|
||||
void SDLWindow::StopDragging()
|
||||
{
|
||||
LOG(Info, "StopDragging");
|
||||
|
||||
wl_data_device_set_user_data(dataDevice, nullptr);
|
||||
_dragOver = false;
|
||||
|
||||
if (toplevelDrag != nullptr)
|
||||
{
|
||||
xdg_toplevel_drag_v1_destroy(toplevelDrag);
|
||||
toplevelDrag = nullptr;
|
||||
}
|
||||
|
||||
if (dataSource != nullptr)
|
||||
{
|
||||
wl_data_source_destroy(dataSource);
|
||||
dataSource = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLWindow::CreateDockHints()
|
||||
{
|
||||
LOG(Info, "CreateDockHints");
|
||||
|
||||
|
||||
}
|
||||
|
||||
void SDLWindow::RemoveDockHints()
|
||||
{
|
||||
LOG(Info, "RemoveDockHints");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ public:
|
||||
void StartClippingCursor(const Rectangle& bounds) override;
|
||||
void EndClippingCursor() override;
|
||||
void SetCursor(CursorType type) override;
|
||||
void StartDragging(const Float2& offset) override;
|
||||
void StopDragging() override;
|
||||
void CreateDockHints() override;
|
||||
void RemoveDockHints() override;
|
||||
|
||||
#if USE_EDITOR && PLATFORM_WINDOWS
|
||||
// [IUnknown]
|
||||
|
||||
10
Source/ThirdParty/SDL/SDL3/SDL_asyncio.h
vendored
10
Source/ThirdParty/SDL/SDL3/SDL_asyncio.h
vendored
@@ -117,7 +117,7 @@ extern "C" {
|
||||
* This operates as an opaque handle. One can then request read or write
|
||||
* operations on it.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
* \since This struct is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_AsyncIOFromFile
|
||||
*/
|
||||
@@ -126,7 +126,7 @@ typedef struct SDL_AsyncIO SDL_AsyncIO;
|
||||
/**
|
||||
* Types of asynchronous I/O tasks.
|
||||
*
|
||||
* \since This enum is available since SDL 3.0.0.
|
||||
* \since This enum is available since SDL 3.2.0.
|
||||
*/
|
||||
typedef enum SDL_AsyncIOTaskType
|
||||
{
|
||||
@@ -138,7 +138,7 @@ typedef enum SDL_AsyncIOTaskType
|
||||
/**
|
||||
* Possible outcomes of an asynchronous I/O task.
|
||||
*
|
||||
* \since This enum is available since SDL 3.0.0.
|
||||
* \since This enum is available since SDL 3.2.0.
|
||||
*/
|
||||
typedef enum SDL_AsyncIOResult
|
||||
{
|
||||
@@ -150,7 +150,7 @@ typedef enum SDL_AsyncIOResult
|
||||
/**
|
||||
* Information about a completed asynchronous I/O request.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
* \since This struct is available since SDL 3.2.0.
|
||||
*/
|
||||
typedef struct SDL_AsyncIOOutcome
|
||||
{
|
||||
@@ -172,7 +172,7 @@ typedef struct SDL_AsyncIOOutcome
|
||||
* allowing an app to manage multiple pending tasks in one place, in whatever
|
||||
* order they complete.
|
||||
*
|
||||
* \since This struct is available since SDL 3.0.0.
|
||||
* \since This struct is available since SDL 3.2.0.
|
||||
*
|
||||
* \sa SDL_CreateAsyncIOQueue
|
||||
* \sa SDL_ReadAsyncIO
|
||||
|
||||
22
Source/ThirdParty/SDL/SDL3/SDL_filesystem.h
vendored
22
Source/ThirdParty/SDL/SDL3/SDL_filesystem.h
vendored
@@ -22,7 +22,23 @@
|
||||
/**
|
||||
* # CategoryFilesystem
|
||||
*
|
||||
* SDL Filesystem API.
|
||||
* SDL offers an API for examining and manipulating the system's filesystem.
|
||||
* This covers most things one would need to do with directories, except for
|
||||
* actual file I/O (which is covered by [CategoryIOStream](CategoryIOStream)
|
||||
* and [CategoryAsyncIO](CategoryAsyncIO) instead).
|
||||
*
|
||||
* There are functions to answer necessary path questions:
|
||||
*
|
||||
* - Where is my app's data? SDL_GetBasePath().
|
||||
* - Where can I safely write files? SDL_GetPrefPath().
|
||||
* - Where are paths like Downloads, Desktop, Music? SDL_GetUserFolder().
|
||||
* - What is this thing at this location? SDL_GetPathInfo().
|
||||
* - What items live in this folder? SDL_EnumerateDirectory().
|
||||
* - What items live in this folder by wildcard? SDL_GlobDirectory().
|
||||
* - What is my current working directory? SDL_GetCurrentDirectory().
|
||||
*
|
||||
* SDL also offers functions to manipulate the directory tree: renaming,
|
||||
* removing, copying files.
|
||||
*/
|
||||
|
||||
#ifndef SDL_filesystem_h_
|
||||
@@ -425,10 +441,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo
|
||||
* Enumerate a directory tree, filtered by pattern, and return a list.
|
||||
*
|
||||
* Files are filtered out if they don't match the string in `pattern`, which
|
||||
* may contain wildcard characters '*' (match everything) and '?' (match one
|
||||
* may contain wildcard characters '\*' (match everything) and '?' (match one
|
||||
* character). If pattern is NULL, no filtering is done and all results are
|
||||
* returned. Subdirectories are permitted, and are specified with a path
|
||||
* separator of '/'. Wildcard characters '*' and '?' never match a path
|
||||
* separator of '/'. Wildcard characters '\*' and '?' never match a path
|
||||
* separator.
|
||||
*
|
||||
* `flags` may be set to SDL_GLOB_CASEINSENSITIVE to make the pattern matching
|
||||
|
||||
6
Source/ThirdParty/SDL/SDL3/SDL_gamepad.h
vendored
6
Source/ThirdParty/SDL/SDL3/SDL_gamepad.h
vendored
@@ -63,6 +63,12 @@
|
||||
* By default SDL will try to use the most capable driver available, but you
|
||||
* can tune which OS drivers to use with the various joystick hints in
|
||||
* SDL_hints.h.
|
||||
*
|
||||
* Your application should always support gamepad hotplugging. On some
|
||||
* platforms like Xbox, Steam Deck, etc., this is a requirement for
|
||||
* certification. On other platforms, like macOS and Windows when using
|
||||
* Windows.Gaming.Input, controllers may not be available at startup and will
|
||||
* come in at some point after you've started processing events.
|
||||
*/
|
||||
|
||||
#ifndef SDL_gamepad_h_
|
||||
|
||||
93
Source/ThirdParty/SDL/SDL3/SDL_hints.h
vendored
93
Source/ThirdParty/SDL/SDL3/SDL_hints.h
vendored
@@ -1264,6 +1264,31 @@ extern "C" {
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_DEVICE "SDL_JOYSTICK_DEVICE"
|
||||
|
||||
/**
|
||||
* A variable controlling whether enhanced reports should be used for
|
||||
* controllers when using the HIDAPI driver.
|
||||
*
|
||||
* Enhanced reports allow rumble and effects on Bluetooth PlayStation
|
||||
* controllers and gyro on Nintendo Switch controllers, but break Windows
|
||||
* DirectInput for other applications that don't use SDL.
|
||||
*
|
||||
* Once enhanced reports are enabled, they can't be disabled on PlayStation
|
||||
* controllers without power cycling the controller.
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
*
|
||||
* - "0": enhanced reports are not enabled.
|
||||
* - "1": enhanced reports are enabled. (default)
|
||||
* - "auto": enhanced features are advertised to the application, but SDL
|
||||
* doesn't change the controller report mode unless the application uses
|
||||
* them.
|
||||
*
|
||||
* This hint can be enabled anytime.
|
||||
*
|
||||
* \since This hint is available since SDL 3.1.8.
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_ENHANCED_REPORTS "SDL_JOYSTICK_ENHANCED_REPORTS"
|
||||
|
||||
/**
|
||||
* A variable containing a list of flightstick style controllers.
|
||||
*
|
||||
@@ -1571,32 +1596,6 @@ extern "C" {
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL "SDL_JOYSTICK_HIDAPI_PS4_REPORT_INTERVAL"
|
||||
|
||||
/**
|
||||
* A variable controlling whether extended input reports should be used for
|
||||
* PS4 controllers when using the HIDAPI driver.
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
*
|
||||
* - "0": extended reports are not enabled. (default)
|
||||
* - "1": extended reports are enabled.
|
||||
*
|
||||
* Extended input reports allow rumble on Bluetooth PS4 controllers, but break
|
||||
* DirectInput handling for applications that don't use SDL.
|
||||
*
|
||||
* Once extended reports are enabled, they can not be disabled without power
|
||||
* cycling the controller.
|
||||
*
|
||||
* For compatibility with applications written for versions of SDL prior to
|
||||
* the introduction of PS5 controller support, this value will also control
|
||||
* the state of extended reports on PS5 controllers when the
|
||||
* SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE hint is not explicitly set.
|
||||
*
|
||||
* This hint can be enabled anytime.
|
||||
*
|
||||
* \since This hint is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE "SDL_JOYSTICK_HIDAPI_PS4_RUMBLE"
|
||||
|
||||
/**
|
||||
* A variable controlling whether the HIDAPI driver for PS5 controllers should
|
||||
* be used.
|
||||
@@ -1627,31 +1626,6 @@ extern "C" {
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED "SDL_JOYSTICK_HIDAPI_PS5_PLAYER_LED"
|
||||
|
||||
/**
|
||||
* A variable controlling whether extended input reports should be used for
|
||||
* PS5 controllers when using the HIDAPI driver.
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
*
|
||||
* - "0": extended reports are not enabled. (default)
|
||||
* - "1": extended reports.
|
||||
*
|
||||
* Extended input reports allow rumble on Bluetooth PS5 controllers, but break
|
||||
* DirectInput handling for applications that don't use SDL.
|
||||
*
|
||||
* Once extended reports are enabled, they can not be disabled without power
|
||||
* cycling the controller.
|
||||
*
|
||||
* For compatibility with applications written for versions of SDL prior to
|
||||
* the introduction of PS5 controller support, this value defaults to the
|
||||
* value of SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE.
|
||||
*
|
||||
* This hint can be enabled anytime.
|
||||
*
|
||||
* \since This hint is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE "SDL_JOYSTICK_HIDAPI_PS5_RUMBLE"
|
||||
|
||||
/**
|
||||
* A variable controlling whether the HIDAPI driver for NVIDIA SHIELD
|
||||
* controllers should be used.
|
||||
@@ -2661,12 +2635,25 @@ extern "C" {
|
||||
* Specify the OpenGL library to load.
|
||||
*
|
||||
* This hint should be set before creating an OpenGL window or creating an
|
||||
* OpenGL context.
|
||||
* OpenGL context. If this hint isn't set, SDL will choose a reasonable
|
||||
* default.
|
||||
*
|
||||
* \since This hint is available since SDL 3.1.3.
|
||||
*/
|
||||
#define SDL_HINT_OPENGL_LIBRARY "SDL_OPENGL_LIBRARY"
|
||||
|
||||
/**
|
||||
* Specify the EGL library to load.
|
||||
*
|
||||
* This hint should be set before creating an OpenGL window or creating an
|
||||
* OpenGL context. This hint is only considered if SDL is using EGL to manage
|
||||
* OpenGL contexts. If this hint isn't set, SDL will choose a reasonable
|
||||
* default.
|
||||
*
|
||||
* \since This hint is available since SDL 3.2.0.
|
||||
*/
|
||||
#define SDL_HINT_EGL_LIBRARY "SDL_EGL_LIBRARY"
|
||||
|
||||
/**
|
||||
* A variable controlling what driver to use for OpenGL ES contexts.
|
||||
*
|
||||
@@ -3497,6 +3484,8 @@ extern "C" {
|
||||
*
|
||||
* - Rounding errors can result with odd window sizes and/or desktop scales,
|
||||
* which can cause the window contents to appear slightly blurry.
|
||||
* - Positioning the window may be imprecise due to unit conversions and
|
||||
* rounding.
|
||||
* - The window may be unusably small on scaled desktops.
|
||||
* - The window may jump in size when moving between displays of different
|
||||
* scale factors.
|
||||
|
||||
2
Source/ThirdParty/SDL/SDL3/SDL_iostream.h
vendored
2
Source/ThirdParty/SDL/SDL3/SDL_iostream.h
vendored
@@ -25,7 +25,7 @@
|
||||
* # CategoryIOStream
|
||||
*
|
||||
* SDL provides an abstract interface for reading and writing data streams. It
|
||||
* offers implementations for files, memory, etc, and the app can provideo
|
||||
* offers implementations for files, memory, etc, and the app can provide
|
||||
* their own implementations, too.
|
||||
*
|
||||
* SDL_IOStream is not related to the standard C++ iostream class, other than
|
||||
|
||||
2
Source/ThirdParty/SDL/SDL3/SDL_keyboard.h
vendored
2
Source/ThirdParty/SDL/SDL3/SDL_keyboard.h
vendored
@@ -318,6 +318,8 @@ extern SDL_DECLSPEC SDL_Scancode SDLCALL SDL_GetScancodeFromName(const char *nam
|
||||
*
|
||||
* If the key doesn't have a name, this function returns an empty string ("").
|
||||
*
|
||||
* Letters will be presented in their uppercase form, if applicable.
|
||||
*
|
||||
* \param key the desired SDL_Keycode to query.
|
||||
* \returns a UTF-8 encoded string of the key name.
|
||||
*
|
||||
|
||||
4
Source/ThirdParty/SDL/SDL3/SDL_metal.h
vendored
4
Source/ThirdParty/SDL/SDL3/SDL_metal.h
vendored
@@ -23,6 +23,10 @@
|
||||
* # CategoryMetal
|
||||
*
|
||||
* Functions to creating Metal layers and views on SDL windows.
|
||||
*
|
||||
* This provides some platform-specific glue for Apple platforms. Most macOS
|
||||
* and iOS apps can use SDL without these functions, but this API they can be
|
||||
* useful for specific OS-level integration tasks.
|
||||
*/
|
||||
|
||||
#ifndef SDL_metal_h_
|
||||
|
||||
4
Source/ThirdParty/SDL/SDL3/SDL_oldnames.h
vendored
4
Source/ThirdParty/SDL/SDL3/SDL_oldnames.h
vendored
@@ -307,6 +307,8 @@
|
||||
#define SDL_HINT_DIRECTINPUT_ENABLED SDL_HINT_JOYSTICK_DIRECTINPUT
|
||||
#define SDL_HINT_GDK_TEXTINPUT_DEFAULT SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT
|
||||
#define SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE SDL_HINT_JOYSTICK_ENHANCED_REPORTS
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE SDL_HINT_JOYSTICK_ENHANCED_REPORTS
|
||||
#define SDL_HINT_LINUX_DIGITAL_HATS SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS
|
||||
#define SDL_HINT_LINUX_HAT_DEADZONES SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES
|
||||
#define SDL_HINT_LINUX_JOYSTICK_CLASSIC SDL_HINT_JOYSTICK_LINUX_CLASSIC
|
||||
@@ -949,6 +951,8 @@
|
||||
#define SDL_HINT_DIRECTINPUT_ENABLED SDL_HINT_DIRECTINPUT_ENABLED_renamed_SDL_HINT_JOYSTICK_DIRECTINPUT
|
||||
#define SDL_HINT_GDK_TEXTINPUT_DEFAULT SDL_HINT_GDK_TEXTINPUT_DEFAULT_renamed_SDL_HINT_GDK_TEXTINPUT_DEFAULT_TEXT
|
||||
#define SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE_renamed_SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE_RUMBLE_BRAKE
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE_renamed_SDL_HINT_JOYSTICK_ENHANCED_REPORTS
|
||||
#define SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE_renamed_SDL_HINT_JOYSTICK_ENHANCED_REPORTS
|
||||
#define SDL_HINT_LINUX_DIGITAL_HATS SDL_HINT_LINUX_DIGITAL_HATS_renamed_SDL_HINT_JOYSTICK_LINUX_DIGITAL_HATS
|
||||
#define SDL_HINT_LINUX_HAT_DEADZONES SDL_HINT_LINUX_HAT_DEADZONES_renamed_SDL_HINT_JOYSTICK_LINUX_HAT_DEADZONES
|
||||
#define SDL_HINT_LINUX_JOYSTICK_CLASSIC SDL_HINT_LINUX_JOYSTICK_CLASSIC_renamed_SDL_HINT_JOYSTICK_LINUX_CLASSIC
|
||||
|
||||
10
Source/ThirdParty/SDL/SDL3/SDL_render.h
vendored
10
Source/ThirdParty/SDL/SDL3/SDL_render.h
vendored
@@ -1498,10 +1498,18 @@ extern SDL_DECLSPEC bool SDLCALL SDL_RenderCoordinatesToWindow(SDL_Renderer *ren
|
||||
* - The scale (SDL_SetRenderScale)
|
||||
* - The viewport (SDL_SetRenderViewport)
|
||||
*
|
||||
* Various event types are converted with this function: mouse, touch, pen,
|
||||
* etc.
|
||||
*
|
||||
* Touch coordinates are converted from normalized coordinates in the window
|
||||
* to non-normalized rendering coordinates.
|
||||
*
|
||||
* Once converted, the coordinates may be outside the rendering area.
|
||||
* Relative mouse coordinates (xrel and yrel event fields) are _also_
|
||||
* converted. Applications that do not want these fields converted should use
|
||||
* SDL_RenderCoordinatesFromWindow() on the specific event fields instead of
|
||||
* converting the entire event structure.
|
||||
*
|
||||
* Once converted, coordinates may be outside the rendering area.
|
||||
*
|
||||
* \param renderer the rendering context.
|
||||
* \param event the event to modify.
|
||||
|
||||
4
Source/ThirdParty/SDL/SDL3/SDL_revision.h
vendored
4
Source/ThirdParty/SDL/SDL3/SDL_revision.h
vendored
@@ -31,9 +31,9 @@
|
||||
/* #undef SDL_VENDOR_INFO */
|
||||
|
||||
#ifdef SDL_VENDOR_INFO
|
||||
#define SDL_REVISION "SDL3-3.1.7-2b1d809 (" SDL_VENDOR_INFO ")"
|
||||
#define SDL_REVISION "SDL3-3.1.7-preview-3.1.6-637-g8ec576ddab (" SDL_VENDOR_INFO ")"
|
||||
#else
|
||||
#define SDL_REVISION "SDL3-3.1.7-2b1d809"
|
||||
#define SDL_REVISION "SDL3-3.1.7-preview-3.1.6-637-g8ec576ddab"
|
||||
#endif
|
||||
|
||||
#endif /* SDL_revision_h_ */
|
||||
|
||||
24
Source/ThirdParty/SDL/SDL3/SDL_stdinc.h
vendored
24
Source/ThirdParty/SDL/SDL3/SDL_stdinc.h
vendored
@@ -22,11 +22,25 @@
|
||||
/**
|
||||
* # CategoryStdinc
|
||||
*
|
||||
* This is a general header that includes C language support. It implements a
|
||||
* subset of the C runtime APIs, but with an `SDL_` prefix. For most common
|
||||
* use cases, these should behave the same way as their C runtime equivalents,
|
||||
* but they may differ in how or whether they handle certain edge cases. When
|
||||
* in doubt, consult the documentation for details.
|
||||
* SDL provides its own implementation of some of the most important C runtime
|
||||
* functions.
|
||||
*
|
||||
* Using these functions allows an app to have access to common C
|
||||
* functionality without depending on a specific C runtime (or a C runtime at
|
||||
* all). More importantly, the SDL implementations work identically across
|
||||
* platforms, so apps can avoid surprises like snprintf() behaving differently
|
||||
* between Windows and Linux builds, or itoa() only existing on some
|
||||
* platforms.
|
||||
*
|
||||
* For many of the most common functions, like SDL_memcpy, SDL might just call
|
||||
* through to the usual C runtime behind the scenes, if it makes sense to do
|
||||
* so (if it's faster and always available/reliable on a given platform),
|
||||
* reducing library size and offering the most optimized option.
|
||||
*
|
||||
* SDL also offers other C-runtime-adjacent functionality in this header that
|
||||
* either isn't, strictly speaking, part of any C runtime standards, like
|
||||
* SDL_crc32() and SDL_reinterpret_cast, etc. It also offers a few better
|
||||
* options, like SDL_strlcpy(), which functions as a safer form of strcpy().
|
||||
*/
|
||||
|
||||
#ifndef SDL_stdinc_h_
|
||||
|
||||
3
Source/ThirdParty/SDL/SDL3/SDL_storage.h
vendored
3
Source/ThirdParty/SDL/SDL3/SDL_storage.h
vendored
@@ -24,7 +24,8 @@
|
||||
*
|
||||
* The storage API is a high-level API designed to abstract away the
|
||||
* portability issues that come up when using something lower-level (in SDL's
|
||||
* case, this sits on top of SDL_filesystem). It is significantly more
|
||||
* case, this sits on top of the [Filesystem](CategoryFilesystem) and
|
||||
* [IOStream](CategoryIOStream) subsystems). It is significantly more
|
||||
* restrictive than a typical filesystem API, for a number of reasons:
|
||||
*
|
||||
* 1. **What to Access:** A common pitfall with existing filesystem APIs is
|
||||
|
||||
10
Source/ThirdParty/SDL/SDL3/SDL_surface.h
vendored
10
Source/ThirdParty/SDL/SDL3/SDL_surface.h
vendored
@@ -1021,12 +1021,14 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FillSurfaceRect(SDL_Surface *dst, const SDL
|
||||
extern SDL_DECLSPEC bool SDLCALL SDL_FillSurfaceRects(SDL_Surface *dst, const SDL_Rect *rects, int count, Uint32 color);
|
||||
|
||||
/**
|
||||
* Performs a fast blit from the source surface to the destination surface.
|
||||
* Performs a fast blit from the source surface to the destination surface
|
||||
* with clipping.
|
||||
*
|
||||
* This assumes that the source and destination rectangles are the same size.
|
||||
* If either `srcrect` or `dstrect` are NULL, the entire surface (`src` or
|
||||
* `dst`) is copied. The final blit rectangles are saved in `srcrect` and
|
||||
* `dstrect` after all clipping is performed.
|
||||
* `dst`) is copied while ensuring clipping to `dst->clip_rect`.
|
||||
*
|
||||
* The final blit rectangles are saved in `srcrect` and `dstrect` after all
|
||||
* clipping is performed.
|
||||
*
|
||||
* The blit function should not be called on a locked surface.
|
||||
*
|
||||
|
||||
8
Source/ThirdParty/SDL/SDL3/SDL_system.h
vendored
8
Source/ThirdParty/SDL/SDL3/SDL_system.h
vendored
@@ -22,7 +22,13 @@
|
||||
/**
|
||||
* # CategorySystem
|
||||
*
|
||||
* Platform-specific SDL API functions.
|
||||
* Platform-specific SDL API functions. These are functions that deal with
|
||||
* needs of specific operating systems, that didn't make sense to offer as
|
||||
* platform-independent, generic APIs.
|
||||
*
|
||||
* Most apps can make do without these functions, but they can be useful for
|
||||
* integrating with other parts of a specific system, adding platform-specific
|
||||
* polish to an app, or solving problems that only affect one target.
|
||||
*/
|
||||
|
||||
#ifndef SDL_system_h_
|
||||
|
||||
6
Source/ThirdParty/SDL/SDL3/SDL_tray.h
vendored
6
Source/ThirdParty/SDL/SDL3/SDL_tray.h
vendored
@@ -228,9 +228,9 @@ extern SDL_DECLSPEC SDL_TrayMenu *SDLCALL SDL_GetTraySubmenu(SDL_TrayEntry *entr
|
||||
* \param menu The menu to get entries from.
|
||||
* \param size An optional pointer to obtain the number of entries in the
|
||||
* menu.
|
||||
* \returns the entries within the given menu. The pointer becomes invalid
|
||||
* when any function that inserts or deletes entries in the menu is
|
||||
* called.
|
||||
* \returns a NULL-terminated list of entries within the given menu. The
|
||||
* pointer becomes invalid when any function that inserts or deletes
|
||||
* entries in the menu is called.
|
||||
*
|
||||
* \since This function is available since SDL 3.2.0.
|
||||
*
|
||||
|
||||
10
Source/ThirdParty/SDL/SDL3/SDL_video.h
vendored
10
Source/ThirdParty/SDL/SDL3/SDL_video.h
vendored
@@ -1228,10 +1228,12 @@ extern SDL_DECLSPEC SDL_Window * SDLCALL SDL_CreatePopupWindow(SDL_Window *paren
|
||||
* - `SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER`: the width of the window
|
||||
* - `SDL_PROP_WINDOW_CREATE_X_NUMBER`: the x position of the window, or
|
||||
* `SDL_WINDOWPOS_CENTERED`, defaults to `SDL_WINDOWPOS_UNDEFINED`. This is
|
||||
* relative to the parent for windows with the "parent" property set.
|
||||
* relative to the parent for windows with the "tooltip" or "menu" property
|
||||
* set.
|
||||
* - `SDL_PROP_WINDOW_CREATE_Y_NUMBER`: the y position of the window, or
|
||||
* `SDL_WINDOWPOS_CENTERED`, defaults to `SDL_WINDOWPOS_UNDEFINED`. This is
|
||||
* relative to the parent for windows with the "parent" property set.
|
||||
* relative to the parent for windows with the "tooltip" or "menu" property
|
||||
* set.
|
||||
*
|
||||
* These are additional supported properties on macOS:
|
||||
*
|
||||
@@ -2786,6 +2788,10 @@ extern SDL_DECLSPEC bool SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOp
|
||||
* Any child windows owned by the window will be recursively destroyed as
|
||||
* well.
|
||||
*
|
||||
* Note that on some platforms, the visible window may not actually be removed
|
||||
* from the screen until the SDL event loop is pumped again, even though the
|
||||
* SDL_Window is no longer valid after this call.
|
||||
*
|
||||
* \param window the window to destroy.
|
||||
*
|
||||
* \threadsafety This function should only be called on the main thread.
|
||||
|
||||
22
Source/ThirdParty/SDL/SDL3/SDL_vulkan.h
vendored
22
Source/ThirdParty/SDL/SDL3/SDL_vulkan.h
vendored
@@ -87,6 +87,13 @@ struct VkAllocationCallbacks;
|
||||
* creating any Vulkan windows. If no Vulkan loader library is loaded, the
|
||||
* default library will be loaded upon creation of the first Vulkan window.
|
||||
*
|
||||
* SDL keeps a counter of how many times this function has been successfully
|
||||
* called, so it is safe to call this function multiple times, so long as it
|
||||
* is eventually paired with an equivalent number of calls to
|
||||
* SDL_Vulkan_UnloadLibrary. The `path` argument is ignored unless there is no
|
||||
* library currently loaded, and and the library isn't actually unloaded until
|
||||
* there have been an equivalent number of calls to SDL_Vulkan_UnloadLibrary.
|
||||
*
|
||||
* It is fairly common for Vulkan applications to link with libvulkan instead
|
||||
* of explicitly loading it at run time. This will work with SDL provided the
|
||||
* application links to a dynamic library and both it and SDL use the same
|
||||
@@ -116,6 +123,8 @@ struct VkAllocationCallbacks;
|
||||
* \returns true on success or false on failure; call SDL_GetError() for more
|
||||
* information.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_Vulkan_GetVkGetInstanceProcAddr
|
||||
@@ -147,6 +156,19 @@ extern SDL_DECLSPEC SDL_FunctionPointer SDLCALL SDL_Vulkan_GetVkGetInstanceProcA
|
||||
/**
|
||||
* Unload the Vulkan library previously loaded by SDL_Vulkan_LoadLibrary().
|
||||
*
|
||||
* SDL keeps a counter of how many times this function has been called, so it
|
||||
* is safe to call this function multiple times, so long as it is paired with
|
||||
* an equivalent number of calls to SDL_Vulkan_LoadLibrary. The library isn't
|
||||
* actually unloaded until there have been an equivalent number of calls to
|
||||
* SDL_Vulkan_UnloadLibrary.
|
||||
*
|
||||
* Once the library has actually been unloaded, if any Vulkan instances
|
||||
* remain, they will likely crash the program. Clean up any existing Vulkan
|
||||
* resources, and destroy appropriate windows, renderers and GPU devices
|
||||
* before calling this function.
|
||||
*
|
||||
* \threadsafety This function is not thread safe.
|
||||
*
|
||||
* \since This function is available since SDL 3.1.3.
|
||||
*
|
||||
* \sa SDL_Vulkan_LoadLibrary
|
||||
|
||||
5
Source/ThirdParty/Wayland/Wayland.Build.cs
vendored
5
Source/ThirdParty/Wayland/Wayland.Build.cs
vendored
@@ -33,5 +33,10 @@ public class Wayland : ThirdPartyModule
|
||||
// Include generated protocol files for dependency
|
||||
options.PublicIncludePaths.Add(Path.Combine(FolderPath, "include"));
|
||||
options.SourceFiles.AddRange(Directory.GetFiles(FolderPath, "*.cpp", SearchOption.TopDirectoryOnly));
|
||||
options.SourceFiles.AddRange(Directory.GetFiles(FolderPath, "*.c", SearchOption.TopDirectoryOnly));
|
||||
|
||||
// TODO: The glue code needs to be generated as C-code: https://gitlab.freedesktop.org/wayland/wayland/-/issues/90
|
||||
//options.CompileEnv.CustomArgs.Add("-x c");
|
||||
//options.LinkEnv.InputLibraries.Add("wayland-client");
|
||||
}
|
||||
}
|
||||
|
||||
2325
Source/ThirdParty/Wayland/include/wayland/xdg-shell.h
vendored
Normal file
2325
Source/ThirdParty/Wayland/include/wayland/xdg-shell.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1383
Source/ThirdParty/Wayland/protocols/xdg-shell.xml
vendored
Normal file
1383
Source/ThirdParty/Wayland/protocols/xdg-shell.xml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
184
Source/ThirdParty/Wayland/xdg-shell.c
vendored
Normal file
184
Source/ThirdParty/Wayland/xdg-shell.c
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
/* Generated by wayland-scanner 1.23.1 */
|
||||
|
||||
/*
|
||||
* Copyright © 2008-2013 Kristian Høgsberg
|
||||
* Copyright © 2013 Rafael Antognolli
|
||||
* Copyright © 2013 Jasper St. Pierre
|
||||
* Copyright © 2010-2013 Intel Corporation
|
||||
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
|
||||
* Copyright © 2015-2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface xdg_popup_interface;
|
||||
extern const struct wl_interface xdg_positioner_interface;
|
||||
extern const struct wl_interface xdg_surface_interface;
|
||||
extern const struct wl_interface xdg_toplevel_interface;
|
||||
|
||||
static const struct wl_interface *xdg_shell_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_surface_interface,
|
||||
&wl_surface_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&xdg_popup_interface,
|
||||
&xdg_surface_interface,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_output_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "create_positioner", "n", xdg_shell_types + 4 },
|
||||
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
|
||||
{ "pong", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_events[] = {
|
||||
{ "ping", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
|
||||
"xdg_wm_base", 6,
|
||||
4, xdg_wm_base_requests,
|
||||
1, xdg_wm_base_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_positioner_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
|
||||
{ "set_anchor", "u", xdg_shell_types + 0 },
|
||||
{ "set_gravity", "u", xdg_shell_types + 0 },
|
||||
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
|
||||
{ "set_offset", "ii", xdg_shell_types + 0 },
|
||||
{ "set_reactive", "3", xdg_shell_types + 0 },
|
||||
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
|
||||
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
|
||||
"xdg_positioner", 6,
|
||||
10, xdg_positioner_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "get_toplevel", "n", xdg_shell_types + 7 },
|
||||
{ "get_popup", "n?oo", xdg_shell_types + 8 },
|
||||
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
|
||||
{ "ack_configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_events[] = {
|
||||
{ "configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
|
||||
"xdg_surface", 6,
|
||||
5, xdg_surface_requests,
|
||||
1, xdg_surface_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_parent", "?o", xdg_shell_types + 11 },
|
||||
{ "set_title", "s", xdg_shell_types + 0 },
|
||||
{ "set_app_id", "s", xdg_shell_types + 0 },
|
||||
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
|
||||
{ "move", "ou", xdg_shell_types + 16 },
|
||||
{ "resize", "ouu", xdg_shell_types + 18 },
|
||||
{ "set_max_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_min_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_maximized", "", xdg_shell_types + 0 },
|
||||
{ "unset_maximized", "", xdg_shell_types + 0 },
|
||||
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
|
||||
{ "unset_fullscreen", "", xdg_shell_types + 0 },
|
||||
{ "set_minimized", "", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_events[] = {
|
||||
{ "configure", "iia", xdg_shell_types + 0 },
|
||||
{ "close", "", xdg_shell_types + 0 },
|
||||
{ "configure_bounds", "4ii", xdg_shell_types + 0 },
|
||||
{ "wm_capabilities", "5a", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
|
||||
"xdg_toplevel", 6,
|
||||
14, xdg_toplevel_requests,
|
||||
4, xdg_toplevel_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "grab", "ou", xdg_shell_types + 22 },
|
||||
{ "reposition", "3ou", xdg_shell_types + 24 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_events[] = {
|
||||
{ "configure", "iiii", xdg_shell_types + 0 },
|
||||
{ "popup_done", "", xdg_shell_types + 0 },
|
||||
{ "repositioned", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
|
||||
"xdg_popup", 6,
|
||||
3, xdg_popup_requests,
|
||||
3, xdg_popup_events,
|
||||
};
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Flax.Build.Bindings
|
||||
var headerFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
|
||||
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
|
||||
{
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".h", StringComparison.OrdinalIgnoreCase))
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".h", StringComparison.OrdinalIgnoreCase) || moduleOptions.SourceFiles[i].EndsWith(".hpp", StringComparison.OrdinalIgnoreCase))
|
||||
headerFiles.Add(moduleOptions.SourceFiles[i]);
|
||||
}
|
||||
if (headerFiles.Count == 0)
|
||||
|
||||
@@ -482,7 +482,7 @@ namespace Flax.Build
|
||||
var cppFiles = new List<string>(moduleOptions.SourceFiles.Count / 2);
|
||||
for (int i = 0; i < moduleOptions.SourceFiles.Count; i++)
|
||||
{
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".cpp", StringComparison.OrdinalIgnoreCase))
|
||||
if (moduleOptions.SourceFiles[i].EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || moduleOptions.SourceFiles[i].EndsWith(".c", StringComparison.OrdinalIgnoreCase))
|
||||
cppFiles.Add(moduleOptions.SourceFiles[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,9 +88,8 @@ namespace Flax.Deps.Dependencies
|
||||
Path.Combine(root, "include", "SDL3"),
|
||||
};
|
||||
|
||||
CloneGitRepoFastSince(root, "https://github.com/libsdl-org/SDL", new DateTime(2025, 01, 01));
|
||||
//CloneGitRepo(root, "https://github.com/Kontrabant/SDL");
|
||||
GitResetToCommit(root, "4797970bfa6d6258bc9e636b6797acdfc1e73c31");
|
||||
CloneGitRepoFastSince(root, "https://github.com/libsdl-org/SDL", new DateTime(2025, 01, 06));
|
||||
GitResetToCommit(root, "8ec576ddabdc7edfd68e7a8a3214e84e4026328d");
|
||||
|
||||
foreach (var platform in options.Platforms)
|
||||
{
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Flax.Deps.Dependencies
|
||||
foreach (var protocolPath in protocolFiles)
|
||||
{
|
||||
var headerFile = Path.ChangeExtension(Path.GetFileName(protocolPath), "h");
|
||||
var glueFile = Path.ChangeExtension(Path.GetFileName(protocolPath), "cpp");
|
||||
var glueFile = Path.ChangeExtension(Path.GetFileName(protocolPath), "c");
|
||||
Utilities.Run("wayland-scanner", $"client-header {protocolPath} include/wayland/{headerFile}", null, dstPath, Utilities.RunOptions.DefaultTool);
|
||||
Utilities.Run("wayland-scanner", $"private-code {protocolPath} {glueFile}", null, dstPath, Utilities.RunOptions.DefaultTool);
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ namespace Flax.Build.Platforms
|
||||
args.Add("-lXcursor");
|
||||
args.Add("-lXinerama");
|
||||
args.Add("-lXfixes");
|
||||
args.Add("-lwayland-client");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -333,23 +333,6 @@ namespace Flax.Build.Platforms
|
||||
{
|
||||
commonArgs.Add("-c");
|
||||
commonArgs.Add("-pipe");
|
||||
commonArgs.Add("-x");
|
||||
commonArgs.Add("c++");
|
||||
|
||||
// C++ version
|
||||
switch (compileEnvironment.CppVersion)
|
||||
{
|
||||
case CppVersion.Cpp14:
|
||||
commonArgs.Add("-std=c++14");
|
||||
break;
|
||||
case CppVersion.Cpp17:
|
||||
case CppVersion.Latest:
|
||||
commonArgs.Add("-std=c++17");
|
||||
break;
|
||||
case CppVersion.Cpp20:
|
||||
commonArgs.Add("-std=c++20");
|
||||
break;
|
||||
}
|
||||
|
||||
commonArgs.Add("-Wdelete-non-virtual-dtor");
|
||||
commonArgs.Add("-fno-math-errno");
|
||||
@@ -407,7 +390,7 @@ namespace Flax.Build.Platforms
|
||||
commonArgs.Add(string.Format("-I\"{0}\"", includePath.Replace('\\', '/')));
|
||||
}
|
||||
|
||||
// Compile all C++ files
|
||||
// Compile all C/C++ files
|
||||
var args = new List<string>();
|
||||
foreach (var sourceFile in sourceFiles)
|
||||
{
|
||||
@@ -417,6 +400,30 @@ namespace Flax.Build.Platforms
|
||||
// Use shared arguments
|
||||
args.Clear();
|
||||
args.AddRange(commonArgs);
|
||||
|
||||
// Language for the file
|
||||
args.Add("-x");
|
||||
if (Path.GetExtension(sourceFile).Equals(".c", StringComparison.OrdinalIgnoreCase))
|
||||
args.Add("c");
|
||||
else
|
||||
{
|
||||
args.Add("c++");
|
||||
|
||||
// C++ version
|
||||
switch (compileEnvironment.CppVersion)
|
||||
{
|
||||
case CppVersion.Cpp14:
|
||||
args.Add("-std=c++14");
|
||||
break;
|
||||
case CppVersion.Cpp17:
|
||||
case CppVersion.Latest:
|
||||
args.Add("-std=c++17");
|
||||
break;
|
||||
case CppVersion.Cpp20:
|
||||
args.Add("-std=c++20");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Object File Name
|
||||
var objFile = Path.Combine(outputPath, sourceFilename + ".o");
|
||||
|
||||
@@ -241,11 +241,11 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
foreach (var file in files)
|
||||
{
|
||||
string fileType;
|
||||
if (file.EndsWith(".h", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".inl", StringComparison.OrdinalIgnoreCase))
|
||||
if (file.EndsWith(".h", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".inl", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".hpp", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fileType = "ClInclude";
|
||||
}
|
||||
else if (file.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".cc", StringComparison.OrdinalIgnoreCase))
|
||||
else if (file.EndsWith(".cpp", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".cc", StringComparison.OrdinalIgnoreCase) || file.EndsWith(".c", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fileType = "ClCompile";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user