diff --git a/.gitattributes b/.gitattributes index 590777f125..7666e06c19 100644 --- a/.gitattributes +++ b/.gitattributes @@ -30,3 +30,10 @@ *.zip binary *.pdf binary *.ttf binary +*.glb binary +*.blend binary +*.blend1 binary +*.ani binary +*.gif binary +*.res binary +*.dds binary diff --git a/.gitignore b/.gitignore index 3545a028a7..e5746e5761 100644 --- a/.gitignore +++ b/.gitignore @@ -490,7 +490,12 @@ fabric.properties .LSOverride # Icon must end with two \r -Icon +# Icon\r\r is a folder that macos creates, but keeping two \r\r characters in .gitignore +# seems to be hard, so we instead wildcard on Icon? and then use a negative pattern to bring +# back most other uses of Icon. +# - Noggin_bops 2024-06-14 +Icon? +![iI]con[_a-zA-Z0-9] # Thumbnails ._* diff --git a/.vscode/launch.json b/.vscode/launch.json index e6adcb4cd4..4db508bfc7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,12 +1,20 @@ { "version": "0.2.0", "configurations": [ + { "name": "C#: OpenTK.Platform.Tests", "type": "dotnet", "request": "launch", "projectPath": "${workspaceFolder}/tests/OpenTK.Platform.Tests/OpenTK.Platform.Tests.csproj", "launchConfigurationId": "TargetFramework=;OpenTK.Platform.Tests" + }, + { + "name": "C#: Bejeweled", + "type": "dotnet", + "request": "launch", + "projectPath": "${workspaceFolder}/tests/Bejeweled/Bejeweled.csproj", + "launchConfigurationId": "TargetFramework=;Bejeweled" } ] } \ No newline at end of file diff --git a/OpenTK.sln b/OpenTK.sln index d89337ccdb..fc05c9dd00 100644 --- a/OpenTK.sln +++ b/OpenTK.sln @@ -99,6 +99,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ANGLETestProject", "tests\A EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "build", "build\build.fsproj", "{49DFA6FD-D4F9-4EAD-BC24-3D585B1E8B94}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bejeweled", "tests\Bejeweled\Bejeweled.csproj", "{C7D52AD4-4D53-4D37-87B5-0864CF94EA45}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -213,6 +215,10 @@ Global {49DFA6FD-D4F9-4EAD-BC24-3D585B1E8B94}.Debug|Any CPU.Build.0 = Debug|Any CPU {49DFA6FD-D4F9-4EAD-BC24-3D585B1E8B94}.Release|Any CPU.ActiveCfg = Release|Any CPU {49DFA6FD-D4F9-4EAD-BC24-3D585B1E8B94}.Release|Any CPU.Build.0 = Release|Any CPU + {C7D52AD4-4D53-4D37-87B5-0864CF94EA45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7D52AD4-4D53-4D37-87B5-0864CF94EA45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7D52AD4-4D53-4D37-87B5-0864CF94EA45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7D52AD4-4D53-4D37-87B5-0864CF94EA45}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -238,6 +244,7 @@ Global {0892F9BC-D689-47BC-80A3-2AF88DCD84BF} = {0B9B71A1-29F7-4CC5-9A9D-9DF122BE8B28} {7888FB56-FCBC-4240-B78F-691481A56AA1} = {0B9B71A1-29F7-4CC5-9A9D-9DF122BE8B28} {49DFA6FD-D4F9-4EAD-BC24-3D585B1E8B94} = {471E0D18-D620-4CBE-A471-F3DB470E6D49} + {C7D52AD4-4D53-4D37-87B5-0864CF94EA45} = {0B9B71A1-29F7-4CC5-9A9D-9DF122BE8B28} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {82DDE6D6-0D55-49B4-9C64-B31B4A6E729F} diff --git a/build/build.fsproj b/build/build.fsproj index adcd134a6a..ad88829b2d 100644 --- a/build/build.fsproj +++ b/build/build.fsproj @@ -26,13 +26,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/props/stylecop.props b/props/stylecop.props index 37097dd719..ae1bf8fd89 100644 --- a/props/stylecop.props +++ b/props/stylecop.props @@ -14,7 +14,7 @@ - + diff --git a/src/OpenAL/OpenTK.Audio.OpenAL/Native/ALBase.cs b/src/OpenAL/OpenTK.Audio.OpenAL/Native/ALBase.cs index dddbf55152..36a834a282 100644 --- a/src/OpenAL/OpenTK.Audio.OpenAL/Native/ALBase.cs +++ b/src/OpenAL/OpenTK.Audio.OpenAL/Native/ALBase.cs @@ -42,6 +42,9 @@ public static void RegisterOpenALResolver() IntPtr ptr = AL.GetProcAddress(name); if (ptr == IntPtr.Zero) { + // FIXME: We want to have a strategy for AOT and trimming here. + // What do we do in contexts where we can't generate functions at runtime...? + // If we can't load the function for whatever reason we dynamically generate a delegate to // give the user an error message that is actually understandable. MethodInfo invoke = typeof(TDelegate).GetMethod("Invoke"); diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFX.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFX.cs index 0c11d7a913..58e23f80a0 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFX.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFX.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.Creative.EFX { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXEffectSlots.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXEffectSlots.cs index ad7f5f19a2..36318c9f9a 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXEffectSlots.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXEffectSlots.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - // ReSharper disable ExplicitCallerInfoArgument namespace OpenTK.OpenAL.Extensions.Creative.EFX { diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXFilters.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXFilters.cs index 75301c986b..daac7bfd8a 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXFilters.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXFilters.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - // ReSharper disable ExplicitCallerInfoArgument namespace OpenTK.OpenAL.Extensions.Creative.EFX { diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXListeners.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXListeners.cs index a80c789acf..279a0004a0 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXListeners.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.EFX/Interfaces/IEFXListeners.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - // ReSharper disable ExplicitCallerInfoArgument namespace OpenTK.OpenAL.Extensions.Creative.EFX { diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.MultiChannelBuffers/Interfaces/IMultiChannelBuffers.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.MultiChannelBuffers/Interfaces/IMultiChannelBuffers.cs index 3988fd814f..586b55c7c7 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.MultiChannelBuffers/Interfaces/IMultiChannelBuffers.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/Creative.MultiChannelBuffers/Interfaces/IMultiChannelBuffers.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.Creative.MultiChannelBuffers { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.ALAWFormat/Interfaces/IALAWFormat.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.ALAWFormat/Interfaces/IALAWFormat.cs index 9cf486850d..81060592f8 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.ALAWFormat/Interfaces/IALAWFormat.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.ALAWFormat/Interfaces/IALAWFormat.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.ALAWFormat { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture.Enumeration/Interfaces/ICaptureEnumerationContextState.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture.Enumeration/Interfaces/ICaptureEnumerationContextState.cs index f11c9051dd..b6235b9e5d 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture.Enumeration/Interfaces/ICaptureEnumerationContextState.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture.Enumeration/Interfaces/ICaptureEnumerationContextState.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - // ReSharper disable ExplicitCallerInfoArgument namespace OpenTK.OpenAL.Extensions.EXT.Capture.Enumeration { diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContext.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContext.cs index 2a4b232412..c6bedff27d 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContext.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContext.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.Capture { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContextState.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContextState.cs index 6978562469..ab52bc2c5f 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContextState.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.Capture/Interfaces/ICaptureContextState.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.Capture { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.IMA4Format/Interfaces/IIMA4Format.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.IMA4Format/Interfaces/IIMA4Format.cs index 8452c00b3f..927c2a69ca 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.IMA4Format/Interfaces/IIMA4Format.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.IMA4Format/Interfaces/IIMA4Format.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Native.Extensions.EXT.IMA4Format { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MP3Format/Interfaces/IMP3Format.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MP3Format/Interfaces/IMP3Format.cs index c1cf76f0ec..3b3a719396 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MP3Format/Interfaces/IMP3Format.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MP3Format/Interfaces/IMP3Format.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.MP3Format { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MULAWFormat/Interfaces/IMULAWFormat.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MULAWFormat/Interfaces/IMULAWFormat.cs index 0c101aff87..19edbf6978 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MULAWFormat/Interfaces/IMULAWFormat.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.MULAWFormat/Interfaces/IMULAWFormat.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.MULAWFormat { /// diff --git a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.VorbisFormat/Interfaces/IVorbisFormat.cs b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.VorbisFormat/Interfaces/IVorbisFormat.cs index bbcd4a6fef..9a0e26a32a 100644 --- a/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.VorbisFormat/Interfaces/IVorbisFormat.cs +++ b/src/OpenAL/OpenTK.OpenAL.Extensions/EXT.VorbisFormat/Interfaces/IVorbisFormat.cs @@ -7,7 +7,6 @@ // of the MIT license. See the LICENSE file for details. // - namespace OpenTK.OpenAL.Extensions.EXT.VorbisFormat { /// diff --git a/src/OpenTK.Compute/OpenCL/CLGL.cs b/src/OpenTK.Compute/OpenCL/CLGL.cs index a6679bd056..e5a01dbdb7 100644 --- a/src/OpenTK.Compute/OpenCL/CLGL.cs +++ b/src/OpenTK.Compute/OpenCL/CLGL.cs @@ -16,22 +16,22 @@ public enum ObjectType : uint ObjectRenderBuffer = 0x2003, /// - /// Introduced in OpenCL 1.2 + /// Introduced in OpenCL 1.2. /// ObjectTexture2DArray = 0x200E, /// - /// Introduced in OpenCL 1.2 + /// Introduced in OpenCL 1.2. /// ObjectTexture1D = 0x200F, /// - /// Introduced in OpenCL 1.2 + /// Introduced in OpenCL 1.2. /// ObjectTexture1DArray = 0x2010, /// - /// Introduced in OpenCL 1.2 + /// Introduced in OpenCL 1.2. /// ObjectTextureBuffer = 0x2011, } @@ -42,7 +42,7 @@ public enum TextureInfo : uint MipmapLevel = 0x2005, /// - /// Introduced in OpenCL 1.2 + /// Introduced in OpenCL 1.2. /// NumSamples = 0x2012, } diff --git a/src/OpenTK.Core/OpenTK.Core.csproj b/src/OpenTK.Core/OpenTK.Core.csproj index 841f4faba5..05414f9725 100644 --- a/src/OpenTK.Core/OpenTK.Core.csproj +++ b/src/OpenTK.Core/OpenTK.Core.csproj @@ -6,6 +6,7 @@ Core shared functionality for the OpenTK library. Library icon.png + true @@ -18,6 +19,5 @@ - diff --git a/src/OpenTK.Core/Platform/Bitmap.cs b/src/OpenTK.Core/Platform/Bitmap.cs index bf63ed0763..9cff3e1a9c 100644 --- a/src/OpenTK.Core/Platform/Bitmap.cs +++ b/src/OpenTK.Core/Platform/Bitmap.cs @@ -11,6 +11,8 @@ namespace OpenTK.Core.Platform /// public class Bitmap { + // FIXME: Add stuff like colorspace info? + /// /// The width of the bitmap in pixels. /// diff --git a/src/OpenTK.Core/Platform/ComponentSet.cs b/src/OpenTK.Core/Platform/ComponentSet.cs deleted file mode 100644 index 8e526ef4a2..0000000000 --- a/src/OpenTK.Core/Platform/ComponentSet.cs +++ /dev/null @@ -1,1033 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using OpenTK.Core.Utility; -using OpenTK.Mathematics; - -#nullable enable - -namespace OpenTK.Core.Platform -{ - /// - /// A set of platform abstraction layer components. - /// - public class ComponentSet : IClipboardComponent, - ICursorComponent, - IDisplayComponent, - IIconComponent, - IKeyboardComponent, - IMouseComponent, - IOpenGLComponent, - ISurfaceComponent, - IWindowComponent, - IShellComponent, - IJoystickComponent - { - // This class is a bunch of boilerplate some of which has been generated by the IDE. - private IClipboardComponent? _clipboardComponent; - private ICursorComponent? _cursorComponent; - private IDisplayComponent? _displayComponent; - private IIconComponent? _iconComponent; - private IKeyboardComponent? _keyboardComponent; - private IMouseComponent? _mouseComponent; - private IOpenGLComponent? _openGLComponent; - private ISurfaceComponent? _surfaceComponent; - private IWindowComponent? _windowComponent; - private IShellComponent? _shellComponent; - private IJoystickComponent? _joystickComponent; - - public IWindowComponent Window => _windowComponent; - - public ISurfaceComponent Surface => _surfaceComponent; - - public IOpenGLComponent OpenGL => _openGLComponent; - - public IDisplayComponent Display => _displayComponent; - - public IShellComponent Shell => _shellComponent; - - public IMouseComponent Mouse => _mouseComponent; - - public IKeyboardComponent Keyboard => _keyboardComponent; - - public ICursorComponent Cursor => _cursorComponent; - - public IIconComponent Icon => _iconComponent; - - public IClipboardComponent Clipboard => _clipboardComponent; - - public IJoystickComponent Joystick => _joystickComponent; - - /// - /// Indicated whether the component set has been initialized. - /// - /// - /// An initialized component set cannot be modified. - /// - public bool Initialized { get; private set; } = false; - - /// - public string Name - { - get - { - HashSet names = new HashSet(); - if (_cursorComponent is not null) - { - names.Add(_cursorComponent.Name); - } - - if (_displayComponent is not null) - { - names.Add(_displayComponent.Name); - } - - if (_iconComponent is not null) - { - names.Add(_iconComponent.Name); - } - - if (_keyboardComponent is not null) - { - names.Add(_keyboardComponent.Name); - } - - if (_mouseComponent is not null) - { - names.Add(_mouseComponent.Name); - } - - if (_openGLComponent is not null) - { - names.Add(_openGLComponent.Name); - } - - if (_surfaceComponent is not null) - { - names.Add(_surfaceComponent.Name); - } - - if (_windowComponent is not null) - { - names.Add(_windowComponent.Name); - } - - if (_shellComponent is not null) - { - names.Add(_shellComponent.Name); - } - - if (_joystickComponent is not null) - { - names.Add(_joystickComponent.Name); - } - - return $"Component Set [{string.Join(", ", names)}]"; - } - } - - /// - public PalComponents Provides => (_cursorComponent is not null ? PalComponents.MouseCursor : 0) | - (_displayComponent is not null ? PalComponents.Display : 0) | - (_iconComponent is not null ? PalComponents.WindowIcon : 0) | - (_keyboardComponent is not null ? PalComponents.KeyboardInput : 0) | - (_mouseComponent is not null ? PalComponents.MouseCursor : 0) | - (_openGLComponent is not null ? PalComponents.OpenGL : 0) | - (_surfaceComponent is not null ? PalComponents.Surface : 0) | - (_windowComponent is not null ? PalComponents.Window : 0) | - (_shellComponent is not null ? PalComponents.Shell : 0) | - (_joystickComponent is not null ? PalComponents.Joystick : 0); - - private ILogger? _logger; - - /// - public ILogger? Logger - { - get => _logger; - set - { - _logger = value; - -#pragma warning disable SA1503 // Braces should not be omitted - - if (_clipboardComponent != null) _clipboardComponent.Logger = _logger; - - if (_cursorComponent != null) _cursorComponent.Logger = _logger; - - if (_displayComponent != null) _displayComponent.Logger = _logger; - - if (_iconComponent != null) _iconComponent.Logger = _logger; - - if (_keyboardComponent != null) _keyboardComponent.Logger = _logger; - - if (_mouseComponent != null) _mouseComponent.Logger = _logger; - - if (_openGLComponent != null) _openGLComponent.Logger = _logger; - - if (_surfaceComponent != null) _surfaceComponent.Logger = _logger; - - if (_windowComponent != null) _windowComponent.Logger = _logger; - - if (_shellComponent != null) _shellComponent.Logger = _logger; - - if (_joystickComponent != null) _joystickComponent.Logger = _logger; - -#pragma warning restore SA1503 // Braces should not be omitted - } - } - - /// - /// Get or set which components are in the set. - /// - /// The component group. - /// Not implemented, yet. - /// The given enum should only contain bit set for get. - /// Raised when the set is modified after initialization. - public IPalComponent? this[PalComponents which] - { - get => which switch - { - PalComponents.ControllerInput => throw new NotImplementedException(), - PalComponents.Display => _displayComponent, - PalComponents.KeyboardInput => _keyboardComponent, - PalComponents.MiceInput => _mouseComponent, - PalComponents.MouseCursor => _cursorComponent, - PalComponents.Surface => _surfaceComponent, - PalComponents.Vulkan => throw new NotImplementedException(), - PalComponents.Window => _windowComponent, - PalComponents.WindowIcon => _iconComponent, - PalComponents.OpenGL => _openGLComponent, - PalComponents.Clipboard => _clipboardComponent, - PalComponents.Shell => _shellComponent, - PalComponents.Joystick => _joystickComponent, - _ => throw new ArgumentException("Components are a bitfield or out of range.", nameof(which)) - }; - set - { - if (Initialized) - { - throw new PalException(this, "Cannot change set after components are initialized."); - } - - if ((which & PalComponents.Display) != 0) - { - _displayComponent = value as IDisplayComponent; - } - if ((which & PalComponents.KeyboardInput) != 0) - { - _keyboardComponent = value as IKeyboardComponent; - } - if ((which & PalComponents.MiceInput) != 0) - { - _mouseComponent = value as IMouseComponent; - } - if ((which & PalComponents.MouseCursor) != 0) - { - _cursorComponent = value as ICursorComponent; - } - if ((which & PalComponents.Surface) != 0) - { - _surfaceComponent = value as ISurfaceComponent; - } - if ((which & PalComponents.Window) != 0) - { - _windowComponent = value as IWindowComponent; - } - if ((which & PalComponents.WindowIcon) != 0) - { - _iconComponent = value as IIconComponent; - } - if ((which & PalComponents.OpenGL) != 0) - { - _openGLComponent = value as IOpenGLComponent; - } - if ((which & PalComponents.Clipboard) != 0) - { - _clipboardComponent = value as IClipboardComponent; - } - if ((which & PalComponents.Shell) != 0) - { - _shellComponent = value as IShellComponent; - } - if ((which & PalComponents.Joystick) != 0) - { - _joystickComponent = value as IJoystickComponent; - } - } - } - - /// - void IPalComponent.Initialize(PalComponents which) - { - if ((which & ~Provides) != 0) - { - throw new PalException(this, $"Platform does not support requested features.") - { - Data = - { - ["Requested"] = which, - ["Supported"] = Provides - } - }; - } - - _cursorComponent?.Initialize(which & PalComponents.MouseCursor); - _displayComponent?.Initialize(which & PalComponents.Display); - _iconComponent?.Initialize(which & PalComponents.WindowIcon); - _keyboardComponent?.Initialize(which & PalComponents.KeyboardInput); - _mouseComponent?.Initialize(which & PalComponents.MiceInput); - _surfaceComponent?.Initialize(which & PalComponents.Surface); - _windowComponent?.Initialize(which & PalComponents.Window); - _openGLComponent?.Initialize(which & PalComponents.OpenGL); - _clipboardComponent?.Initialize(which & PalComponents.Clipboard); - _shellComponent?.Initialize(which & PalComponents.Shell); - - Initialized = true; - } - - IReadOnlyList IClipboardComponent.SupportedFormats => _clipboardComponent!.SupportedFormats; - - ClipboardFormat IClipboardComponent.GetClipboardFormat() - { - return _clipboardComponent!.GetClipboardFormat(); - } - - void IClipboardComponent.SetClipboardText(string text) - { - _clipboardComponent!.SetClipboardText(text); - } - - string? IClipboardComponent.GetClipboardText() - { - return _clipboardComponent!.GetClipboardText(); - } - - AudioData? IClipboardComponent.GetClipboardAudio() - { - return _clipboardComponent!.GetClipboardAudio(); - } - - Bitmap? IClipboardComponent.GetClipboardBitmap() - { - return _clipboardComponent!.GetClipboardBitmap(); - } - - string? IClipboardComponent.GetClipboardHTML() - { - return _clipboardComponent!.GetClipboardHTML(); - } - - List? IClipboardComponent.GetClipboardFiles() - { - return _clipboardComponent!.GetClipboardFiles(); - } - - /// - bool ICursorComponent.CanLoadSystemCursors => _cursorComponent!.CanLoadSystemCursors; - - /// - bool ICursorComponent.CanInspectSystemCursors => _cursorComponent!.CanInspectSystemCursors; - - /// - bool IIconComponent.CanLoadSystemIcons => _iconComponent!.CanLoadSystemIcons; - - /// - bool IWindowComponent.CanSetIcon => _windowComponent!.CanSetIcon; - - /// - bool IWindowComponent.CanGetDisplay => _windowComponent!.CanGetDisplay; - - /// - bool IWindowComponent.CanSetCursor => _windowComponent!.CanSetCursor; - - /// - bool IWindowComponent.CanCaptureCursor => _windowComponent!.CanCaptureCursor; - - /// - IReadOnlyList IWindowComponent.SupportedEvents => _windowComponent!.SupportedEvents; - - /// - IReadOnlyList IWindowComponent.SupportedStyles => _windowComponent!.SupportedStyles; - - /// - IReadOnlyList IWindowComponent.SupportedModes => _windowComponent!.SupportedModes; - - /// - void IWindowComponent.ProcessEvents(bool waitForEvents) - { - _windowComponent!.ProcessEvents(waitForEvents); - } - - /// - WindowHandle IWindowComponent.Create(GraphicsApiHints hints) - { - return _windowComponent!.Create(hints); - } - - /// - void IWindowComponent.Destroy(WindowHandle handle) - { - _windowComponent!.Destroy(handle); - } - - /// - bool IWindowComponent.IsWindowDestroyed(WindowHandle handle) - { - return _windowComponent!.IsWindowDestroyed(handle); - } - - /// - string IWindowComponent.GetTitle(WindowHandle handle) - { - return _windowComponent!.GetTitle(handle); - } - - /// - void IWindowComponent.SetTitle(WindowHandle handle, string title) - { - _windowComponent!.SetTitle(handle, title); - } - - /// - IconHandle? IWindowComponent.GetIcon(WindowHandle handle) - { - return _windowComponent!.GetIcon(handle); - } - - /// - void IWindowComponent.SetIcon(WindowHandle handle, IconHandle icon) - { - _windowComponent!.SetIcon(handle, icon); - } - - /// - void IWindowComponent.GetPosition(WindowHandle handle, out int x, out int y) - { - _windowComponent!.GetPosition(handle, out x, out y); - } - - /// - void IWindowComponent.SetPosition(WindowHandle handle, int x, int y) - { - _windowComponent!.SetPosition(handle, x, y); - } - - /// - void IWindowComponent.GetSize(WindowHandle handle, out int width, out int height) - { - _windowComponent!.GetSize(handle, out width, out height); - } - - /// - void IWindowComponent.SetSize(WindowHandle handle, int width, int height) - { - _windowComponent!.SetSize(handle, width, height); - } - - /// - void IWindowComponent.GetBounds(WindowHandle handle, out int x, out int y, out int width, out int height) - { - _windowComponent!.GetBounds(handle, out x, out y, out width, out height); - } - - /// - void IWindowComponent.SetBounds(WindowHandle handle, int x, int y, int width, int height) - { - _windowComponent!.SetBounds(handle, x, y, width, height); - } - - /// - void IWindowComponent.GetClientPosition(WindowHandle handle, out int x, out int y) - { - _windowComponent!.GetClientPosition(handle, out x, out y); - } - - /// - void IWindowComponent.SetClientPosition(WindowHandle handle, int x, int y) - { - _windowComponent!.SetClientPosition(handle, x, y); - } - - /// - void IWindowComponent.GetClientSize(WindowHandle handle, out int width, out int height) - { - _windowComponent!.GetClientSize(handle, out width, out height); - } - - /// - void IWindowComponent.SetClientSize(WindowHandle handle, int width, int height) - { - _windowComponent!.SetClientSize(handle, width, height); - } - - /// - void IWindowComponent.GetClientBounds(WindowHandle handle, out int x, out int y, out int width, out int height) - { - _windowComponent!.GetClientBounds(handle, out x, out y, out width, out height); - } - - /// - void IWindowComponent.SetClientBounds(WindowHandle handle, int x, int y, int width, int height) - { - _windowComponent!.SetClientBounds(handle, x, y, width, height); - } - - /// - void IWindowComponent.GetMaxClientSize(WindowHandle handle, out int? width, out int? height) - { - _windowComponent!.GetMaxClientSize(handle, out width, out height); - } - - /// - void IWindowComponent.SetMaxClientSize(WindowHandle handle, int? width, int? height) - { - _windowComponent!.SetMaxClientSize(handle, width, height); - } - - /// - void IWindowComponent.GetMinClientSize(WindowHandle handle, out int? width, out int? height) - { - _windowComponent!.GetMinClientSize(handle, out width, out height); - } - - /// - void IWindowComponent.SetMinClientSize(WindowHandle handle, int? width, int? height) - { - _windowComponent!.SetMinClientSize(handle, width, height); - } - - /// - DisplayHandle IWindowComponent.GetDisplay(WindowHandle handle) - { - return _windowComponent!.GetDisplay(handle); - } - - /// - WindowMode IWindowComponent.GetMode(WindowHandle handle) - { - return _windowComponent!.GetMode(handle); - } - - /// - void IWindowComponent.SetMode(WindowHandle handle, WindowMode mode) - { - _windowComponent!.SetMode(handle, mode); - } - - /// - void IWindowComponent.SetFullscreenDisplay(WindowHandle window, DisplayHandle? display) - { - _windowComponent!.SetFullscreenDisplay(window, display); - } - - /// - void IWindowComponent.SetFullscreenDisplay(WindowHandle window, DisplayHandle display, VideoMode videoMode) - { - _windowComponent!.SetFullscreenDisplay(window, display, videoMode); - } - - /// - bool IWindowComponent.GetFullscreenDisplay(WindowHandle window, [NotNullWhen(true)] out DisplayHandle? display) - { - return _windowComponent!.GetFullscreenDisplay(window, out display); - } - - /// - WindowBorderStyle IWindowComponent.GetBorderStyle(WindowHandle handle) - { - return _windowComponent!.GetBorderStyle(handle); - } - - /// - void IWindowComponent.SetBorderStyle(WindowHandle handle, WindowBorderStyle style) - { - _windowComponent!.SetBorderStyle(handle, style); - } - - /// - void IWindowComponent.SetAlwaysOnTop(WindowHandle handle, bool floating) - { - _windowComponent!.SetAlwaysOnTop(handle, floating); - } - - /// - bool IWindowComponent.IsAlwaysOnTop(WindowHandle handle) - { - return _windowComponent!.IsAlwaysOnTop(handle); - } - - /// - void IWindowComponent.SetHitTestCallback(WindowHandle handle, HitTest? test) - { - _windowComponent!.SetHitTestCallback(handle, test); - } - - /// - void IWindowComponent.SetCursor(WindowHandle handle, CursorHandle? cursor) - { - _windowComponent!.SetCursor(handle, cursor); - } - - /// - CursorCaptureMode IWindowComponent.GetCursorCaptureMode(WindowHandle handle) - { - return _windowComponent!.GetCursorCaptureMode(handle); - } - - /// - void IWindowComponent.SetCursorCaptureMode(WindowHandle handle, CursorCaptureMode mode) - { - _windowComponent!.SetCursorCaptureMode(handle, mode); - } - - /// - void IWindowComponent.FocusWindow(WindowHandle handle) - { - _windowComponent!.FocusWindow(handle); - } - - /// - void IWindowComponent.RequestAttention(WindowHandle handle) - { - _windowComponent!.RequestAttention(handle); - } - - /// - void IWindowComponent.ScreenToClient(WindowHandle handle, int x, int y, out int clientX, out int clientY) - { - _windowComponent!.ScreenToClient(handle, x, y, out clientX, out clientY); - } - - /// - void IWindowComponent.ClientToScreen(WindowHandle handle, int clientX, int clientY, out int x, out int y) - { - _windowComponent!.ClientToScreen(handle, clientX, clientY, out x, out y); - } - - /// - IconHandle IIconComponent.Create(SystemIconType systemIcon) - { - return _iconComponent!.Create(systemIcon); - } - - /// - IconHandle IIconComponent.Create(int width, int height, System.ReadOnlySpan data) - { - return _iconComponent!.Create(width, height, data); - } - - /// - void ISurfaceComponent.Destroy(SurfaceHandle handle) - { - _surfaceComponent!.Destroy(handle); - } - - /// - SurfaceType ISurfaceComponent.GetType(SurfaceHandle handle) - { - return _surfaceComponent!.GetType(handle); - } - - /// - DisplayHandle ISurfaceComponent.GetDisplay(SurfaceHandle handle) - { - return _surfaceComponent!.GetDisplay(handle); - } - - /// - void ISurfaceComponent.SetDisplay(SurfaceHandle handle, DisplayHandle display) - { - _surfaceComponent!.SetDisplay(handle, display); - } - - /// - void ISurfaceComponent.GetClientSize(SurfaceHandle handle, out int width, out int height) - { - _surfaceComponent!.GetClientSize(handle, out width, out height); - } - - /// - void IIconComponent.Destroy(IconHandle handle) - { - _iconComponent!.Destroy(handle); - } - - /// - void IIconComponent.GetSize(IconHandle handle, out int width, out int height) - { - _iconComponent!.GetSize(handle, out width, out height); - } - - /// - CursorHandle ICursorComponent.Create(SystemCursorType systemCursor) - { - return _cursorComponent!.Create(systemCursor); - } - - /// - CursorHandle ICursorComponent.Create(int width, int height, ReadOnlySpan image, int hotspotX, int hotspotY) - { - return _cursorComponent!.Create(width, height, image, hotspotX, hotspotY); - } - - /// - CursorHandle ICursorComponent.Create(int width, int height, ReadOnlySpan colorData, ReadOnlySpan maskData, int hotspotX, int hotspotY) - { - return _cursorComponent!.Create(width, height, colorData, maskData, hotspotX, hotspotY); - } - - /// - bool ICursorComponent.IsSystemCursor(CursorHandle handle) - { - return _cursorComponent!.IsSystemCursor(handle); - } - - /// - void ICursorComponent.Destroy(CursorHandle handle) - { - _cursorComponent!.Destroy(handle); - } - - /// - void ICursorComponent.GetSize(CursorHandle handle, out int width, out int height) - { - _cursorComponent!.GetSize(handle, out width, out height); - } - - /// - void ICursorComponent.GetHotspot(CursorHandle handle, out int x, out int y) - { - _cursorComponent!.GetHotspot(handle, out x, out y); - } - - /// - bool IDisplayComponent.CanGetVirtualPosition => _displayComponent!.CanGetVirtualPosition; - - /// - int IDisplayComponent.GetDisplayCount() - { - return _displayComponent!.GetDisplayCount(); - } - - /// - bool IMouseComponent.CanSetMousePosition => _mouseComponent!.CanSetMousePosition; - - /// - void IMouseComponent.GetPosition(out int x, out int y) - { - _mouseComponent!.GetPosition(out x, out y); - } - - /// - void IMouseComponent.SetPosition(int x, int y) - { - _mouseComponent!.SetPosition(x, y); - } - - /// - void IMouseComponent.GetMouseState(out MouseState state) - { - _mouseComponent!.GetMouseState(out state); - } - - /// - DisplayHandle IDisplayComponent.Open(int index) - { - return _displayComponent!.Open(index); - } - - /// - DisplayHandle IDisplayComponent.OpenPrimary() - { - return _displayComponent!.OpenPrimary(); - } - - /// - void IDisplayComponent.Close(DisplayHandle handle) - { - _displayComponent!.Close(handle); - } - - /// - bool IDisplayComponent.IsPrimary(DisplayHandle handle) - { - return _displayComponent!.IsPrimary(handle); - } - - /// - string IDisplayComponent.GetName(DisplayHandle handle) - { - return _displayComponent!.GetName(handle); - } - - /// - void IDisplayComponent.GetVideoMode(DisplayHandle handle, out VideoMode mode) - { - _displayComponent!.GetVideoMode(handle, out mode); - } - - /// - VideoMode[] IDisplayComponent.GetSupportedVideoModes(DisplayHandle handle) - { - return _displayComponent!.GetSupportedVideoModes(handle); - } - - /// - void IDisplayComponent.GetVirtualPosition(DisplayHandle handle, out int x, out int y) - { - _displayComponent!.GetVirtualPosition(handle, out x, out y); - } - - /// - void IDisplayComponent.GetResolution(DisplayHandle handle, out int width, out int height) - { - _displayComponent!.GetResolution(handle, out width, out height); - } - - /// - void IDisplayComponent.GetWorkArea(DisplayHandle handle, out Box2i area) - { - _displayComponent!.GetWorkArea(handle, out area); - } - - /// - void IDisplayComponent.GetRefreshRate(DisplayHandle handle, out float refreshRate) - { - _displayComponent!.GetRefreshRate(handle, out refreshRate); - } - - /// - void IDisplayComponent.GetDisplayScale(DisplayHandle handle, out float scaleX, out float scaleY) - { - _displayComponent!.GetDisplayScale(handle, out scaleX, out scaleY); - } - - /// - bool IKeyboardComponent.SupportsLayouts => _keyboardComponent!.SupportsLayouts; - - /// - bool IKeyboardComponent.SupportsIme => _keyboardComponent!.SupportsIme; - - /// - string IKeyboardComponent.GetActiveKeyboardLayout(WindowHandle? handle) - { - return _keyboardComponent!.GetActiveKeyboardLayout(handle); - } - - /// - string[] IKeyboardComponent.GetAvailableKeyboardLayouts() - { - return _keyboardComponent!.GetAvailableKeyboardLayouts(); - } - - /// - Scancode IKeyboardComponent.GetScancodeFromKey(Key key) - { - return _keyboardComponent!.GetScancodeFromKey(key); - } - - /// - Key IKeyboardComponent.GetKeyFromScancode(Scancode scancode) - { - return _keyboardComponent!.GetKeyFromScancode(scancode); - } - - /// - void IKeyboardComponent.GetKeyboardState(bool[] keyboardState) - { - _keyboardComponent!.GetKeyboardState(keyboardState); - } - - /// - KeyModifier IKeyboardComponent.GetKeyboardModifiers() - { - return _keyboardComponent!.GetKeyboardModifiers(); - } - - /// - void IKeyboardComponent.BeginIme(WindowHandle window) - { - _keyboardComponent!.BeginIme(window); - } - - /// - void IKeyboardComponent.SetImeRectangle(WindowHandle window, int x, int y, int width, int height) - { - _keyboardComponent!.SetImeRectangle(window, x, y, width, height); - } - - /// - void IKeyboardComponent.EndIme(WindowHandle window) - { - _keyboardComponent!.EndIme(window); - } - - /// - bool IOpenGLComponent.CanShareContexts => _openGLComponent!.CanShareContexts; - - /// - bool IOpenGLComponent.CanCreateFromWindow => _openGLComponent!.CanCreateFromWindow; - - /// - bool IOpenGLComponent.CanCreateFromSurface => _openGLComponent!.CanCreateFromSurface; - - /// - OpenGLContextHandle IOpenGLComponent.CreateFromSurface() - { - return _openGLComponent!.CreateFromSurface(); - } - - /// - OpenGLContextHandle IOpenGLComponent.CreateFromWindow(WindowHandle handle) - { - return _openGLComponent!.CreateFromWindow(handle); - } - - /// - void IOpenGLComponent.DestroyContext(OpenGLContextHandle handle) - { - _openGLComponent!.DestroyContext(handle); - } - - /// - IBindingsContext IOpenGLComponent.GetBindingsContext(OpenGLContextHandle handle) - { - return _openGLComponent!.GetBindingsContext(handle); - } - - /// - IntPtr IOpenGLComponent.GetProcedureAddress(OpenGLContextHandle handle, string procedureName) - { - return _openGLComponent!.GetProcedureAddress(handle, procedureName); - } - - /// - OpenGLContextHandle? IOpenGLComponent.GetCurrentContext() - { - return _openGLComponent!.GetCurrentContext(); - } - - /// - bool IOpenGLComponent.SetCurrentContext(OpenGLContextHandle? handle) - { - return _openGLComponent!.SetCurrentContext(handle); - } - - /// - OpenGLContextHandle? IOpenGLComponent.GetSharedContext(OpenGLContextHandle handle) - { - return _openGLComponent!.GetSharedContext(handle); - } - - /// - void IOpenGLComponent.SetSwapInterval(int interval) - { - _openGLComponent!.SetSwapInterval(interval); - } - - /// - int IOpenGLComponent.GetSwapInterval() - { - return _openGLComponent!.GetSwapInterval(); - } - - /// - void IOpenGLComponent.SwapBuffers(OpenGLContextHandle handle) - { - _openGLComponent!.SwapBuffers(handle); - } - - /// - SurfaceHandle ISurfaceComponent.Create() - { - return _surfaceComponent!.Create(); - } - - /// - void IShellComponent.AllowScreenSaver(bool allow) - { - _shellComponent!.AllowScreenSaver(allow); - } - - /// - BatteryStatus IShellComponent.GetBatteryInfo(out BatteryInfo batteryInfo) - { - return _shellComponent!.GetBatteryInfo(out batteryInfo); - } - - /// - ThemeInfo IShellComponent.GetPreferredTheme() - { - return _shellComponent!.GetPreferredTheme(); - } - - /// - SystemMemoryInfo IShellComponent.GetSystemMemoryInformation() - { - return _shellComponent!.GetSystemMemoryInformation(); - } - - /// - float IJoystickComponent.LeftDeadzone => _joystickComponent!.LeftDeadzone; - - /// - float IJoystickComponent.RightDeadzone => _joystickComponent!.RightDeadzone; - - /// - float IJoystickComponent.TriggerThreshold => _joystickComponent!.TriggerThreshold; - - /// - bool IJoystickComponent.IsConnected(int index) - { - return _joystickComponent!.IsConnected(index); - } - - /// - JoystickHandle IJoystickComponent.Open(int index) - { - return _joystickComponent!.Open(index); - } - - /// - void IJoystickComponent.Close(JoystickHandle handle) - { - _joystickComponent!.Close(handle); - } - - /// - Guid IJoystickComponent.GetGuid(JoystickHandle handle) - { - return _joystickComponent!.GetGuid(handle); - } - - /// - string IJoystickComponent.GetName(JoystickHandle handle) - { - return _joystickComponent!.GetName(handle); - } - - /// - float IJoystickComponent.GetAxis(JoystickHandle handle, JoystickAxis axis) - { - return _joystickComponent!.GetAxis(handle, axis); - } - - /// - bool IJoystickComponent.GetButton(JoystickHandle handle, JoystickButton button) - { - return _joystickComponent!.GetButton(handle, button); - } - - /// - bool IJoystickComponent.SetVibration(JoystickHandle handle, float lowFreqIntensity, float highFreqIntensity) - { - return _joystickComponent!.SetVibration(handle, lowFreqIntensity, highFreqIntensity); - } - - /// - bool IJoystickComponent.TryGetBatteryInfo(JoystickHandle handle, out GamepadBatteryInfo batteryInfo) - { - return _joystickComponent!.TryGetBatteryInfo(handle, out batteryInfo); - } - } -} diff --git a/src/OpenTK.Core/Platform/ContextSettings.cs b/src/OpenTK.Core/Platform/ContextSettings.cs index 05a73e0e50..e771f73ea2 100644 --- a/src/OpenTK.Core/Platform/ContextSettings.cs +++ b/src/OpenTK.Core/Platform/ContextSettings.cs @@ -1,81 +1,512 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using OpenTK.Core.Utility; + +#nullable enable namespace OpenTK.Core.Platform { - // FIXME: Some way of relaying the actually chosen values to the user. - /// - /// Settings for creating OpenGL contexts. + /// Delegate used to select appropriate context values to use. /// - public class ContextSettings + /// A list of possible context values. + /// The user requested context values. + /// A logger to use for logging. + /// The index of the context value to use. + public delegate int ContextValueSelector(IReadOnlyList options, ContextValues requested, ILogger? logger); + + // FIXME: Better name. + public struct ContextValues : IEquatable { - /// - /// If double buffering should be enabled. - /// - public bool DoubleBuffer { get; set; } + public ulong ID; - /// - /// If sRGB default framebuffer should be enabled. - /// - public bool sRGBFramebuffer { get; set; } + public int RedBits; + public int GreenBits; + public int BlueBits; + public int AlphaBits; + public int DepthBits; + public int StencilBits; + public bool DoubleBuffered; + public bool SRGBFramebuffer; + public ContextPixelFormat PixelFormat; + public ContextSwapMethod SwapMethod; + public int Samples; - /// - /// If the backbuffer should support multisample. - /// - public bool Multisample { get; set; } + // FIXME: Add stereo? + // FIXME: Add transparency? /// - /// How many samples the backbuffer should have if it supports multisampling. + /// Default context values selector. Prioritizes the requested values with a series of "relaxations" to find a close match.
+ /// The relaxations are done in the following order: + /// + /// If no exact match is found try find a format with a larger number of color, depth, or stencil bits. + /// If == false is requested, == true formats will be accepted. + /// If == , any swap method will be accepted. + /// If == true, accept == false formats. + /// Accept any . + /// Decrement by one at a time until 0 and see if any alternative sample counts are possible. + /// Accept any . + /// If all relaxations fail, select the first option in the list. + /// ///
- public int Samples { get; set; } + /// The possible context values. + /// The requested context values. + /// A logger to use for logging. + /// The index of the selected "best match" context values. + public static int DefaultValuesSelector(IReadOnlyList options, ContextValues requested, ILogger? logger) + { + if (options.Count == 0) + { + // FIXME: Maybe better exception? + throw new InvalidOperationException("There needs to be at least one ContextValues option."); + } + + logger?.LogDebug("Default context values matcher:"); + + for (int i = 0; i < options.Count; i++) + { + if (IsEqualExcludingID(options[i], requested)) + { + logger?.LogDebug("Found exact match!"); + return i; + } + } + + // We have to relax some requirements. + + // See if there are any formats with greater bit depth. + logger?.LogDebug("No exact match, looking for context values with greater or equal color depth."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + HasEqualSRGB(options[i], requested) && + HasEqualPixelFormat(options[i], requested) && + HasEqualSwapMethod(options[i], requested)) + { + logger?.LogDebug("Found matching format with greater color depth!"); + return i; + } + } + + // Relax requested SRGBFramebuffer == false to match formats with sRGB support. + if (requested.SRGBFramebuffer == false) + { + logger?.LogDebug("No match found, relaxing SRGBFramebuffer == false to match with SRGBFramebuffer == true."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + (HasEqualSRGB(options[i], requested) || requested.SRGBFramebuffer == false) && + HasEqualPixelFormat(options[i], requested)) + { + logger?.LogDebug("Found matching format with SRGBFramebuffer == true!"); + return i; + } + } + } + + // Relax requested ContextSwapMethod.Undefined to match with any swap method. + if (requested.SwapMethod == ContextSwapMethod.Undefined) + { + logger?.LogDebug("No match found, relaxing ContextSwapMethod.Undefined to match with any swap method."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + (HasEqualSRGB(options[i], requested) || requested.SRGBFramebuffer == false) && + HasEqualPixelFormat(options[i], requested)) + { + logger?.LogDebug("Found matching format with any swap format!"); + return i; + } + } + } + + // Relax srgb + logger?.LogDebug("No match found, relaxing sRGB framebuffer requirement."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + HasEqualPixelFormat(options[i], requested) && + (requested.SwapMethod == ContextSwapMethod.Undefined || HasEqualSwapMethod(options[i], requested))) + { + logger?.LogDebug("Found matching format without sRGB framebuffer!"); + return i; + } + } + + // Relax pixel format + logger?.LogDebug("No match found, relaxing ContextPixelFormat to match with any pixel format."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + (requested.SwapMethod == ContextSwapMethod.Undefined || HasEqualSwapMethod(options[i], requested))) + { + logger?.LogDebug("Found matching format after relaxing ContextPixelFormat!"); + return i; + } + } + + // Relax MSAA + logger?.LogDebug("No match found, relaxing MSAA samples."); + while (requested.Samples > 0) + { + requested.Samples--; + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualMSAA(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested) && + (requested.SwapMethod == ContextSwapMethod.Undefined || HasEqualSwapMethod(options[i], requested))) + { + logger?.LogDebug($"Found match with {requested.Samples} MSAA samples."); + return i; + } + } + } + + // Relax swap method completely + logger?.LogDebug("No match found, relaxing swap method completely."); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing swap method."); + return i; + } + } + + // FIXME: When we get here we actually want to get the "closest" format + // and not just remove every bit depth test completely. + + // Relax color bits to allow lower color bits. + logger?.LogDebug("No match found, relaxing one of color, depth, or stencil bits"); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasLessOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing stencil bits."); + return i; + } + + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasLessOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing depth bits."); + return i; + } + + if (HasLessOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing color bits."); + return i; + } + } + + // Relax color bits to allow lower color bits. + logger?.LogDebug("No match found, relaxing two of color, depth, or stencil bits"); + for (int i = 0; i < options.Count; i++) + { + if (HasGreaterOrEqualColorBits(options[i], requested) && + HasLessOrEqualDepthBits(options[i], requested) && + HasLessOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing stencil and depth bits."); + return i; + } + + if (HasLessOrEqualColorBits(options[i], requested) && + HasLessOrEqualDepthBits(options[i], requested) && + HasGreaterOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing color and depth bits."); + return i; + } + + if (HasLessOrEqualColorBits(options[i], requested) && + HasGreaterOrEqualDepthBits(options[i], requested) && + HasLessOrEqualStencilBits(options[i], requested) && + HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing color and stencil bits."); + return i; + } + } + + // Relax stencil bits to allow lower stencil bits. + logger?.LogDebug("No match found, relaxing all bits."); + for (int i = 0; i < options.Count; i++) + { + if (HasEqualDoubleBuffer(options[i], requested)) + { + logger?.LogDebug("Found matching format after relaxing all bits."); + return i; + } + } + + // FIXME: More relaxations. + + // All else has failed, return the first format. + logger?.LogDebug("No match found, all relaxations failed. Using the first format in the list..."); + return 0; + } + + public static bool IsEqualExcludingID(ContextValues option, ContextValues requested) + { + return option.RedBits == requested.RedBits && + option.GreenBits == requested.GreenBits && + option.BlueBits == requested.BlueBits && + option.AlphaBits == requested.AlphaBits && + option.DepthBits == requested.DepthBits && + option.StencilBits == requested.StencilBits && + option.DoubleBuffered == requested.DoubleBuffered && + option.SRGBFramebuffer == requested.SRGBFramebuffer && + option.PixelFormat == requested.PixelFormat && + option.SwapMethod == requested.SwapMethod && + option.Samples == requested.Samples; + } + + public static bool HasEqualColorBits(ContextValues option, ContextValues requested) + { + return option.RedBits == requested.RedBits && + option.GreenBits == requested.GreenBits && + option.BlueBits == requested.BlueBits && + option.AlphaBits == requested.AlphaBits; + } + + public static bool HasGreaterOrEqualColorBits(ContextValues option, ContextValues requested) + { + return option.RedBits >= requested.RedBits && + option.GreenBits >= requested.GreenBits && + option.BlueBits >= requested.BlueBits && + option.AlphaBits >= requested.AlphaBits; + } + + public static bool HasLessOrEqualColorBits(ContextValues option, ContextValues requested) + { + return option.RedBits <= requested.RedBits && + option.GreenBits <= requested.GreenBits && + option.BlueBits <= requested.BlueBits && + option.AlphaBits <= requested.AlphaBits; + } + + public static bool HasEqualDepthBits(ContextValues option, ContextValues requested) + { + return option.DepthBits == requested.DepthBits; + } + + public static bool HasGreaterOrEqualDepthBits(ContextValues option, ContextValues requested) + { + return option.DepthBits >= requested.DepthBits; + } + + public static bool HasLessOrEqualDepthBits(ContextValues option, ContextValues requested) + { + return option.DepthBits <= requested.DepthBits; + } + + public static bool HasEqualStencilBits(ContextValues option, ContextValues requested) + { + return option.StencilBits == requested.StencilBits; + } + + public static bool HasGreaterOrEqualStencilBits(ContextValues option, ContextValues requested) + { + return option.StencilBits >= requested.StencilBits; + } + + public static bool HasLessOrEqualStencilBits(ContextValues option, ContextValues requested) + { + return option.StencilBits <= requested.StencilBits; + } + + public static bool HasEqualMSAA(ContextValues option, ContextValues requested) + { + return option.Samples == requested.Samples; + } + + public static bool HasEqualDoubleBuffer(ContextValues option, ContextValues requested) + { + return option.DoubleBuffered == requested.DoubleBuffered; + } + public static bool HasEqualSRGB(ContextValues option, ContextValues requested) + { + return option.SRGBFramebuffer == requested.SRGBFramebuffer; + } + + public static bool HasEqualPixelFormat(ContextValues option, ContextValues requested) + { + return option.PixelFormat == requested.PixelFormat; + } + + public static bool HasEqualSwapMethod(ContextValues option, ContextValues requested) + { + return option.SwapMethod == requested.SwapMethod; + } + + public ContextValues(ulong id, int redBits, int greenBits, int blueBits, int alphaBits, int depthBits, int stencilBits, bool doubleBuffered, bool sRGBFramebuffer, ContextPixelFormat pixelFormat, ContextSwapMethod swapMethod, int samples) + { + ID = id; + RedBits = redBits; + GreenBits = greenBits; + BlueBits = blueBits; + AlphaBits = alphaBits; + DepthBits = depthBits; + StencilBits = stencilBits; + DoubleBuffered = doubleBuffered; + SRGBFramebuffer = sRGBFramebuffer; + PixelFormat = pixelFormat; + SwapMethod = swapMethod; + Samples = samples; + } + + public override bool Equals(object? obj) + { + return obj is ContextValues values && Equals(values); + } + + public bool Equals(ContextValues other) + { + return // ID == other.ID && + RedBits == other.RedBits && + GreenBits == other.GreenBits && + BlueBits == other.BlueBits && + AlphaBits == other.AlphaBits && + DepthBits == other.DepthBits && + StencilBits == other.StencilBits && + DoubleBuffered == other.DoubleBuffered && + SRGBFramebuffer == other.SRGBFramebuffer && + PixelFormat == other.PixelFormat && + SwapMethod == other.SwapMethod && + Samples == other.Samples; + } + + public override int GetHashCode() + { + HashCode hash = new HashCode(); + // hash.Add(ID); + hash.Add(RedBits); + hash.Add(GreenBits); + hash.Add(BlueBits); + hash.Add(AlphaBits); + hash.Add(DepthBits); + hash.Add(StencilBits); + hash.Add(DoubleBuffered); + hash.Add(SRGBFramebuffer); + hash.Add(PixelFormat); + hash.Add(SwapMethod); + hash.Add(Samples); + return hash.ToHashCode(); + } + + public static bool operator ==(ContextValues left, ContextValues right) + { + return left.Equals(right); + } + + public static bool operator !=(ContextValues left, ContextValues right) + { + return !(left == right); + } + + public override readonly string ToString() + { + return $"ID: {ID}, " + + $"RedBits: {RedBits}, " + + $"GreenBits: {GreenBits}, " + + $"BlueBits: {BlueBits}, " + + $"AlphaBits: {AlphaBits}, " + + $"DepthBits: {DepthBits}, " + + $"StencilBits: {StencilBits}, " + + $"DoubleBuffered: {DoubleBuffered}, " + + $"SRGBFramebuffer: {SRGBFramebuffer}, " + + $"PixelFormat: {PixelFormat}, " + + $"SwapMethod: {SwapMethod}, " + + $"Samples: {Samples}"; + } + } + + /// + /// Defined the pixel format type of the context. + /// This is used to differentiate between "normal" fixed point LDR formats + /// and floating point HDR formats. + /// + public enum ContextPixelFormat + { /// - /// The number of bits to represent the red channel. + /// Normal fixed point RGBA format specified by the color bits. /// - public int RedBits { get; set; } = 8; + RGBA, /// - /// The number of bits to represent the green channel. + /// Floating point RGBA pixel format specified by + /// ARB_color_buffer_float + /// or + /// WGL_ATI_pixel_format_float. /// - public int GreenBits { get; set; } = 8; + RGBAFloat, /// - /// The number of bits to represent the blue channel. + /// From EXT_packed_float. + /// Pixel format is unsigned 10F_11F_11F format. /// - public int BlueBits { get; set; } = 8; + RGBAPackedFloat, + } + /// + /// Defines differnet semantics for what happens to the backbuffer after a swap. + /// + public enum ContextSwapMethod + { /// - /// The number of bits to represent the alpha channel. + /// The contents of the backbuffer after a swap is undefined. /// - public int AlphaBits { get; set; } = 8; + Undefined, /// - /// The number of bits to represent the depth backbuffer. + /// The contents of the frontbuffer and backbuffer are exchanged after a swap. /// - public ContextDepthBits DepthBits { get; set; } = ContextDepthBits.Depth24; + Exchange, /// - /// The number of bits to represent the stencil backbuffer. + /// The contents of the backbuffer are copied to the frontbuffer during a swap. + /// Leaving the contents of the backbuffer unchanged. /// - public ContextStencilBits StencilBits { get; set; } = ContextStencilBits.Stencil8; - } - - public struct ContextValues - { - public int RedBits; - public int GreenBits; - public int BlueBits; - public int AlphaBits; - public int DepthBits; - public int StencilBits; - public bool DoubleBuffered; - public bool SRGBFramebuffer; - public bool Multisample; - public int Samples; + Copy, } /// @@ -88,6 +519,11 @@ public enum ContextDepthBits /// None = 0, + /// + /// 16-bit depth buffer. + /// + Depth16 = 16, + /// /// 24-bit depth buffer. /// @@ -97,6 +533,8 @@ public enum ContextDepthBits /// 32-bit depth buffer. ///
Depth32 = 32, + + // FIXME: Floating point depth buffers?? } /// @@ -119,4 +557,29 @@ public enum ContextStencilBits /// Stencil8 = 8, } + + /// + /// See GL_ARB_robustness extension for details. + /// + public enum ContextResetNotificationStrategy + { + NoResetNotification, + LoseContextOnReset, + } + + /// + /// See KHR_context_flush_control extension for details. + /// + public enum ContextReleaseBehaviour + { + /// + /// No flush is done when the context is released (made not current). + /// + None, + + /// + /// A flush is done when the context is released (made not current). + /// + Flush, + } } diff --git a/src/OpenTK.Core/Platform/DialogFileFilter.cs b/src/OpenTK.Core/Platform/DialogFileFilter.cs new file mode 100644 index 0000000000..a507defe61 --- /dev/null +++ b/src/OpenTK.Core/Platform/DialogFileFilter.cs @@ -0,0 +1,48 @@ +using System; + +namespace OpenTK.Core.Platform +{ + public struct DialogFileFilter : IEquatable + { + public string Name; + public string Filter; + + public DialogFileFilter(string name, string filter) + { + Name = name; + Filter = filter; + } + + public override bool Equals(object obj) + { + return obj is DialogFileFilter filter && Equals(filter); + } + + public bool Equals(DialogFileFilter other) + { + return Name == other.Name && + Filter == other.Filter; + } + + public override int GetHashCode() + { + return HashCode.Combine(Name, Filter); + } + + public static bool operator ==(DialogFileFilter left, DialogFileFilter right) + { + return left.Equals(right); + } + + public static bool operator !=(DialogFileFilter left, DialogFileFilter right) + { + return !(left == right); + } + + public override string ToString() + { + return $"{Name} - {Filter}"; + } + } +} + diff --git a/src/OpenTK.Core/Platform/Enums/ClipboardFormat.cs b/src/OpenTK.Core/Platform/Enums/ClipboardFormat.cs index 4ce2a1c533..4d4ceaed0b 100644 --- a/src/OpenTK.Core/Platform/Enums/ClipboardFormat.cs +++ b/src/OpenTK.Core/Platform/Enums/ClipboardFormat.cs @@ -27,17 +27,10 @@ public enum ClipboardFormat Audio = 2, /// - /// A bitmap. See + /// A bitmap. See . /// Bitmap = 3, - /// - /// HTML data. - /// If the clipboard contains this format, - /// it is also possible to get the unformated string using . - /// - HTML = 4, - /// /// A list of files and directories. /// diff --git a/src/OpenTK.Core/Platform/Enums/OpenDialogOptions.cs b/src/OpenTK.Core/Platform/Enums/OpenDialogOptions.cs new file mode 100644 index 0000000000..537a4a9c24 --- /dev/null +++ b/src/OpenTK.Core/Platform/Enums/OpenDialogOptions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTK.Core.Platform +{ + /// + /// Options for open dialogs. + /// + [Flags] + public enum OpenDialogOptions + { + /// + /// Allows multiple items to be selected. + /// + AllowMultiSelect = 1 << 0, + + /// + /// Select directories instead of files. + /// + // FIXME: Make selecting folder it's own function... + SelectDirectory = 1 << 1, + } +} diff --git a/src/OpenTK.Core/Platform/Enums/PalComponents.cs b/src/OpenTK.Core/Platform/Enums/PalComponents.cs index 8d527d8d0e..d628fc59f4 100644 --- a/src/OpenTK.Core/Platform/Enums/PalComponents.cs +++ b/src/OpenTK.Core/Platform/Enums/PalComponents.cs @@ -97,5 +97,10 @@ public enum PalComponents /// Abstraction layer provides the joystick component. ///
Joystick = 1 << 12, + + /// + /// Abstraction layer provides the dialog component. + /// + Dialog = 1 << 13, } } diff --git a/src/OpenTK.Core/Platform/Enums/PlatformEventType.cs b/src/OpenTK.Core/Platform/Enums/PlatformEventType.cs index 10f921c839..d22e807340 100644 --- a/src/OpenTK.Core/Platform/Enums/PlatformEventType.cs +++ b/src/OpenTK.Core/Platform/Enums/PlatformEventType.cs @@ -17,10 +17,11 @@ public enum PlatformEventType Focus, WindowMove, WindowResize, + WindowFramebufferResize, WindowModeChange, - WindowDpiChange, + WindowScaleChange, MouseEnter, diff --git a/src/OpenTK.Core/Platform/Enums/SaveDialogOptions.cs b/src/OpenTK.Core/Platform/Enums/SaveDialogOptions.cs new file mode 100644 index 0000000000..1c641291ba --- /dev/null +++ b/src/OpenTK.Core/Platform/Enums/SaveDialogOptions.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTK.Core.Platform +{ + /// + /// Options for save dialogs. + /// + [Flags] + public enum SaveDialogOptions + { + } +} diff --git a/src/OpenTK.Core/Platform/Enums/Scancode.cs b/src/OpenTK.Core/Platform/Enums/Scancode.cs index bb3e15058a..75bb023de1 100644 --- a/src/OpenTK.Core/Platform/Enums/Scancode.cs +++ b/src/OpenTK.Core/Platform/Enums/Scancode.cs @@ -21,7 +21,7 @@ public enum Scancode Return, Escape, - /// Delete + /// Delete. Backspace, Tab, Spacebar, diff --git a/src/OpenTK.Core/Platform/Enums/SystemCursorType.cs b/src/OpenTK.Core/Platform/Enums/SystemCursorType.cs index 90cf6d94aa..c558cb10c2 100644 --- a/src/OpenTK.Core/Platform/Enums/SystemCursorType.cs +++ b/src/OpenTK.Core/Platform/Enums/SystemCursorType.cs @@ -13,7 +13,7 @@ public enum SystemCursorType Default = 1, /// - /// A mouse cursor to tell the user something is loading (like an arrow with an hour glass) + /// A mouse cursor to tell the user something is loading (like an arrow with an hour glass.) /// Loading = 2, @@ -43,7 +43,7 @@ public enum SystemCursorType TextBeam = 7, /// - /// A cursor which tells the user they can't do something (like a crossed out red circle) + /// A cursor which tells the user they can't do something (like a crossed out red circle.) /// Forbidden = 8, diff --git a/src/OpenTK.Core/Platform/Interfaces/IClipboardComponent.cs b/src/OpenTK.Core/Platform/Interfaces/IClipboardComponent.cs index ae0733dc30..ce7cf93539 100644 --- a/src/OpenTK.Core/Platform/Interfaces/IClipboardComponent.cs +++ b/src/OpenTK.Core/Platform/Interfaces/IClipboardComponent.cs @@ -53,14 +53,6 @@ public interface IClipboardComponent : IPalComponent // FIXME: What is the orientation of this image? Is it bottom up or top down? Bitmap? GetClipboardBitmap(); - /// - /// Gets the HTML string currently in the clipboard. - /// This function returns null if the current clipboard data doesn't have the format. - /// - /// The HTML string currently in the clipboard. - // FIXME: Make more formal what the expected result of this is - string? GetClipboardHTML(); - /// /// Gets a list of files and directories currently in the clipboard. /// This function returns null if the current clipboard data doesn't have the format. diff --git a/src/OpenTK.Core/Platform/Interfaces/IDialogComponent.cs b/src/OpenTK.Core/Platform/Interfaces/IDialogComponent.cs new file mode 100644 index 0000000000..35ab29ff8a --- /dev/null +++ b/src/OpenTK.Core/Platform/Interfaces/IDialogComponent.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +#nullable enable + +namespace OpenTK.Core.Platform +{ + public interface IDialogComponent : IPalComponent + { + /// + /// If the value of this property is true and will work. + /// Otherwise these flags will be ignored. + /// + public bool CanTargetFolders { get; } + + // FIXME: Formalize the format for allowedExtensions so that we avoid being platform dependent. + + public unsafe List? ShowOpenDialog(WindowHandle parent, string title, string directory, DialogFileFilter[]? allowedExtensions, OpenDialogOptions options); + + // FIXME: Does a save dialog return multiple items? + public unsafe string? ShowSaveDialog(WindowHandle parent, string title, string directory, DialogFileFilter[]? allowedExtensions, SaveDialogOptions options); + } +} diff --git a/src/OpenTK.Core/Platform/Interfaces/IMouseComponent.cs b/src/OpenTK.Core/Platform/Interfaces/IMouseComponent.cs index 5c76ffcac8..8188412184 100644 --- a/src/OpenTK.Core/Platform/Interfaces/IMouseComponent.cs +++ b/src/OpenTK.Core/Platform/Interfaces/IMouseComponent.cs @@ -36,6 +36,10 @@ public interface IMouseComponent : IPalComponent /// bool CanSetMousePosition { get; } + // FIXME: When using CaptureMode.Locked should these return the virtual position? + // If not, we need to have a clear distinction between which coordinates get + // virtualized and which ones do not. + /// /// Get the mouse cursor position. /// diff --git a/src/OpenTK.Core/Platform/Interfaces/IPalComponent.cs b/src/OpenTK.Core/Platform/Interfaces/IPalComponent.cs index c4916bd461..d5ca29aedd 100644 --- a/src/OpenTK.Core/Platform/Interfaces/IPalComponent.cs +++ b/src/OpenTK.Core/Platform/Interfaces/IPalComponent.cs @@ -50,9 +50,9 @@ public interface IPalComponent ILogger? Logger { get; set; } /// - /// Initialize the driver. + /// Initialize the component. /// - /// Bitfield of which components the driver need to be initialized. - void Initialize(PalComponents which); + /// The options to initialize the component with. + void Initialize(ToolkitOptions options); } } diff --git a/src/OpenTK.Core/Platform/Interfaces/IWindowComponent.cs b/src/OpenTK.Core/Platform/Interfaces/IWindowComponent.cs index 57829d83a9..7aac517627 100644 --- a/src/OpenTK.Core/Platform/Interfaces/IWindowComponent.cs +++ b/src/OpenTK.Core/Platform/Interfaces/IWindowComponent.cs @@ -242,6 +242,15 @@ public interface IWindowComponent : IPalComponent /// New height of the client area in pixels. void SetClientBounds(WindowHandle handle, int x, int y, int width, int height); + /// + /// Get the size of the window framebuffer in pixels. + /// Use this value when calls to graphics APIs that want pixels, e.g. GL.Viewport(). + /// + /// Handle to a window. + /// Width in pixels of the window framebuffer. + /// Height in pixels of the window framebuffer. + void GetFramebufferSize(WindowHandle handle, out int width, out int height); + /// /// Gets the maximum size of the client area. /// @@ -417,6 +426,13 @@ public interface IWindowComponent : IPalComponent /// The cursor capture mode. void SetCursorCaptureMode(WindowHandle handle, CursorCaptureMode mode); + /// + /// Returns true if the given window has input focus. + /// + /// Handle to a window. + /// If the window has input focus. + bool IsFocused(WindowHandle handle); + /// /// Gives the window input focus. /// @@ -450,5 +466,14 @@ public interface IWindowComponent : IPalComponent /// The screen y coordinate. /// FIXME: Change to use Vector2i instead of x and y variables. void ClientToScreen(WindowHandle handle, int clientX, int clientY, out int x, out int y); + + /// + /// Returns the current scale factor of this window. + /// + /// The window handle. + /// The x scale factor of the window. + /// The y scale factor of the window. + /// + void GetScaleFactor(WindowHandle handle, out float scaleX, out float scaleY); } } diff --git a/src/OpenTK.Core/Platform/OpenGLGraphicsApiHints.cs b/src/OpenTK.Core/Platform/OpenGLGraphicsApiHints.cs index 92fdbe8274..83ed57e8d5 100644 --- a/src/OpenTK.Core/Platform/OpenGLGraphicsApiHints.cs +++ b/src/OpenTK.Core/Platform/OpenGLGraphicsApiHints.cs @@ -10,13 +10,18 @@ namespace OpenTK.Core.Platform public class OpenGLGraphicsApiHints : GraphicsApiHints { /// - public override GraphicsApi Api { get; } + public override GraphicsApi Api => GraphicsApi.OpenGL; /// /// OpenGL version to create. /// public Version Version { get; set; } = new Version(4, 1); + // FIXME: Convert a bunch of the framebuffer settings + // into a single ContextValues property that we can use + // directly when we call the selector. + // - Noggin_bops 2024-06-22 + /// /// Number of bits for red color channel. /// @@ -62,6 +67,21 @@ public class OpenGLGraphicsApiHints : GraphicsApiHints ///
public bool sRGBFramebuffer { get; set; } = false; + /// + /// The pixel format of the context. + /// This differentiates between "normal" fixed point LDR formats + /// and floating point HDR formats. + /// Use or for HDR support. + /// Is by default. + /// + public ContextPixelFormat PixelFormat { get; set; } = ContextPixelFormat.RGBA; + + /// + /// The swap method to use for the context. + /// Is by default. + /// + public ContextSwapMethod SwapMethod { get; set; } = ContextSwapMethod.Undefined; + /// /// The OpenGL profile to request. /// @@ -77,25 +97,64 @@ public class OpenGLGraphicsApiHints : GraphicsApiHints ///
public bool DebugFlag { get; set; } = false; - // FIXME: Robust access and reset notification flags? + /// + /// If the robustness flag should be set or not. + /// + public bool RobustnessFlag { get; set; } = false; + + /// + /// The reset notification strategy to use if is set to true. + /// See GL_ARB_robustness for details. + /// Default value is . + /// + public ContextResetNotificationStrategy ResetNotificationStrategy { get; set; } = ContextResetNotificationStrategy.NoResetNotification; + + /// + /// See GL_ARB_robustness_isolation. + /// needs to be . + /// + public bool ResetIsolation { get; set; } = false; + + /// + /// If the "no error" flag should be set or not. + /// See KHR_no_error. + /// Cannot be enabled while or is set. + /// + public bool NoError { get; set; } = false; + + /// + /// Whether to use KHR_context_flush_control (if available) or not. + /// See for flush control options. + /// + public bool UseFlushControl { get; set; } = false; + + /// + /// If is true then this controls the context release behaviour when the context is changed. + /// Is by default. + /// + public ContextReleaseBehaviour ReleaseBehaviour { get; set; } = ContextReleaseBehaviour.Flush; /// /// A context to enable context sharing with. /// public OpenGLContextHandle? SharedContext { get; set; } = null; + /// + /// A callback that can be used to select appropriate backbuffer values. + /// + public ContextValueSelector Selector { get; set; } = ContextValues.DefaultValuesSelector; + + /// + /// Enumerating on macOS is slow, so by default is not used on macOS. + /// When this property is false the default platform selection of context values are used, which tries to find a closest match. + /// + public bool UseSelectorOnMacOS { get; set; } = false; + /// /// Initializes a new instance of the class. /// - /// The OpenGL variant the hints are for. - public OpenGLGraphicsApiHints(GraphicsApi api = GraphicsApi.OpenGL) + public OpenGLGraphicsApiHints() { - if (api != GraphicsApi.OpenGL && api != GraphicsApi.OpenGLES) - { - throw new Exception($"Cannot create OpenGL hints for graphics API {api}."); - } - - Api = api; } /// diff --git a/src/OpenTK.Core/Platform/ToolkitOptions.cs b/src/OpenTK.Core/Platform/ToolkitOptions.cs new file mode 100644 index 0000000000..0e4da0d8cd --- /dev/null +++ b/src/OpenTK.Core/Platform/ToolkitOptions.cs @@ -0,0 +1,26 @@ +using OpenTK.Core.Utility; + +#nullable enable + +namespace OpenTK.Core.Platform +{ + /// + /// Options used to initialize OpenTK with. + /// + public sealed class ToolkitOptions + { + /// + /// The application name. + /// + public string ApplicationName { get; set; } = "OpenTK Application"; + + /// + /// The logger to send logging to. + /// Log info can be useful to understand what might be going wrong if something isn't working. + /// It's also useful to debug OpenTK. + /// + public ILogger? Logger { get; set; } = null; + + // FIXME: Add additional settings such as PreferSDL and PreferANGLE here... + } +} diff --git a/src/OpenTK.Core/Platform/WindowEventArgs.cs b/src/OpenTK.Core/Platform/WindowEventArgs.cs index fc1c90ff45..ee88acdf1c 100644 --- a/src/OpenTK.Core/Platform/WindowEventArgs.cs +++ b/src/OpenTK.Core/Platform/WindowEventArgs.cs @@ -35,6 +35,8 @@ public class FocusEventArgs : WindowEventArgs { // FIXME: A reference to the window that got or lost focus? + // FIXME: The mouse position + /// /// If the window got or lost focus. /// @@ -89,15 +91,44 @@ public class WindowResizeEventArgs : WindowEventArgs /// public Vector2i NewSize { get; private set; } + /// + /// The new client size of the window. + /// + public Vector2i NewClientSize { get; private set; } + /// /// Initializes a new instance of the class. /// /// The window that got resized. /// The new window size. + /// The new client size of the window. // FIXME: Window client size? framebuffer size? - public WindowResizeEventArgs(WindowHandle window, Vector2i newSize) : base(window) + public WindowResizeEventArgs(WindowHandle window, Vector2i newSize, Vector2i newClientSize) : base(window) { NewSize = newSize; + NewClientSize = newClientSize; + } + } + + /// + /// This event is triggered when a window has its framebuffer change size. + /// This event can occur for different reasons, not only the window changing size. + /// + public class WindowFramebufferResizeEventArgs : WindowEventArgs + { + /// + /// The new framebuffer size of the window. + /// + public Vector2i NewFramebufferSize { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The window whoes framebuffer changed size. + /// The new framebuffer size. + public WindowFramebufferResizeEventArgs(WindowHandle window, Vector2i newFramebufferSize) : base(window) + { + NewFramebufferSize = newFramebufferSize; } } @@ -127,18 +158,8 @@ public WindowModeChangeEventArgs(WindowHandle window, WindowMode newMode) : base /// This can happen if the window is moved between monitors with /// different DPI scaling settings or if the user changes DPI settings. ///
- public class WindowDpiChangeEventArgs : WindowEventArgs + public class WindowScaleChangeEventArgs : WindowEventArgs { - /// - /// The new DPI value in the x-axis. - /// - public int DpiX { get; private set; } - - /// - /// The new DPI value in the y-axis. - /// - public int DpiY { get; private set; } - /// /// The new scale value in the x-axis. /// @@ -150,17 +171,13 @@ public class WindowDpiChangeEventArgs : WindowEventArgs public float ScaleY { get; private set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The window whose dpi has changed. - /// The new x axis dpi. - /// The new y axis dpi. /// The new x axis scale factor. /// The new y axis scale factor. - public WindowDpiChangeEventArgs(WindowHandle window, int dpiX, int dpiY, float scaleX, float scaleY) : base(window) + public WindowScaleChangeEventArgs(WindowHandle window, float scaleX, float scaleY) : base(window) { - DpiX = dpiX; - DpiY = dpiY; ScaleX = scaleX; ScaleY = scaleY; } @@ -391,6 +408,8 @@ public class MouseMoveEventArgs : WindowEventArgs { // FIXME: In what coordinate space is the mouse coords? + // FIXME: Position delta + /// /// The new position of the mouse cursor. /// diff --git a/src/OpenTK.Core/Utility/ConsoleLogger.cs b/src/OpenTK.Core/Utility/ConsoleLogger.cs index c1c2aab87a..3b6500d65d 100644 --- a/src/OpenTK.Core/Utility/ConsoleLogger.cs +++ b/src/OpenTK.Core/Utility/ConsoleLogger.cs @@ -39,5 +39,10 @@ void ILogger.LogInternal(string str, LogLevel level, string filePath, int line, writer.Write($"[{level}] {member} {Path.GetFileName(fileName)}:{line} "); writer.WriteLine(str); } + + void ILogger.Flush() + { + Console.Out.Flush(); + } } } diff --git a/src/OpenTK.Core/Utility/DebugFileLogger.cs b/src/OpenTK.Core/Utility/DebugFileLogger.cs index 2260000260..1ee9fad290 100644 --- a/src/OpenTK.Core/Utility/DebugFileLogger.cs +++ b/src/OpenTK.Core/Utility/DebugFileLogger.cs @@ -40,5 +40,10 @@ void ILogger.LogInternal(string str, LogLevel level, string filePath, int lineNu Writer.Write($"[{level}] {member} {Path.GetFileName(filePath)}:{lineNumber} "); Writer.WriteLine(str); } + + void ILogger.Flush() + { + Writer.Flush(); + } } } diff --git a/src/OpenTK.Core/Utility/DebugLogger.cs b/src/OpenTK.Core/Utility/DebugLogger.cs new file mode 100644 index 0000000000..ec0fc89421 --- /dev/null +++ b/src/OpenTK.Core/Utility/DebugLogger.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenTK.Core.Utility +{ + /// + /// A logger that uses to write messages to the debug log. + /// + public class DebugLogger : ILogger + { + /// + public LogLevel Filter { get; set; } = LogLevel.Debug; + + void ILogger.LogInternal(string str, LogLevel level, string filePath, int lineNumber, string member) + { + if (level < Filter) + { + return; + } + + Debug.Write($"[{level}] {member} {Path.GetFileName(filePath)}:{lineNumber} "); + Debug.WriteLine(str); + } + + void ILogger.Flush() + { + Debug.Flush(); + } + } +} diff --git a/src/OpenTK.Core/Utility/ILogger.cs b/src/OpenTK.Core/Utility/ILogger.cs index 7c5664ecc5..fa539a2ab1 100644 --- a/src/OpenTK.Core/Utility/ILogger.cs +++ b/src/OpenTK.Core/Utility/ILogger.cs @@ -102,5 +102,12 @@ public interface ILogger /// The callsite line number. /// The member name at the callsite. void LogError(string str, [CallerFilePath] string filePath = null, [CallerLineNumber] int line = -1, [CallerMemberName] string member = null) => Log(str, LogLevel.Error, filePath, line, member); + + /// + /// Flushes any pending IO operations. + /// This is useful when e.g. the application is terminating + /// and the final log messages containing the termination reason needs to be written. + /// + void Flush(); } } diff --git a/src/OpenTK.Graphics/OpenTK.Graphics.csproj b/src/OpenTK.Graphics/OpenTK.Graphics.csproj index 1e10f97ca5..01ae327497 100644 --- a/src/OpenTK.Graphics/OpenTK.Graphics.csproj +++ b/src/OpenTK.Graphics/OpenTK.Graphics.csproj @@ -5,6 +5,7 @@ OpenTK.Graphics true icon.png + true diff --git a/src/OpenTK.Input/Hid/HidGenericDesktopUsage.cs b/src/OpenTK.Input/Hid/HidGenericDesktopUsage.cs index c6b5f09a85..9acf398a54 100644 --- a/src/OpenTK.Input/Hid/HidGenericDesktopUsage.cs +++ b/src/OpenTK.Input/Hid/HidGenericDesktopUsage.cs @@ -242,17 +242,17 @@ public enum HidGenericDesktopUsage : ushort DPadUp = 0x90, /// - /// Indicates that bottom of a Direction Pad is pressed + /// Indicates that bottom of a Direction Pad is pressed. /// DPadDown = 0x91, /// - /// Indicates that right side of a Direction Pad is pressed + /// Indicates that right side of a Direction Pad is pressed. /// DPadRight = 0x92, /// - /// Indicates that left side of a Direction Pad is pressed + /// Indicates that left side of a Direction Pad is pressed. /// DPadLeft = 0x93, diff --git a/src/OpenTK.Mathematics/OpenTK.Mathematics.csproj b/src/OpenTK.Mathematics/OpenTK.Mathematics.csproj index 6aee55eb7d..1b53862552 100644 --- a/src/OpenTK.Mathematics/OpenTK.Mathematics.csproj +++ b/src/OpenTK.Mathematics/OpenTK.Mathematics.csproj @@ -6,6 +6,7 @@ OpenTK.Mathematics Mathematical structures and algorithms, provided and used by OpenTK. icon.png + true diff --git a/src/OpenTK.Platform.Native/ANGLE/ANGLEOpenGLComponent.cs b/src/OpenTK.Platform.Native/ANGLE/ANGLEOpenGLComponent.cs index 078e82487f..703678a7db 100644 --- a/src/OpenTK.Platform.Native/ANGLE/ANGLEOpenGLComponent.cs +++ b/src/OpenTK.Platform.Native/ANGLE/ANGLEOpenGLComponent.cs @@ -31,13 +31,8 @@ public class ANGLEOpenGLComponent : IOpenGLComponent internal static readonly Dictionary ContextDict = new Dictionary(); /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.OpenGL) - { - throw new Exception("ANGLEOpenGLComponent can only initialize the OpenGL component."); - } - const IntPtr EGL_NO_DISPLAY = 0; IntPtr extensionsPtr = Egl.QueryString(EGL_NO_DISPLAY, Egl.EXTENSIONS); string extensionsStr = Marshal.PtrToStringAnsi(extensionsPtr)!; diff --git a/src/OpenTK.Platform.Native/DllResolver.cs b/src/OpenTK.Platform.Native/DllResolver.cs index 1cb800bb41..29b0ba2da4 100644 --- a/src/OpenTK.Platform.Native/DllResolver.cs +++ b/src/OpenTK.Platform.Native/DllResolver.cs @@ -70,6 +70,14 @@ public static IntPtr DllImportResolver(string libraryName, Assembly assembly, Dl "libX11.so.0", }, + ["Xrandr"] = new string[] + { + "libXrandr.so", + "libXrandr.so.3", + "libXrandr.so.2", + "libXrandr.so.1", + }, + ["XFixes"] = new string[] { "libXfixes.so", diff --git a/src/OpenTK.Platform.Native/OpenTK.Platform.Native.csproj b/src/OpenTK.Platform.Native/OpenTK.Platform.Native.csproj index 51687821e2..35aaa521c2 100644 --- a/src/OpenTK.Platform.Native/OpenTK.Platform.Native.csproj +++ b/src/OpenTK.Platform.Native/OpenTK.Platform.Native.csproj @@ -5,6 +5,7 @@ true Platform support for OpenTK, contains backends for Windows, Linux, and macOS. icon.png + true diff --git a/src/OpenTK.Platform.Native/PlatformComponents.cs b/src/OpenTK.Platform.Native/PlatformComponents.cs index 5ecf4b0bb3..384b02ed49 100644 --- a/src/OpenTK.Platform.Native/PlatformComponents.cs +++ b/src/OpenTK.Platform.Native/PlatformComponents.cs @@ -48,6 +48,7 @@ public static class PlatformComponents [PalComponents.WindowIcon] = () => new SDL.SDLIconComponent(), [PalComponents.Clipboard] = () => new SDL.SDLClipboardComponent(), [PalComponents.Joystick] = () => new SDL.SDLJoystickComponent(), + //[PalComponents.Dialog] = () => new SDL.SDLDialogComponent(), }; private static Dictionary win32Components = @@ -63,6 +64,7 @@ public static class PlatformComponents [PalComponents.WindowIcon] = () => new Windows.IconComponent(), [PalComponents.Clipboard] = () => new Windows.ClipboardComponent(), [PalComponents.Joystick] = () => new Windows.JoystickComponent(), + [PalComponents.Dialog] = () => new Windows.DialogComponent(), }; private static Dictionary x11Components = @@ -78,6 +80,7 @@ public static class PlatformComponents [PalComponents.WindowIcon] = () => new X11.X11IconComponent(), [PalComponents.Clipboard] = () => new X11.X11ClipboardComponent(), //[PalComponents.Joystick] = () => new X11.X11JoystickComponent(), + //[PalComponents.Dialog] = () => new X11.X11DialogComponent(), }; private static Dictionary macosComponents = @@ -91,8 +94,9 @@ public static class PlatformComponents [PalComponents.KeyboardInput] = () => new macOS.MacOSKeyboardComponent(), [PalComponents.MouseCursor] = () => new macOS.MacOSCursorComponent(), [PalComponents.WindowIcon] = () => new macOS.MacOSIconComponent(), - //[PalComponents.Clipboard] = () => new macOS.MacOSClipboardComponent(), + [PalComponents.Clipboard] = () => new macOS.MacOSClipboardComponent(), //[PalComponents.Joystick] = () => new macOS.MacOSJoystickComponent(), + [PalComponents.Dialog] = () => new macOS.MacOSDialogComponent(), }; /// @@ -182,13 +186,13 @@ public static Backend GetBackend() } } - /// + /// public static IWindowComponent CreateWindowComponent() { return GetPlatformComponent(PalComponents.Window); } - /// + /// public static IOpenGLComponent CreateOpenGLComponent() { // FIXME: Should we do this here? @@ -203,58 +207,64 @@ public static IOpenGLComponent CreateOpenGLComponent() } } - /// + /// public static IDisplayComponent CreateDisplayComponent() { return GetPlatformComponent(PalComponents.Display); } - /// + /// public static IShellComponent CreateShellComponent() { return GetPlatformComponent(PalComponents.Shell); } - /// + /// public static IMouseComponent CreateMouseComponent() { return GetPlatformComponent(PalComponents.MiceInput); } - /// + /// public static IKeyboardComponent CreateKeyboardComponent() { return GetPlatformComponent(PalComponents.KeyboardInput); } - /// + /// public static ICursorComponent CreateCursorComponent() { return GetPlatformComponent(PalComponents.MouseCursor); } - /// + /// public static IIconComponent CreateIconComponent() { return GetPlatformComponent(PalComponents.WindowIcon); } - /// + /// public static IClipboardComponent CreateClipboardComponent() { return GetPlatformComponent(PalComponents.Clipboard); } - /// + /// public static ISurfaceComponent CreateSurfaceComponent() { return GetPlatformComponent(PalComponents.Surface); } - /// + /// public static IJoystickComponent CreateJoystickComponent() { return GetPlatformComponent(PalComponents.Joystick); } + + /// + public static IDialogComponent CreateDialogComponent() + { + return GetPlatformComponent(PalComponents.Dialog); + } } } diff --git a/src/OpenTK.Platform.Native/SDL/SDL.cs b/src/OpenTK.Platform.Native/SDL/SDL.cs index c3702c65b4..fefbc581d7 100644 --- a/src/OpenTK.Platform.Native/SDL/SDL.cs +++ b/src/OpenTK.Platform.Native/SDL/SDL.cs @@ -150,6 +150,9 @@ public override int GetHashCode() [DllImport(SDLLib, CallingConvention = CallingConvention.Cdecl)] internal static extern void SDL_SetWindowSize(SDL_WindowPtr window, int w, int h); + [DllImport(SDLLib, CallingConvention = CallingConvention.Cdecl)] + internal static extern void SDL_GetWindowSizeInPixels(SDL_WindowPtr window, out int w, out int h); + [DllImport(SDLLib, CallingConvention = CallingConvention.Cdecl)] internal static extern int SDL_GetWindowBordersSize(SDL_WindowPtr window, out int top, out int left, out int bottom, out int right); diff --git a/src/OpenTK.Platform.Native/SDL/SDLClipboardComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLClipboardComponent.cs index a996b449e5..2cc491e3e1 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLClipboardComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLClipboardComponent.cs @@ -21,12 +21,8 @@ public class SDLClipboardComponent : IClipboardComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Clipboard) - { - throw new PalException(this, "SDLClipboardComponent can only initialize the Clipboard component."); - } } private ClipboardFormat[] _supportedFormats = { @@ -87,12 +83,6 @@ public void SetClipboardText(string text) throw new InvalidOperationException("SDL 2 doesn't support bitmap clipboard data."); } - /// - public string? GetClipboardHTML() - { - throw new InvalidOperationException("SDL 2 doesn't support HTML clipboard data."); - } - /// public List? GetClipboardFiles() { diff --git a/src/OpenTK.Platform.Native/SDL/SDLCursorComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLCursorComponent.cs index 8be8021bc0..50818cac64 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLCursorComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLCursorComponent.cs @@ -23,12 +23,8 @@ public class SDLCursorComponent : ICursorComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.MouseCursor) - { - throw new PalException(this, "SDLCursorComponent can only initialize the MouseCursor component."); - } } /// diff --git a/src/OpenTK.Platform.Native/SDL/SDLDisplayComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLDisplayComponent.cs index aff8511a9f..44761da1f8 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLDisplayComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLDisplayComponent.cs @@ -25,12 +25,8 @@ public class SDLDisplayComponent : IDisplayComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Display) - { - throw new PalException(this, "SDLDisplayComponent can only initialize the Display component."); - } } /// diff --git a/src/OpenTK.Platform.Native/SDL/SDLIconComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLIconComponent.cs index 4fa6a37b69..bcd5c977b6 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLIconComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLIconComponent.cs @@ -22,12 +22,8 @@ public class SDLIconComponent : IIconComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if(which != PalComponents.WindowIcon) - { - throw new PalException(this, "SDLIconComponent can only initialize the WindowIcon component."); - } } /// diff --git a/src/OpenTK.Platform.Native/SDL/SDLJoystickComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLJoystickComponent.cs index 989dbfc462..1ae70aa692 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLJoystickComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLJoystickComponent.cs @@ -23,13 +23,8 @@ public unsafe class SDLJoystickComponent : IJoystickComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Joystick) - { - throw new Exception("SDLJoystickComponent can only initialize the Joystick component."); - } - SDL_JoystickUpdate(); Console.WriteLine( $"{SDL_NumJoysticks()} joysticks connected." ); } diff --git a/src/OpenTK.Platform.Native/SDL/SDLKeyboardComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLKeyboardComponent.cs index 50d59c1a20..c51df01e63 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLKeyboardComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLKeyboardComponent.cs @@ -23,13 +23,8 @@ public class SDLKeyboardComponent : IKeyboardComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.KeyboardInput) - { - throw new PalException(this, "SDLKeyboardComponent can only initialize the KeyboardInput component."); - } - // Show the default IME UI. // FIXME: Make this user togglable so they can make their own IME window. SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); diff --git a/src/OpenTK.Platform.Native/SDL/SDLMouseComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLMouseComponent.cs index 794869bca0..04c3814ef0 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLMouseComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLMouseComponent.cs @@ -23,12 +23,8 @@ public class SDLMouseComponent : IMouseComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.MiceInput) - { - throw new PalException(this, "SDLMouseComponent can only initialize the Mouse component."); - } } /// diff --git a/src/OpenTK.Platform.Native/SDL/SDLOpenGLComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLOpenGLComponent.cs index ee4636edf3..633212a149 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLOpenGLComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLOpenGLComponent.cs @@ -23,13 +23,8 @@ public class SDLOpenGLComponent : IOpenGLComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.OpenGL) - { - throw new PalException(this, "SDLOpenGLComponent can only initialize the OpenGL component."); - } - int result = SDL_GL_LoadLibrary(null); if (result < 0) { @@ -45,7 +40,7 @@ public void Initialize(PalComponents which) public bool CanCreateFromWindow => true; /// - public bool CanCreateFromSurface => throw new NotImplementedException(); + public bool CanCreateFromSurface => false; /// public OpenGLContextHandle CreateFromSurface() diff --git a/src/OpenTK.Platform.Native/SDL/SDLShellComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLShellComponent.cs index 50279c6aaf..3ce5412b36 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLShellComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLShellComponent.cs @@ -22,12 +22,8 @@ public class SDLShellComponent : IShellComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Shell) - { - throw new PalException(this, "SDLShellComponent can only initialize the Shell component."); - } } /// diff --git a/src/OpenTK.Platform.Native/SDL/SDLWindowComponent.cs b/src/OpenTK.Platform.Native/SDL/SDLWindowComponent.cs index 4f5ac44c0b..a15096b920 100644 --- a/src/OpenTK.Platform.Native/SDL/SDLWindowComponent.cs +++ b/src/OpenTK.Platform.Native/SDL/SDLWindowComponent.cs @@ -27,13 +27,8 @@ public class SDLWindowComponent : IWindowComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Window) - { - throw new PalException(this, "SDLWindowComponent can only initialize the Window component."); - } - // Load SDLLib int result = SDL_Init(SDL_INIT.SDL_INIT_VIDEO | SDL_INIT.SDL_INIT_EVENTS | SDL_INIT.SDL_INIT_JOYSTICK | SDL_INIT.SDL_INIT_GAMECONTROLLER); @@ -150,10 +145,15 @@ public unsafe void ProcessEvents(bool waitForEvents = false) } case SDL_WindowEventID.SDL_WINDOWEVENT_RESIZED: { - Vector2i newSize = new Vector2i(windowEvent.data1, windowEvent.data2); + SDL_GetWindowBordersSize(sdlWindow.Window, out int top, out int left, out int bottom, out int right); - // FIXME: Client area position! - EventQueue.Raise(sdlWindow, PlatformEventType.WindowResize, new WindowResizeEventArgs(sdlWindow, newSize)); + Vector2i newClientSize = new Vector2i(windowEvent.data1, windowEvent.data2); + Vector2i newSize = new Vector2i(newClientSize.X + left + right, newClientSize.Y + top + bottom); + + EventQueue.Raise(sdlWindow, PlatformEventType.WindowResize, new WindowResizeEventArgs(sdlWindow, newSize, newClientSize)); + + SDL_GetWindowSizeInPixels(sdlWindow.Window, out int framebufferWidth, out int framebufferHeight); + EventQueue.Raise(sdlWindow, PlatformEventType.WindowFramebufferResize, new WindowFramebufferResizeEventArgs(sdlWindow, (framebufferWidth, framebufferHeight))); break; } @@ -718,6 +718,14 @@ public void SetClientBounds(WindowHandle handle, int x, int y, int width, int he SDL_SetWindowSize(window.Window, width, height); } + /// + public void GetFramebufferSize(WindowHandle handle, out int width, out int height) + { + SDLWindow window = handle.As(this); + + SDL_GetWindowSizeInPixels(window.Window, out width, out height); + } + /// public void GetMaxClientSize(WindowHandle handle, out int? width, out int? height) { @@ -1139,6 +1147,14 @@ public void SetCursorCaptureMode(WindowHandle handle, CursorCaptureMode mode) } } + /// + public bool IsFocused(WindowHandle handle) + { + SDLWindow window = handle.As(this); + + return SDL_GetWindowFlags(window.Window).HasFlag(SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS); + } + /// public void FocusWindow(WindowHandle handle) { @@ -1174,5 +1190,17 @@ public void ClientToScreen(WindowHandle handle, int clientX, int clientY, out in throw new NotImplementedException(); } + + /// + public void GetScaleFactor(WindowHandle handle, out float scaleX, out float scaleY) + { + SDLWindow window = handle.As(this); + + SDL_GetWindowSize(window.Window, out int width, out int height); + SDL_GetWindowSizeInPixels(window.Window, out int pixelWidth, out int pixelHeight); + + scaleX = pixelWidth / (float)width; + scaleY = pixelHeight / (float)height; + } } } diff --git a/src/OpenTK.Platform.Native/Toolkit.cs b/src/OpenTK.Platform.Native/Toolkit.cs index 3feebcbe52..b56c679164 100644 --- a/src/OpenTK.Platform.Native/Toolkit.cs +++ b/src/OpenTK.Platform.Native/Toolkit.cs @@ -1,5 +1,4 @@ using OpenTK.Core.Platform; -using OpenTK.Core.Utility; using OpenTK.Platform.Native.Windows; using System; using System.Collections.Generic; @@ -9,14 +8,12 @@ namespace OpenTK.Platform.Native { - public sealed class ToolkitOptions - { - public string ApplicationName { get; set; } = "OpenTK Application"; - - public ILogger? Logger { get; set; } = null; - } // FIXME: Maybe find another name for this? + /// + /// Provides static access to all OpenTK platform abstraction interfaces. + /// This is the main way to access the OpenTK PAL2 api. + /// public static class Toolkit { private static IClipboardComponent? _clipboardComponent; @@ -30,29 +27,73 @@ public static class Toolkit private static IWindowComponent? _windowComponent; private static IShellComponent? _shellComponent; private static IJoystickComponent? _joystickComponent; + private static IDialogComponent? _dialogComponent; + /// + /// Interface for creating, interacting with, and deleting windows. + /// public static IWindowComponent Window => _windowComponent; + /// + /// Interface for creating, interacting with, and deleting surfaces. + /// public static ISurfaceComponent Surface => _surfaceComponent; + /// + /// Interface for creating, interacting with, and deleting OpenGL contexts. + /// public static IOpenGLComponent OpenGL => _openGLComponent; + /// + /// Interface for querying information about displays attached to the system. + /// public static IDisplayComponent Display => _displayComponent; + /// + /// Interface for shell functions such as battery information, preferred theme, etc. + /// public static IShellComponent Shell => _shellComponent; + /// + /// Interface for getting and setting the mouse position, and getting mouse button information. + /// public static IMouseComponent Mouse => _mouseComponent; + /// + /// Interface for dealing with keyboard layouts, conversions between and , and IME. + /// public static IKeyboardComponent Keyboard => _keyboardComponent; + /// + /// Interface for creating, interacting with, and deleting mouse cursor images. + /// public static ICursorComponent Cursor => _cursorComponent; + /// + /// Interface for creating, interacting with, and deleting window icon images. + /// public static IIconComponent Icon => _iconComponent; + /// + /// Interface for getting and setting clipboard data. + /// public static IClipboardComponent Clipboard => _clipboardComponent; + /// + /// Interface for getting joystick input. + /// public static IJoystickComponent Joystick => _joystickComponent; + /// + /// Interface for opening system dialogs such as file open dialogs. + /// + public static IDialogComponent Dialog => _dialogComponent; + + /// + /// Initialize OpenTK with the given settings. + /// This function must be called before trying to use the OpenTK api. + /// + /// The options to initialize with. public static void Init(ToolkitOptions options) { // FIXME: Figure out options... @@ -68,6 +109,7 @@ public static void Init(ToolkitOptions options) try { _iconComponent = PlatformComponents.CreateIconComponent(); } catch (NotSupportedException) { } try { _clipboardComponent = PlatformComponents.CreateClipboardComponent(); } catch (NotSupportedException) { } try { _joystickComponent = PlatformComponents.CreateJoystickComponent(); } catch (NotSupportedException) { } + try { _dialogComponent = PlatformComponents.CreateDialogComponent(); } catch (NotSupportedException) { } if (_windowComponent != null) _windowComponent.Logger = options.Logger; @@ -91,23 +133,26 @@ public static void Init(ToolkitOptions options) _clipboardComponent.Logger = options.Logger; if (_joystickComponent != null) _joystickComponent.Logger = options.Logger; + if (_dialogComponent != null) + _dialogComponent.Logger = options.Logger; // FIXME: Change initialize to take toolkit options // This will also allow us to potentially remove the need // to have static classes in the different components // as they could get instances to each other through // this object... - _windowComponent?.Initialize(PalComponents.Window); - _surfaceComponent?.Initialize(PalComponents.Surface); - _openGLComponent?.Initialize(PalComponents.OpenGL); - _displayComponent?.Initialize(PalComponents.Display); - _shellComponent?.Initialize(PalComponents.Shell); - _mouseComponent?.Initialize(PalComponents.MiceInput); - _keyboardComponent?.Initialize(PalComponents.KeyboardInput); - _cursorComponent?.Initialize(PalComponents.MouseCursor); - _iconComponent?.Initialize(PalComponents.WindowIcon); - _clipboardComponent?.Initialize(PalComponents.Clipboard); - _joystickComponent?.Initialize(PalComponents.Joystick); + _windowComponent?.Initialize(options); + _surfaceComponent?.Initialize(options); + _openGLComponent?.Initialize(options); + _displayComponent?.Initialize(options); + _shellComponent?.Initialize(options); + _mouseComponent?.Initialize(options); + _keyboardComponent?.Initialize(options); + _cursorComponent?.Initialize(options); + _iconComponent?.Initialize(options); + _clipboardComponent?.Initialize(options); + _joystickComponent?.Initialize(options); + _dialogComponent?.Initialize(options); } } } diff --git a/src/OpenTK.Platform.Native/Windows/ClipboardComponent.cs b/src/OpenTK.Platform.Native/Windows/ClipboardComponent.cs index 76a825a3d4..b9f4dd28c0 100644 --- a/src/OpenTK.Platform.Native/Windows/ClipboardComponent.cs +++ b/src/OpenTK.Platform.Native/Windows/ClipboardComponent.cs @@ -134,28 +134,13 @@ private void SetClipboardOptions() } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.Clipboard) - { - throw new PalException(this, "ClipboardComponent can only initialize the Clipboard component."); - } - - // Get the HTML format - CF_HTML = Win32.RegisterClipboardFormat("HTML Format"); - if (CF_HTML == 0) - { - throw new Win32Exception(); - } - // FIXME: Should we check for errors here? CF_CanIncludeInClipboardHistory = Win32.RegisterClipboardFormat("CanIncludeInClipboardHistory"); CF_CanUploadToCloudClipboard = Win32.RegisterClipboardFormat("CanUploadToCloudClipboard"); } - // (0xC095): Rich Text Format - private static CF CF_HTML; - private static CF CF_CanIncludeInClipboardHistory; private static CF CF_CanUploadToCloudClipboard; @@ -165,7 +150,6 @@ public void Initialize(PalComponents which) private static readonly ClipboardFormat[] _SupportedFormats = new[] { ClipboardFormat.Text, - ClipboardFormat.HTML, ClipboardFormat.Files, ClipboardFormat.Bitmap, ClipboardFormat.Audio, @@ -207,11 +191,6 @@ internal static ClipboardFormat GetClipboardFormatInternal(ILogger? logger) format = ClipboardFormat.Audio; break; default: - if (cf == CF_HTML) - { - format = ClipboardFormat.HTML; - break; - } break; } @@ -827,72 +806,6 @@ static void GetImage(IntPtr hDC, IntPtr hbm, Span image) return new Bitmap(width, height, image); } - /// - public unsafe string? GetClipboardHTML() - { - bool success = Win32.OpenClipboard(WindowComponent.HelperHWnd); - if (success == false) - { - throw new Win32Exception(); - } - - IntPtr obj = Win32.GetClipboardData(CF_HTML); - if (obj == IntPtr.Zero) - { - int lastError = Marshal.GetLastWin32Error(); - - success = Win32.CloseClipboard(); - if (success == false) - { - throw new Win32Exception(); - } - - if (lastError == 0) - { - Logger?.LogDebug($"Could not get CF_HTML data from clipboard."); - return null; - } - else - { - throw new Win32Exception(lastError); - } - } - - int strSize = (int)Win32.GlobalSize(obj); - if (strSize == 0) - { - throw new Win32Exception(); - } - - byte* strData = (byte*)Win32.GlobalLock(obj); - if (strData == null) - { - throw new Win32Exception(); - } - - // FIXME: Can this fail? Or is this a security issue? - string str = Encoding.UTF8.GetString(strData, strSize); - - bool stillLocked = Win32.GlobalUnlock(obj); - if (stillLocked) - { - // If the function returns NO_ERROR then there is no error. - int errorCode = Marshal.GetLastWin32Error(); - if (errorCode != 0) - { - throw new Win32Exception(errorCode); - } - } - - bool succeess = Win32.CloseClipboard(); - if (succeess == false) - { - throw new Win32Exception(); - } - - return str; - } - /// public List? GetClipboardFiles() { diff --git a/src/OpenTK.Platform.Native/Windows/CursorComponent.cs b/src/OpenTK.Platform.Native/Windows/CursorComponent.cs index 968519af43..25110b12a1 100644 --- a/src/OpenTK.Platform.Native/Windows/CursorComponent.cs +++ b/src/OpenTK.Platform.Native/Windows/CursorComponent.cs @@ -25,12 +25,8 @@ public class CursorComponent : ICursorComponent public ILogger? Logger { get; set; } /// - public void Initialize(PalComponents which) + public void Initialize(ToolkitOptions options) { - if (which != PalComponents.MouseCursor) - { - throw new Exception("CursorComponent can only initialize the MouseCursor component."); - } } /// diff --git a/src/OpenTK.Platform.Native/Windows/DialogComponent.cs b/src/OpenTK.Platform.Native/Windows/DialogComponent.cs new file mode 100644 index 0000000000..476fad23eb --- /dev/null +++ b/src/OpenTK.Platform.Native/Windows/DialogComponent.cs @@ -0,0 +1,471 @@ +using Microsoft.Win32.SafeHandles; +using OpenTK.Core.Platform; +using OpenTK.Core.Utility; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using static OpenTK.Platform.Native.Windows.XInput; + +namespace OpenTK.Platform.Native.Windows +{ + internal class DialogHandle + { + + }; + + internal struct Button + { + public string Text; + } + + internal struct DLGTEMPLATEEX + { + public ushort dlgVer; + public ushort signature; + public uint helpID; + public WindowStylesEx exStyle; + public DialogStyles style; + public ushort cDlgItems; + public short x; + public short y; + public short cx; + public short cy; + /* + sz_Or_Ord menu; + sz_Or_Ord windowClass; + char title[titleLen]; + ushort pointsize; + ushort weight; + byte italic; + byte charset; + char typeface[stringLen]; + */ + } + + internal struct DLGITEMTEMPLATEEX + { + public uint helpID; + public WindowStylesEx exStyle; + // FIXME: + public uint style; + public short x; + public short y; + public short cx; + public short cy; + public uint id; + /* + sz_Or_Ord windowClass; + sz_Or_Ord title; + WORD extraCount; + */ + } + + // Make struct? + internal unsafe struct DialogData + { + public const nuint BLOCK_SIZE = 4096; + + public enum ControlType : ushort + { + Button = 0x0080, + Edit = 0x0081, + Static = 0x0082, + Listbox = 0x0083, + Scrollbar = 0x0084, + Combobox = 0x0085, + } + + public byte* Data; + public nuint Size; + public nuint Used; + + public static DialogData Alloc() + { + DialogData data; + data.Size = 4096; + data.Data = (byte*)NativeMemory.AlignedAlloc(data.Size, sizeof(int)); + data.Used = 0; + return data; + } + + public void EnsureSize(nuint size) + { + if (Used + size > Size) + { + Data = (byte*)NativeMemory.AlignedRealloc(Data, Size + BLOCK_SIZE, sizeof(int)); + Size += BLOCK_SIZE; + } + } + + public void AlignData(nuint alignment) + { + nuint padding = Used % alignment; + EnsureSize(Used + padding); + Used += padding; + } + + public void AddDialogData(T data) where T : unmanaged + { + EnsureSize(Size + (uint)sizeof(T)); + NativeMemory.Copy(&data, &Data[Used], (uint)sizeof(T)); + Used += (uint)sizeof(T); + } + + public void AddDialogString(string str) + { + uint dataSize = (uint)(str.Length * sizeof(char)); + EnsureSize(Size + dataSize); + fixed (char* c = str) + { + NativeMemory.Copy(c, &Data[Used], (uint)dataSize); + } + Used += dataSize; + } + + // FIXME: Strong type for type + public void AddDialogControl(ControlType type, uint style, WindowStylesEx exStyle, uint id, string caption) + { + DLGITEMTEMPLATEEX itemTemplate; + itemTemplate.helpID = 0; + itemTemplate.exStyle = exStyle; + itemTemplate.style = (uint)style; + // FIXME: position! + itemTemplate.x = 0; + itemTemplate.y = 0; + itemTemplate.cx = 10; + itemTemplate.cy = 10; + itemTemplate.id = id; + + AlignData(sizeof(int)); + + AddDialogData(itemTemplate); + + // windowClass + AddDialogData(0xFFFF); + AddDialogData(type); + + AddDialogString(caption); + + // extraData + AddDialogData(0); + + ((DLGTEMPLATEEX*)Data)->cDlgItems++; + } + + public void Init(int w, int h, string caption) + { + DLGTEMPLATEEX template; + template.dlgVer = 1; + template.signature = 0xFFFF; + template.helpID = 0; + template.exStyle = 0; + template.style = DialogStyles.Caption | DialogStyles.Center | DialogStyles.ShellFont; + template.cDlgItems = 0; + template.x = 0; + template.y = 0; + // FIXME: These are in DLU units not in pixels? + template.cx = (short)w; + template.cy = (short)h; + + AddDialogData(template); + + // No menu + AddDialogData(0); + + // No custom class + AddDialogData(0); + + AddDialogString(caption); + + // FIXME: Font stuff! + // pointsize + AddDialogData(12); + // weight + AddDialogData(400); + // italic + AddDialogData(0); + // charset + AddDialogData(0); + // FIXME: Font name! + AddDialogString("Arial"); + + AddDialogControl(ControlType.Button, (uint)WindowStyles.Visible | (uint)WindowStyles.Child | (uint)WindowStyles.TabStop | (uint)ButtonStyles.BS_DEFPUSHBUTTON | (uint)WindowStyles.Group, 0, 1, "Hello!"); + } + } + + public class DialogComponent : IDialogComponent + { + /// + public string Name => nameof(DialogComponent); + + /// + public PalComponents Provides => PalComponents.Dialog; + + /// + public ILogger? Logger { get; set; } + + /// + public void Initialize(ToolkitOptions options) + { + } + + /// + public bool CanTargetFolders => false; + + static IntPtr /* INT_PTR */ MessageBoxDialogProc(IntPtr /* HWND */ hDlg, WM iMessage, UIntPtr /* WPARAM */ wParam, IntPtr /* LPARAM */ lParam) + { + Console.WriteLine($"Message: {iMessage}, wParam: {wParam}, lParam: {lParam}"); + + switch (iMessage) + { + case WM.INITDIALOG: + return 1; + default: + break; + } + + return 0; + } + + internal unsafe int ShowMessageBox(WindowHandle parent, string text, IconHandle icon, ReadOnlySpan