From fe1e6ae2dfce61602966872c68043deb1b05f130 Mon Sep 17 00:00:00 2001 From: vddCore Date: Sat, 27 Jul 2024 09:51:31 +0200 Subject: [PATCH] Add nullability annotations to public API. --- Chroma/Audio/AudioInput.cs | 2 +- Chroma/Audio/AudioManager.cs | 4 +- Chroma/Audio/AudioOutput.cs | 17 +++--- Chroma/Audio/Captures/AudioCapture.cs | 8 +-- Chroma/Audio/Captures/StreamAudioCapture.cs | 4 +- Chroma/Audio/Decoder.cs | 5 +- Chroma/Audio/Sources/FileBasedAudioSource.cs | 11 ++-- Chroma/Audio/Sources/Waveform.cs | 10 +--- Chroma/BootScene.cs | 38 ++++++++----- Chroma/Chroma.csproj | 3 +- .../FileSystem/FileSystemContentProvider.cs | 14 ++--- Chroma/ContentManagement/IContentProvider.cs | 2 +- Chroma/Diagnostics/Logging/Log.cs | 8 +-- Chroma/Diagnostics/Logging/LogInfo.cs | 6 +- Chroma/Diagnostics/Logging/LogManager.cs | 11 +--- Chroma/Diagnostics/SdlLogHook.cs | 4 +- Chroma/EmbeddedAssets.cs | 32 ++++++----- Chroma/Extensibility/HookRegistry.cs | 10 ++-- Chroma/Game.cs | 26 +++++---- Chroma/GameStartupOptions.cs | 2 + Chroma/Graphics/Accelerated/CompoundShader.cs | 9 +-- Chroma/Graphics/Color.cs | 2 +- Chroma/Graphics/Extensions.RenderContext.cs | 6 +- Chroma/Graphics/GraphicsManager.cs | 22 +++++--- Chroma/Graphics/Particles/ParticleEmitter.cs | 2 +- Chroma/Graphics/RenderContext.cs | 20 ++----- Chroma/Graphics/RenderTarget.cs | 2 +- .../TextRendering/Bitmap/BitmapFont.cs | 31 ++++++---- .../TextRendering/Bitmap/BitmapFontInfo.cs | 4 +- .../TextRendering/Bitmap/BitmapFontLexer.cs | 4 +- .../TextRendering/Bitmap/BitmapFontPage.cs | 4 +- .../Graphics/TextRendering/IFontProvider.cs | 2 +- .../TextRendering/TrueType/TrueTypeFont.cs | 46 +++++++++------ Chroma/Graphics/Texture.cs | 2 +- Chroma/Input/Controller.cs | 6 +- .../Input/GameControllers/ControllerDriver.cs | 14 +++-- .../GameControllers/ControllerRegistry.cs | 16 ++++-- .../Nintendo/SwitchJoyConControllerDriver.cs | 56 ++++++++++++++----- .../Nintendo/SwitchProControllerDriver.cs | 42 +++++++++++++- .../Drivers/Sony/DualSense/TriggerEffect.cs | 2 +- .../Drivers/Sony/SonyControllerDriver.cs | 46 +++++++++++++-- Chroma/MemoryManagement/DisposableResource.cs | 2 +- Chroma/MemoryManagement/SdlRwOps.cs | 29 +++++++--- Chroma/README.nuget.md | 4 +- Chroma/Threading/Dispatcher.cs | 8 +-- Chroma/Threading/ScheduledAction.cs | 5 +- Chroma/Threading/ScheduledValueAction.cs | 4 +- Chroma/Windowing/DragDrop/DragDropManager.cs | 3 +- .../Specialized/InputEventHandlers.cs | 36 +++++++----- Chroma/Windowing/MessageBox.cs | 10 ++-- Chroma/Windowing/MessageBoxButton.cs | 4 +- Chroma/Windowing/Window.cs | 50 ++++++++--------- 52 files changed, 443 insertions(+), 267 deletions(-) diff --git a/Chroma/Audio/AudioInput.cs b/Chroma/Audio/AudioInput.cs index 9a99919..9a7eb97 100644 --- a/Chroma/Audio/AudioInput.cs +++ b/Chroma/Audio/AudioInput.cs @@ -13,7 +13,7 @@ public sealed class AudioInput private readonly List _devices = new(); private readonly List _activeCaptures = new(); - private static AudioInput _instance; + private static AudioInput? _instance; internal static AudioInput Instance => _instance ??= new(); public IReadOnlyList Devices => _devices; diff --git a/Chroma/Audio/AudioManager.cs b/Chroma/Audio/AudioManager.cs index c8bca54..4940389 100644 --- a/Chroma/Audio/AudioManager.cs +++ b/Chroma/Audio/AudioManager.cs @@ -16,8 +16,8 @@ public sealed class AudioManager public IReadOnlyList AudioDrivers => _audioDrivers; - public event EventHandler DeviceConnected; - public event EventHandler DeviceDisconnected; + public event EventHandler? DeviceConnected; + public event EventHandler? DeviceDisconnected; internal AudioManager() { diff --git a/Chroma/Audio/AudioOutput.cs b/Chroma/Audio/AudioOutput.cs index 70a8342..f45c067 100644 --- a/Chroma/Audio/AudioOutput.cs +++ b/Chroma/Audio/AudioOutput.cs @@ -16,7 +16,7 @@ public sealed class AudioOutput private readonly List _devices = new(); private readonly List _decoders = new(); - private static AudioOutput _instance; + private static AudioOutput? _instance; internal static AudioOutput Instance => _instance ??= new(); private bool _mixerInitialized; @@ -29,7 +29,7 @@ public sealed class AudioOutput public int Frequency { get; private set; } public ushort SampleCount { get; private set; } - public event EventHandler AudioSourceFinished; + public event EventHandler? AudioSourceFinished; public float MasterVolume { @@ -48,7 +48,7 @@ public float MasterVolume } } - public AudioDevice CurrentOutputDevice => _devices.FirstOrDefault( + public AudioDevice? CurrentOutputDevice => _devices.FirstOrDefault( x => x.OpenIndex == SDL2_nmix.NMIX_GetAudioDevice() ); @@ -62,7 +62,7 @@ public void PauseAll() SDL2_nmix.NMIX_PausePlayback(_playbackPaused); } - public void Open(AudioDevice device = null, int frequency = 44100, ushort sampleCount = 1024) + public void Open(AudioDevice? device = null, int frequency = 44100, ushort sampleCount = 1024) { Close(); @@ -200,10 +200,11 @@ private void EnumerateDecoders() _decoders.Add( new Decoder( - Marshal.PtrToStringAnsi(decoderList[i]->description), - Marshal.PtrToStringUTF8(decoderList[i]->author), - Marshal.PtrToStringAnsi(decoderList[i]->url) - ) { SupportedFormats = fmts } + Marshal.PtrToStringAnsi(decoderList[i]->description) ?? string.Empty, + Marshal.PtrToStringUTF8(decoderList[i]->author) ?? string.Empty, + Marshal.PtrToStringAnsi(decoderList[i]->url) ?? string.Empty, + supportedFormats: fmts + ) ); } } diff --git a/Chroma/Audio/Captures/AudioCapture.cs b/Chroma/Audio/Captures/AudioCapture.cs index c369cfc..bc7cfc9 100644 --- a/Chroma/Audio/Captures/AudioCapture.cs +++ b/Chroma/Audio/Captures/AudioCapture.cs @@ -9,7 +9,7 @@ namespace Chroma.Audio.Captures; public abstract class AudioCapture : DisposableResource { private static readonly Log _log = LogManager.GetForCurrentAssembly(); - private CancellationTokenSource _finishTokenSource; + private CancellationTokenSource? _finishTokenSource; private uint _deviceId; @@ -23,8 +23,8 @@ public abstract class AudioCapture : DisposableResource public ulong TotalSize { get; private set; } protected AudioCapture( - AudioDevice device = null, - AudioFormat format = null, + AudioDevice? device = null, + AudioFormat? format = null, ChannelMode channelMode = ChannelMode.Mono, int frequency = 44100, ushort bufferSize = 4096) @@ -158,7 +158,7 @@ private void Record() { while (true) { - _finishTokenSource.Token.ThrowIfCancellationRequested(); + _finishTokenSource?.Token.ThrowIfCancellationRequested(); var dataSize = SDL2.SDL_GetQueuedAudioSize(_deviceId); diff --git a/Chroma/Audio/Captures/StreamAudioCapture.cs b/Chroma/Audio/Captures/StreamAudioCapture.cs index 917391f..1c4d717 100644 --- a/Chroma/Audio/Captures/StreamAudioCapture.cs +++ b/Chroma/Audio/Captures/StreamAudioCapture.cs @@ -8,8 +8,8 @@ public class StreamAudioCapture : AudioCapture public StreamAudioCapture( Stream stream, - AudioDevice device = null, - AudioFormat format = null, + AudioDevice? device = null, + AudioFormat? format = null, ChannelMode channelMode = ChannelMode.Mono, int frequency = 44100, ushort bufferSize = 4096) : base(device, format, channelMode, frequency, bufferSize) diff --git a/Chroma/Audio/Decoder.cs b/Chroma/Audio/Decoder.cs index 8a97fc6..e03a1ee 100644 --- a/Chroma/Audio/Decoder.cs +++ b/Chroma/Audio/Decoder.cs @@ -8,12 +8,13 @@ public sealed class Decoder public string Author { get; } public string Url { get; } - public IReadOnlyList SupportedFormats { get; internal set; } + public IReadOnlyList SupportedFormats { get; } - internal Decoder(string description, string author, string url) + internal Decoder(string description, string author, string url, IReadOnlyList supportedFormats) { Description = description; Author = author; Url = url; + SupportedFormats = supportedFormats; } } \ No newline at end of file diff --git a/Chroma/Audio/Sources/FileBasedAudioSource.cs b/Chroma/Audio/Sources/FileBasedAudioSource.cs index 98716c8..a3b7481 100644 --- a/Chroma/Audio/Sources/FileBasedAudioSource.cs +++ b/Chroma/Audio/Sources/FileBasedAudioSource.cs @@ -14,8 +14,8 @@ public abstract class FileBasedAudioSource : AudioSource private readonly SdlRwOps _sdlRwOps; - private SDL2_nmix.NMIX_SourceCallback _originalSourceCallback; - private SDL2_nmix.NMIX_SourceCallback _internalSourceCallback; + private SDL2_nmix.NMIX_SourceCallback? _originalSourceCallback; + private SDL2_nmix.NMIX_SourceCallback? _internalSourceCallback; internal IntPtr FileSourceHandle { get; private set; } @@ -71,7 +71,7 @@ internal FileBasedAudioSource(string filePath, bool decodeWhole) { } - internal FileBasedAudioSource(Stream stream, bool decodeWhole, string fileFormat = null) + internal FileBasedAudioSource(Stream stream, bool decodeWhole, string? fileFormat = null) { _sdlRwOps = new SdlRwOps(stream, true); @@ -145,7 +145,6 @@ public override void Play() if (SDL2_nmix.NMIX_Play(Handle) < 0) { _log.Error($"Failed to play the audio source [play]: {SDL2.SDL_GetError()}"); - return; } else { @@ -287,9 +286,9 @@ private void InternalSourceCallback(IntPtr userdata, IntPtr buffer, int bufferSi var actualAudioFormat = AudioFormat.FromSdlFormat(SoundSample->actual.format); for (var i = 0; i < Filters.Count; i++) - Filters[i]?.Invoke(span, actualAudioFormat); + Filters[i](span, actualAudioFormat); - _originalSourceCallback(userdata, buffer, bufferSize); + _originalSourceCallback?.Invoke(userdata, buffer, bufferSize); var sampleDuration = (double)bufferSize / ( SoundSample->actual.rate * SoundSample->actual.channels * (actualAudioFormat.BitsPerSample / 8) diff --git a/Chroma/Audio/Sources/Waveform.cs b/Chroma/Audio/Sources/Waveform.cs index 33be304..9e45e4b 100644 --- a/Chroma/Audio/Sources/Waveform.cs +++ b/Chroma/Audio/Sources/Waveform.cs @@ -8,15 +8,14 @@ public class Waveform : AudioSource { private static readonly Log _log = LogManager.GetForCurrentAssembly(); - private SDL2_nmix.NMIX_SourceCallback _internalCallback; // Needs to be a class field to avoid GC collection. + private SDL2_nmix.NMIX_SourceCallback? _internalCallback; // Needs to be a class field to avoid GC collection. public AudioStreamDelegate SampleGenerator { get; set; } public ChannelMode ChannelMode { get; } public int Frequency { get; } - public Waveform(AudioFormat format, AudioStreamDelegate sampleGenerator, - ChannelMode channelMode = ChannelMode.Stereo, int frequency = 44100) + public Waveform(AudioFormat format, AudioStreamDelegate sampleGenerator, ChannelMode channelMode = ChannelMode.Stereo, int frequency = 44100) { _internalCallback = AudioCallback; SampleGenerator = sampleGenerator; @@ -41,9 +40,6 @@ public Waveform(AudioFormat format, AudioStreamDelegate sampleGenerator, private void AudioCallback(IntPtr userData, IntPtr samples, int bufferSize) { - if (SampleGenerator == null) - return; - unsafe { var span = new Span( @@ -51,7 +47,7 @@ private void AudioCallback(IntPtr userData, IntPtr samples, int bufferSize) bufferSize ); - SampleGenerator.Invoke(span, Format); + SampleGenerator(span, Format); } } } \ No newline at end of file diff --git a/Chroma/BootScene.cs b/Chroma/BootScene.cs index 614e285..e94e27c 100644 --- a/Chroma/BootScene.cs +++ b/Chroma/BootScene.cs @@ -2,27 +2,29 @@ using System; using System.Numerics; +using Chroma.Diagnostics.Logging; using Chroma.Graphics; internal sealed class BootScene { private const int FrameCount = 301; - + + private readonly Log _log = LogManager.GetForCurrentAssembly(); private readonly Game _game; private readonly Vector2 _bootTextTextureOffset = new(0, 270); private int _currentFrame; - private EmbeddedAssets.BootAnimationInfo CurrentBootTextProperties - => EmbeddedAssets.BootAnimationData[0][_currentFrame]; + private EmbeddedAssets.BootAnimationInfo? CurrentBootTextProperties + => EmbeddedAssets.BootAnimationData?[0][_currentFrame]; - private EmbeddedAssets.BootAnimationInfo CurrentBootWheelProperties - => EmbeddedAssets.BootAnimationData[1][_currentFrame]; + private EmbeddedAssets.BootAnimationInfo? CurrentBootWheelProperties + => EmbeddedAssets.BootAnimationData?[1][_currentFrame]; - private EmbeddedAssets.BootAnimationInfo CurrentBootLogoProperties - => EmbeddedAssets.BootAnimationData[2][_currentFrame]; + private EmbeddedAssets.BootAnimationInfo? CurrentBootLogoProperties + => EmbeddedAssets.BootAnimationData?[2][_currentFrame]; - internal event EventHandler Finished; + internal event EventHandler? Finished; internal BootScene(Game game) { @@ -31,9 +33,19 @@ internal BootScene(Game game) internal void FixedUpdate(float delta) { - EmbeddedAssets.BootWheelTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootWheelProperties.Opacity); - EmbeddedAssets.BootLogoTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootLogoProperties.Opacity); - EmbeddedAssets.BootTextTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootTextProperties.Opacity); + if (CurrentBootWheelProperties == null + || CurrentBootLogoProperties == null + || CurrentBootTextProperties == null) + { + _log.Warning("Boot logo properties were null. This is abnormal. Aborting boot logo display."); + _currentFrame = FrameCount; + } + else + { + EmbeddedAssets.BootWheelTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootWheelProperties.Opacity); + EmbeddedAssets.BootLogoTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootLogoProperties.Opacity); + EmbeddedAssets.BootTextTexture.ColorMask = new Color(1f, 1f, 1f, CurrentBootTextProperties.Opacity); + } if (_currentFrame >= FrameCount) return; @@ -51,13 +63,13 @@ internal void Draw(RenderContext context) _game.Window.Center, Vector2.One, EmbeddedAssets.BootWheelTexture.Center, - CurrentBootWheelProperties.Rotation + CurrentBootWheelProperties?.Rotation ?? 0 ); context.DrawTexture( EmbeddedAssets.BootLogoTexture, _game.Window.Center, - new Vector2(CurrentBootLogoProperties.Scale), + new Vector2(CurrentBootLogoProperties?.Scale ?? 0), EmbeddedAssets.BootLogoTexture.Center, 0 ); diff --git a/Chroma/Chroma.csproj b/Chroma/Chroma.csproj index eb183b4..0e90515 100644 --- a/Chroma/Chroma.csproj +++ b/Chroma/Chroma.csproj @@ -2,6 +2,7 @@ net7.0 latest + enable @@ -18,7 +19,7 @@ Chroma - 0.64.1 + 0.65.0 vddCore git https://github.com/Chroma-2D/Chroma diff --git a/Chroma/ContentManagement/FileSystem/FileSystemContentProvider.cs b/Chroma/ContentManagement/FileSystem/FileSystemContentProvider.cs index 57ea9f1..96d1cfc 100644 --- a/Chroma/ContentManagement/FileSystem/FileSystemContentProvider.cs +++ b/Chroma/ContentManagement/FileSystem/FileSystemContentProvider.cs @@ -19,14 +19,14 @@ public class FileSystemContentProvider : DisposableResource, IContentProvider public string ContentRoot { get; } - public FileSystemContentProvider(string contentRoot = null) + public FileSystemContentProvider(string? contentRoot = null) { - ContentRoot = contentRoot; - - if (string.IsNullOrEmpty(ContentRoot)) + if (string.IsNullOrEmpty(contentRoot)) { - ContentRoot = Path.Combine(AppContext.BaseDirectory, "Content"); + contentRoot = Path.Combine(AppContext.BaseDirectory, "Content"); } + + ContentRoot = contentRoot; _loadedResources = new HashSet(); _importers = new Dictionary>(); @@ -34,7 +34,7 @@ public FileSystemContentProvider(string contentRoot = null) RegisterImporters(); } - public T Load(string relativePath, params object[] args) where T : DisposableResource + public T? Load(string relativePath, params object[] args) where T : DisposableResource { var type = typeof(T); @@ -173,7 +173,7 @@ private void RegisterImporters() private string MakeAbsolutePath(string relativePath) => Path.Combine(ContentRoot, relativePath); - private void OnResourceDisposing(object sender, EventArgs e) + private void OnResourceDisposing(object? sender, EventArgs e) { if (sender is DisposableResource disposable) { diff --git a/Chroma/ContentManagement/IContentProvider.cs b/Chroma/ContentManagement/IContentProvider.cs index 8227ef6..0efcd6b 100644 --- a/Chroma/ContentManagement/IContentProvider.cs +++ b/Chroma/ContentManagement/IContentProvider.cs @@ -8,7 +8,7 @@ public interface IContentProvider : IDisposable { string ContentRoot { get; } - T Load(string relativePath, params object[] args) where T : DisposableResource; + T? Load(string relativePath, params object[] args) where T : DisposableResource; void Unload(T resource) where T : DisposableResource; byte[] Read(string relativePath); diff --git a/Chroma/Diagnostics/Logging/Log.cs b/Chroma/Diagnostics/Logging/Log.cs index 7412b6b..d08f539 100644 --- a/Chroma/Diagnostics/Logging/Log.cs +++ b/Chroma/Diagnostics/Logging/Log.cs @@ -76,16 +76,16 @@ public void Exception(Exception e) } public void Info(object obj) - => Info(obj.ToString()); + => Info(obj.ToString() ?? ""); public void Warning(object obj) - => Warning(obj.ToString()); + => Warning(obj.ToString() ?? ""); public void Error(object obj) - => Error(obj.ToString()); + => Error(obj.ToString() ?? ""); public void Debug(object obj) - => Debug(obj.ToString()); + => Debug(obj.ToString() ?? ""); public Log WithOutputTemplate(string template) { diff --git a/Chroma/Diagnostics/Logging/LogInfo.cs b/Chroma/Diagnostics/Logging/LogInfo.cs index f8d049d..31c352e 100644 --- a/Chroma/Diagnostics/Logging/LogInfo.cs +++ b/Chroma/Diagnostics/Logging/LogInfo.cs @@ -2,8 +2,8 @@ using System.Reflection; -internal class LogInfo +internal class LogInfo(Assembly owningAssembly, Log log) { - internal Assembly OwningAssembly { get; set; } - internal Log Log { get; set; } + internal Assembly OwningAssembly { get; } = owningAssembly; + internal Log Log { get; } = log; } \ No newline at end of file diff --git a/Chroma/Diagnostics/Logging/LogManager.cs b/Chroma/Diagnostics/Logging/LogManager.cs index f33f7f2..d606b65 100644 --- a/Chroma/Diagnostics/Logging/LogManager.cs +++ b/Chroma/Diagnostics/Logging/LogManager.cs @@ -41,7 +41,7 @@ public static Log GetForCurrentAssembly(bool initializeDefaults = true) (log) => { log.SinkTo(new FileSink(GetAssemblyLogPath(asm))); }); } - private static Log GetForAssembly(Assembly assembly, bool initializeDefaults, Action postInit = null) + private static Log GetForAssembly(Assembly assembly, bool initializeDefaults, Action? postInit = null) { var logInfo = Logs.FirstOrDefault(x => x.OwningAssembly == assembly); @@ -61,16 +61,11 @@ private static Log GetForAssembly(Assembly assembly, bool initializeDefaults, Ac postInit?.Invoke(log); } - logInfo = new LogInfo - { - OwningAssembly = assembly, - Log = log - }; - + logInfo = new LogInfo(assembly, log); Logs.Add(logInfo); } - return logInfo.Log; + return logInfo.Log ?? throw new InvalidOperationException(""); } private static string GetAssemblyLogPath(Assembly assembly) diff --git a/Chroma/Diagnostics/SdlLogHook.cs b/Chroma/Diagnostics/SdlLogHook.cs index 26d9ff6..9285e09 100644 --- a/Chroma/Diagnostics/SdlLogHook.cs +++ b/Chroma/Diagnostics/SdlLogHook.cs @@ -9,7 +9,7 @@ namespace Chroma.Diagnostics; internal static class SdlLogHook { private static readonly Log _log = LogManager.GetForCurrentAssembly(); - private static SDL2.SDL_LogOutputFunction _originalLogOutputFunction; + private static SDL2.SDL_LogOutputFunction? _originalLogOutputFunction; internal static void Enable() { @@ -26,7 +26,7 @@ private static void LogOutputFunction( SDL2.SDL_LogPriority priority, IntPtr message) { - var messageString = Marshal.PtrToStringAuto(message); + var messageString = Marshal.PtrToStringAuto(message) ?? ""; switch (priority) { diff --git a/Chroma/EmbeddedAssets.cs b/Chroma/EmbeddedAssets.cs index b48b60e..10fa93c 100644 --- a/Chroma/EmbeddedAssets.cs +++ b/Chroma/EmbeddedAssets.cs @@ -35,16 +35,16 @@ internal class BootAnimationInfo internal const string BootTextResourceKey = "Chroma.Resources.boot.crsub.png"; internal const string BootWheelResourceKey = "Chroma.Resources.boot.crwheel.png"; - private static Texture _logoTexture; - private static Texture _betaEmblem; - private static Texture _defaultIcon; - private static Texture _dummyFix; - private static TrueTypeFont _defaultFont; + private static Texture? _logoTexture; + private static Texture? _betaEmblem; + private static Texture? _defaultIcon; + private static Texture? _dummyFix; + private static TrueTypeFont? _defaultFont; - private static Texture _bootLogoTexture; - private static Texture _bootTextTexture; - private static Texture _bootWheelTexture; - private static List> _bootAnimData; + private static Texture? _bootLogoTexture; + private static Texture? _bootTextTexture; + private static Texture? _bootWheelTexture; + private static List>? _bootAnimData; internal static Texture DefaultIconTexture => Retrieve(ref _defaultIcon, nameof(_defaultIcon)); internal static Texture DummyFixTexture => Retrieve(ref _dummyFix, nameof(_dummyFix)); @@ -56,7 +56,7 @@ internal class BootAnimationInfo internal static Texture BootTextTexture => Retrieve(ref _bootTextTexture, nameof(_bootTextTexture)); internal static Texture BootWheelTexture => Retrieve(ref _bootWheelTexture, nameof(_bootWheelTexture)); - internal static List> BootAnimationData => + internal static List>? BootAnimationData => _bootAnimData ?? LazyDeserialize(ref _bootAnimData, BootDataResourceKey); internal static void LoadEmbeddedAssets() @@ -71,12 +71,12 @@ internal static void LoadEmbeddedAssets() _bootWheelTexture = Load(ref _bootWheelTexture, BootWheelResourceKey); } - private static T Retrieve(ref T field, string name) + private static T Retrieve(ref T? field, string name) { return field ?? throw new FrameworkException($"Internal failure: {name} not loaded"); } - private static T Load(ref T field, string resourceKey, params object[] args) + private static T? Load(ref T? field, string resourceKey, params object?[] args) { if (field == null) { @@ -84,13 +84,17 @@ private static T Load(ref T field, string resourceKey, params object[] args) .GetManifestResourceStream(resourceKey); var actualArgs = new[] { resourceStream }.Concat(args).ToArray(); - field = (T)Activator.CreateInstance(typeof(T), actualArgs); + + field = (T)( + Activator.CreateInstance(typeof(T), actualArgs) + ?? throw new FrameworkException($"Unable to create an instance of embedded resource for type '{typeof(T).FullName}.'") + ); } return field; } - private static T LazyDeserialize(ref T field, string resourceKey) + private static T? LazyDeserialize(ref T? field, string resourceKey) { if (field == null) { diff --git a/Chroma/Extensibility/HookRegistry.cs b/Chroma/Extensibility/HookRegistry.cs index d275cfc..5545f9a 100644 --- a/Chroma/Extensibility/HookRegistry.cs +++ b/Chroma/Extensibility/HookRegistry.cs @@ -12,7 +12,7 @@ namespace Chroma.Extensibility; public static class HookRegistry { private static readonly Log _log = LogManager.GetForCurrentAssembly(); - private static Game _owner; + private static Game _owner = null!; private static readonly Dictionary> _prefixHooks = new(); private static readonly Dictionary> _postfixHooks = new(); @@ -173,9 +173,9 @@ private static bool InvokePrefixHooksMutable(HookPoint hookPoint, ref T argum for (var i = 0; i < hookCollection.Count; i++) { - var arguments = new object[] { _owner, argument }; + var arguments = new object?[] { _owner, argument }; continueToMainBody &= (bool)hookCollection[i].Invoke(null, arguments)!; - argument = (T)arguments[1]; + argument = (T?)arguments[1] ?? throw new InvalidOperationException("Argument mutation cannot be a null value."); } return continueToMainBody; @@ -188,7 +188,7 @@ private static bool InvokePrefixHooks(HookPoint hookPoint, T argument) for (var i = 0; i < hookCollection.Count; i++) { - continueToMainBody &= (bool)hookCollection[i].Invoke(null, new object[] { _owner, argument })!; + continueToMainBody &= (bool)hookCollection[i].Invoke(null, new object?[] { _owner, argument })!; } return continueToMainBody; @@ -200,7 +200,7 @@ private static void InvokePostfixHooks(HookPoint hookPoint, T argument) for (var i = 0; i < hookCollection.Count; i++) { - hookCollection[i].Invoke(null, new object[] { _owner, argument }); + hookCollection[i].Invoke(null, new object?[] { _owner, argument }); } } diff --git a/Chroma/Game.cs b/Chroma/Game.cs index 5dba849..0872311 100644 --- a/Chroma/Game.cs +++ b/Chroma/Game.cs @@ -17,13 +17,13 @@ public class Game : IDisposable { - private static DefaultScene _defaultScene; - private static BootScene _bootScene; + private static DefaultScene? _defaultScene; + private static BootScene? _bootScene; private static readonly Log _log = LogManager.GetForCurrentAssembly(); private int _fixedTimeStepTarget; - private IContentProvider _content; + private IContentProvider? _content; internal static bool WasConstructed { get; private set; } @@ -39,11 +39,11 @@ protected set } public GameStartupOptions StartupOptions { get; } - public Window Window { get; private set; } - public GraphicsManager Graphics { get; private set; } - public AudioManager Audio { get; private set; } + public Window Window { get; private set; } = null!; + public GraphicsManager Graphics { get; private set; } = null!; + public AudioManager Audio { get; private set; } = null!; - public Game(GameStartupOptions options = null) + public Game(GameStartupOptions? options = null) { #if !DEBUG _log.LogLevel = LogLevel.Info | LogLevel.Warning | LogLevel.Error; @@ -65,7 +65,7 @@ public Game(GameStartupOptions options = null) SdlLogHook.Enable(); } - options ??= new GameStartupOptions(); + options ??= GameStartupOptions.Default; StartupOptions = options; WasConstructed = true; @@ -326,12 +326,14 @@ private void InitializeAudio() Audio.Initialize(); } - private void BootSceneFinished(object sender, EventArgs e) + private void BootSceneFinished(object? sender, EventArgs e) { - _bootScene.Finished -= BootSceneFinished; + if (_bootScene != null) + { + _bootScene.Finished -= BootSceneFinished; + } FinishBoot(); - InitializeGraphicsDefaults(); } @@ -347,7 +349,7 @@ public void Dispose() OnDispose(); Window.Dispose(); - _content.Dispose(); + _content?.Dispose(); AudioOutput.Instance.Close(); AudioInput.Instance.Close(); diff --git a/Chroma/GameStartupOptions.cs b/Chroma/GameStartupOptions.cs index b8ba1af..cddbdd3 100644 --- a/Chroma/GameStartupOptions.cs +++ b/Chroma/GameStartupOptions.cs @@ -7,6 +7,8 @@ public class GameStartupOptions public int MsaaSamples { get; } + public static readonly GameStartupOptions Default = new(); + public GameStartupOptions(bool constructDefaultScene = true, bool useBootSplash = true, int msaaSamples = 0) { ConstructDefaultScene = constructDefaultScene; diff --git a/Chroma/Graphics/Accelerated/CompoundShader.cs b/Chroma/Graphics/Accelerated/CompoundShader.cs index 648385a..b6b8dcd 100644 --- a/Chroma/Graphics/Accelerated/CompoundShader.cs +++ b/Chroma/Graphics/Accelerated/CompoundShader.cs @@ -8,14 +8,17 @@ public sealed class CompoundShader : Shader public string PixelShaderFilePath { get; } public string VertexShaderFilePath { get; } - public string PixelShaderSourceCode { get; private set; } - public string VertexShaderSourceCode { get; private set; } + public string PixelShaderSourceCode { get; } + public string VertexShaderSourceCode { get; } public CompoundShader(string pixelShaderFilePath, string vertexShaderFilePath) { PixelShaderFilePath = pixelShaderFilePath; VertexShaderFilePath = vertexShaderFilePath; + PixelShaderSourceCode = File.ReadAllText(PixelShaderFilePath); + VertexShaderSourceCode = File.ReadAllText(VertexShaderFilePath); + TryCompilePixelShader(); TryCompileVertexShader(); @@ -25,7 +28,6 @@ public CompoundShader(string pixelShaderFilePath, string vertexShaderFilePath) private void TryCompilePixelShader() { - PixelShaderSourceCode = File.ReadAllText(PixelShaderFilePath); PixelShaderObjectHandle = SDL_gpu.GPU_CompileShader(SDL_gpu.GPU_ShaderEnum.GPU_PIXEL_SHADER, PixelShaderSourceCode); @@ -35,7 +37,6 @@ private void TryCompilePixelShader() private void TryCompileVertexShader() { - VertexShaderSourceCode = File.ReadAllText(VertexShaderFilePath); VertexShaderObjectHandle = SDL_gpu.GPU_CompileShader(SDL_gpu.GPU_ShaderEnum.GPU_VERTEX_SHADER, VertexShaderSourceCode); diff --git a/Chroma/Graphics/Color.cs b/Chroma/Graphics/Color.cs index 34f8647..17d2c18 100644 --- a/Chroma/Graphics/Color.cs +++ b/Chroma/Graphics/Color.cs @@ -372,7 +372,7 @@ public override string ToString() public override int GetHashCode() => RGBA.GetHashCode(); - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is Color color && Equals(color); public bool Equals(Color other) diff --git a/Chroma/Graphics/Extensions.RenderContext.cs b/Chroma/Graphics/Extensions.RenderContext.cs index df2f182..c303939 100644 --- a/Chroma/Graphics/Extensions.RenderContext.cs +++ b/Chroma/Graphics/Extensions.RenderContext.cs @@ -153,7 +153,7 @@ public static void DrawString( IFontProvider font, string text, Vector2 position, - GlyphTransform perCharTransform = null + GlyphTransform? perCharTransform = null ) => context.DrawString(font, text, position.X, position.Y, perCharTransform); public static void DrawString( @@ -175,7 +175,7 @@ public static void DrawString( string text, float x, float y, - GlyphTransform perCharTransform = null + GlyphTransform? perCharTransform = null ) => context.DrawString(EmbeddedAssets.DefaultFont, text, x, y, perCharTransform); public static void DrawString( @@ -201,7 +201,7 @@ public static void DrawString( this RenderContext context, string text, Vector2 position, - GlyphTransform perCharTransform = null + GlyphTransform? perCharTransform = null ) => context.DrawString(EmbeddedAssets.DefaultFont, text, position.X, position.Y, perCharTransform); public static void DrawString( diff --git a/Chroma/Graphics/GraphicsManager.cs b/Chroma/Graphics/GraphicsManager.cs index 01c0252..b3d80d1 100644 --- a/Chroma/Graphics/GraphicsManager.cs +++ b/Chroma/Graphics/GraphicsManager.cs @@ -15,7 +15,7 @@ public sealed class GraphicsManager private static readonly Log _log = LogManager.GetForCurrentAssembly(); private readonly GameStartupOptions _startupOptions; - private Stack _rendererIdStack; + private Stack? _rendererIdStack; private VerticalSyncMode _verticalSyncMode; @@ -69,17 +69,17 @@ public VerticalSyncMode VerticalSyncMode public string OpenGlVendorString => Marshal.PtrToStringAnsi( Gl.GetString(Gl.GL_VENDOR) - ); + ) ?? string.Empty; public string OpenGlVersionString => Marshal.PtrToStringAnsi( Gl.GetString(Gl.GL_VERSION) - ); + ) ?? string.Empty; public string OpenGlRendererString => Marshal.PtrToStringAnsi( Gl.GetString(Gl.GL_RENDERER) - ); + ) ?? string.Empty; public bool IsAdaptiveVSyncSupported { get; private set; } @@ -112,7 +112,7 @@ public IList GetDisplayList() return displays; } - public Display FetchDisplay(int index) + public Display? FetchDisplay(int index) { if (SDL2.SDL_GetCurrentDisplayMode(index, out _) == 0) return new Display(index); @@ -212,7 +212,9 @@ internal IntPtr InitializeRenderer(Window window, out IntPtr windowHandle) "Performance might be degraded."); } - var rendererId = _rendererIdStack.Peek(); + var rendererId = _rendererIdStack?.Peek() + ?? throw new InvalidOperationException("Renderer ID stack was null."); + var renderTargetHandle = SDL_gpu.GPU_InitRendererByID( rendererId, (ushort)window.Size.Width, @@ -279,8 +281,14 @@ private bool EnumerateGlExtensions() for (var i = 0; i < numExtensions; i++) { var strPtr = Gl.GetStringI(Gl.GL_EXTENSIONS, (uint)i); - var str = Marshal.PtrToStringAnsi(strPtr); + var str = Marshal.PtrToStringAnsi(strPtr) ?? string.Empty; + if (string.IsNullOrEmpty(str)) + { + _log.Warning($"Extension ID {i} lookup returned an empty string."); + continue; + } + GlExtensions.Add(str); } diff --git a/Chroma/Graphics/Particles/ParticleEmitter.cs b/Chroma/Graphics/Particles/ParticleEmitter.cs index a541e2c..90e7dc1 100644 --- a/Chroma/Graphics/Particles/ParticleEmitter.cs +++ b/Chroma/Graphics/Particles/ParticleEmitter.cs @@ -21,7 +21,7 @@ public class ParticleEmitter public Texture Texture { get; set; } public ParticleStateInitializer ParticleStateInitializer { get; set; } - public ParticleEmitter(Texture texture, ParticleStateInitializer initializer = null) + public ParticleEmitter(Texture texture, ParticleStateInitializer? initializer = null) { if (texture.Disposed) throw new ArgumentException("Texture provided was already disposed.", nameof(texture)); diff --git a/Chroma/Graphics/RenderContext.cs b/Chroma/Graphics/RenderContext.cs index 84416c1..995ab9f 100644 --- a/Chroma/Graphics/RenderContext.cs +++ b/Chroma/Graphics/RenderContext.cs @@ -311,10 +311,7 @@ public void RenderTo(RenderTarget target, RenderTargetDrawDelegate drawingLogic) "Render target cannot be null." ); } - - if (drawingLogic == null) - return; - + TargetStack.Push(target.TargetHandle); { drawingLogic.Invoke(this, target); @@ -332,9 +329,6 @@ public void WithCamera(Camera camera, CameraDrawDelegate drawingLogic) ); } - if (drawingLogic == null) - return; - SDL_gpu.GPU_SetCamera(CurrentRenderTarget, ref camera.GpuCamera); { drawingLogic.Invoke(this, camera); @@ -342,7 +336,7 @@ public void WithCamera(Camera camera, CameraDrawDelegate drawingLogic) SDL_gpu.GPU_SetCamera(CurrentRenderTarget, IntPtr.Zero); } - public void DrawString(IFontProvider font, string text, float x, float y, GlyphTransform glyphTransform = null) + public void DrawString(IFontProvider font, string? text, float x, float y, GlyphTransform? glyphTransform = null) { if (font == null) { @@ -372,8 +366,9 @@ public void DrawString(IFontProvider font, string text, float x, float y, GlyphT var bounds = font.GetGlyphBounds(c); var offsets = font.GetRenderOffsets(c); var advance = font.GetHorizontalAdvance(c); - var texture = font.GetTexture(c); - + var texture = font.GetTexture(c) + ?? throw new InvalidOperationException("Font provider returned a null texture while rendering glyph"); + var srcRect = new SDL_gpu.GPU_Rect { x = bounds.X, @@ -434,7 +429,7 @@ public void DrawBatch(DrawOrder order = DrawOrder.BackToFront, bool discardBatch ); for (var i = 0; i < BatchBuffer.Count; i++) - BatchBuffer[i].DrawAction?.Invoke(this); + BatchBuffer[i].DrawAction.Invoke(this); if (discardBatchAfterUse) BatchBuffer.Clear(); @@ -442,9 +437,6 @@ public void DrawBatch(DrawOrder order = DrawOrder.BackToFront, bool discardBatch public void Batch(Action drawAction, int depth) { - if (drawAction == null) - return; - BatchBuffer.Add( new BatchInfo { diff --git a/Chroma/Graphics/RenderTarget.cs b/Chroma/Graphics/RenderTarget.cs index 07ea940..2cdf3fb 100644 --- a/Chroma/Graphics/RenderTarget.cs +++ b/Chroma/Graphics/RenderTarget.cs @@ -11,7 +11,7 @@ public class RenderTarget : Texture internal IntPtr TargetHandle { get; } - public Camera CurrentCamera { get; private set; } + public Camera? CurrentCamera { get; private set; } public Rectangle? CurrentViewport { get; private set; } public RenderTarget(int width, int height) diff --git a/Chroma/Graphics/TextRendering/Bitmap/BitmapFont.cs b/Chroma/Graphics/TextRendering/Bitmap/BitmapFont.cs index f109e0c..c62e5ab 100644 --- a/Chroma/Graphics/TextRendering/Bitmap/BitmapFont.cs +++ b/Chroma/Graphics/TextRendering/Bitmap/BitmapFont.cs @@ -14,15 +14,15 @@ public class BitmapFont : DisposableResource, IFontProvider { private static readonly Log _log = LogManager.GetForCurrentAssembly(); - private readonly Func _pageTextureLookup; + private readonly Func? _pageTextureLookup; - private List _lines; - private Dictionary _parsers; + private List _lines = null!; + private Dictionary _parsers = null!; - private BitmapFontLexer _lexer; + private BitmapFontLexer _lexer = null!; - private BitmapFontInfo Info { get; set; } - private BitmapFontCommon Common { get; set; } + private BitmapFontInfo Info { get; set; } = null!; + private BitmapFontCommon Common { get; set; } = null!; private List Pages { get; } = new(); private List Kernings { get; } = new(); @@ -39,7 +39,7 @@ public class BitmapFont : DisposableResource, IFontProvider public int Height => Info.Size; public int LineSpacing => (int)(Common.LineHeight + Info.Spacing.Y); - public BitmapFont(string fileName, Stream dataStream, Func pageTextureLookup = null) + public BitmapFont(string fileName, Stream dataStream, Func? pageTextureLookup = null) { FileName = fileName; _pageTextureLookup = pageTextureLookup; @@ -47,7 +47,7 @@ public BitmapFont(string fileName, Stream dataStream, Func page Initialize(dataStream); } - public BitmapFont(string fileName, Func pageTextureLookup = null) + public BitmapFont(string fileName, Func? pageTextureLookup = null) { FileName = fileName; _pageTextureLookup = pageTextureLookup; @@ -116,7 +116,7 @@ public Size Measure(string text) return new Size(maxWidth, height); } - public Texture GetTexture(char c) + public Texture? GetTexture(char c) { if (Pages.Count == 0) return null; @@ -158,7 +158,7 @@ public int GetHorizontalAdvance(char c) return Glyphs[c].HorizontalAdvance; } - public Texture GetTexture(int page) + public Texture? GetTexture(int page) { if (!Pages.Any()) return null; @@ -174,7 +174,7 @@ protected override void FreeManagedResources() _log.Debug($"Disposing {Info.FaceName}."); foreach (var page in Pages) - page.Texture.Dispose(); + page.Texture?.Dispose(); } private void ParseFontDefinition() @@ -356,7 +356,14 @@ private void ParsePage() if (id >= 0) { - Pages.Add(new BitmapFontPage(id, Path.GetDirectoryName(FileName), fileName, _pageTextureLookup)); + Pages.Add( + new BitmapFontPage( + id, + Path.GetDirectoryName(FileName) ?? throw new ArgumentNullException($"Unable to retrieve directory name for '{FileName}'"), + fileName, + _pageTextureLookup + ) + ); } else { diff --git a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontInfo.cs b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontInfo.cs index e03f9b4..6464c29 100644 --- a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontInfo.cs +++ b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontInfo.cs @@ -4,11 +4,11 @@ internal class BitmapFontInfo { - public string FaceName { get; internal set; } + public string FaceName { get; internal set; } = string.Empty; public int Size { get; internal set; } public bool IsBold { get; internal set; } public bool IsItalic { get; internal set; } - public string CharSet { get; internal set; } + public string CharSet { get; internal set; } = string.Empty; public bool IsUnicode { get; internal set; } public int ScalePercent { get; internal set; } public bool IsSmooth { get; internal set; } diff --git a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontLexer.cs b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontLexer.cs index 63154c8..e05d732 100644 --- a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontLexer.cs +++ b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontLexer.cs @@ -2,8 +2,8 @@ internal class BitmapFontLexer { - public string CurrentKey { get; private set; } - public string CurrentValue { get; private set; } + public string CurrentKey { get; private set; } = string.Empty; + public string CurrentValue { get; private set; } = string.Empty; public string Line { get; } diff --git a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontPage.cs b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontPage.cs index 38121d3..a9aee2d 100644 --- a/Chroma/Graphics/TextRendering/Bitmap/BitmapFontPage.cs +++ b/Chroma/Graphics/TextRendering/Bitmap/BitmapFontPage.cs @@ -13,9 +13,9 @@ internal class BitmapFontPage public string RootDirectory { get; } public string FileName { get; } - public Texture Texture { get; } + public Texture? Texture { get; } - public BitmapFontPage(int id, string rootDirectory, string fileName, Func textureLoader = null) + public BitmapFontPage(int id, string rootDirectory, string fileName, Func? textureLoader = null) { ID = id; diff --git a/Chroma/Graphics/TextRendering/IFontProvider.cs b/Chroma/Graphics/TextRendering/IFontProvider.cs index c63ce8b..72b51e5 100644 --- a/Chroma/Graphics/TextRendering/IFontProvider.cs +++ b/Chroma/Graphics/TextRendering/IFontProvider.cs @@ -18,7 +18,7 @@ public interface IFontProvider int GetKerning(char first, char second); int GetHorizontalAdvance(char c); - Texture GetTexture(char c = (char)0); + Texture? GetTexture(char c = (char)0); Rectangle GetGlyphBounds(char c); Vector2 GetRenderOffsets(char c); } \ No newline at end of file diff --git a/Chroma/Graphics/TextRendering/TrueType/TrueTypeFont.cs b/Chroma/Graphics/TextRendering/TrueType/TrueTypeFont.cs index 0a00dce..54908e1 100644 --- a/Chroma/Graphics/TextRendering/TrueType/TrueTypeFont.cs +++ b/Chroma/Graphics/TextRendering/TrueType/TrueTypeFont.cs @@ -30,17 +30,17 @@ public class TrueTypeFont : DisposableResource, IFontProvider private readonly Dictionary _glyphs = new(); private readonly Dictionary _kernings = new(); - private byte[] _ttfData; - private Texture _atlas; + private byte[]? _ttfData; + private Texture? _atlas; private FT_Error _lastFtError; public static TrueTypeFont Default => EmbeddedAssets.DefaultFont; - public string FamilyName { get; private set; } - public string StyleName { get; private set; } + public string FamilyName { get; private set; } = string.Empty; + public string StyleName { get; private set; } = string.Empty; - public IReadOnlyCollection Alphabet { get; private set; } + public IReadOnlyCollection? Alphabet { get; private set; } public bool IsKerningEnabled { get; set; } = true; public int LineSpacing { get; private set; } @@ -122,7 +122,7 @@ static TrueTypeFont() } } - public TrueTypeFont(string fileName, int height, string alphabet = null) + public TrueTypeFont(string fileName, int height, string? alphabet = null) { using (var fs = new FileStream(fileName, FileMode.Open)) { @@ -130,12 +130,12 @@ public TrueTypeFont(string fileName, int height, string alphabet = null) } } - public TrueTypeFont(Stream stream, int height, string alphabet = null) + public TrueTypeFont(Stream stream, int height, string? alphabet = null) { ConstructWithStream(stream, height, alphabet); } - private void ConstructWithStream(Stream stream, int height, string alphabet = null) + private void ConstructWithStream(Stream stream, int height, string? alphabet = null) { EnsureOnMainThread(); @@ -213,7 +213,7 @@ public Size Measure(string text) return new Size(maxWidth, maxHeight); } - public Texture GetTexture(char c = (char)0) + public Texture? GetTexture(char c = (char)0) => _atlas; public int GetHorizontalAdvance(char c) @@ -272,11 +272,11 @@ private void InvalidateFont() private unsafe void LoadTtf() { fixed (byte* fontPtr = _ttfData) - { + { _lastFtError = FT_New_Memory_Face( _libraryHandle, fontPtr, - _ttfData.Length, + _ttfData?.Length ?? -1, 0, out _face ); @@ -287,16 +287,16 @@ out _face } } - FamilyName = Marshal.PtrToStringUTF8(_face->family_name); - if (FamilyName == null) + FamilyName = Marshal.PtrToStringUTF8(_face->family_name) ?? string.Empty; + if (string.IsNullOrEmpty(FamilyName)) { - _log.Warning("Unable to retrieve family name for the loaded typeface."); + _log.Warning("Typeface family name was empty."); } - - StyleName = Marshal.PtrToStringUTF8(_face->style_name); - if (StyleName == null) + + StyleName = Marshal.PtrToStringUTF8(_face->style_name) ?? string.Empty; + if (string.IsNullOrEmpty(StyleName)) { - _log.Warning("Unable to retrieve style name for the loaded typeface."); + _log.Warning("Typeface style name was empty."); } } @@ -332,11 +332,21 @@ private unsafe void ResizeFont() private void RebuildAtlas() { + if (Alphabet == null) + { + throw new InvalidOperationException("Attempt to generate a texture atlas with a null alphabet."); + } + _atlas = GenerateTextureAtlas(Alphabet); } private void RetrieveKerningData() { + if (Alphabet == null) + { + throw new InvalidOperationException("Attempt to retrieve kerning data with a null alphabet."); + } + for (var i = 0; i < Alphabet.Count; i++) { var left = Alphabet.ElementAt(i); diff --git a/Chroma/Graphics/Texture.cs b/Chroma/Graphics/Texture.cs index 24c85c2..a5dd82f 100644 --- a/Chroma/Graphics/Texture.cs +++ b/Chroma/Graphics/Texture.cs @@ -16,7 +16,7 @@ public class Texture : DisposableResource internal IntPtr ImageHandle { get; private set; } internal unsafe SDL_gpu.GPU_Image* Image => (SDL_gpu.GPU_Image*)ImageHandle.ToPointer(); - private byte[] _pixelData; + private byte[] _pixelData = null!; public PixelFormat Format { get; private set; } diff --git a/Chroma/Input/Controller.cs b/Chroma/Input/Controller.cs index e6bd5bb..20dbda7 100644 --- a/Chroma/Input/Controller.cs +++ b/Chroma/Input/Controller.cs @@ -12,7 +12,7 @@ public static class Controller public static int DeviceCount => ControllerRegistry.Instance.DeviceCount; - public static ControllerDriver Get(int playerIndex) + public static ControllerDriver? Get(int playerIndex) => ControllerRegistry.Instance.GetControllerDriver(playerIndex); public static bool Is(int playerIndex) where T : ControllerDriver @@ -21,7 +21,7 @@ public static bool Is(int playerIndex) where T : ControllerDriver return driver != null && driver.Is(); } - public static T As(int playerIndex) where T : ControllerDriver + public static T? As(int playerIndex) where T : ControllerDriver => Get(playerIndex)?.As(); public static void SetDeadZone(int playerIndex, ControllerAxis axis, ushort value) @@ -139,7 +139,7 @@ public static string RetrieveMapping(int playerIndex) return driver.RetrieveMapping(); } - public static IReadOnlySet GetActiveButtons(int playerIndex) + public static IReadOnlySet? GetActiveButtons(int playerIndex) { var driver = Get(playerIndex); diff --git a/Chroma/Input/GameControllers/ControllerDriver.cs b/Chroma/Input/GameControllers/ControllerDriver.cs index 022c290..854ee26 100644 --- a/Chroma/Input/GameControllers/ControllerDriver.cs +++ b/Chroma/Input/GameControllers/ControllerDriver.cs @@ -18,7 +18,7 @@ public abstract class ControllerDriver public IReadOnlyDictionary DeadZones => _deadZones; public IReadOnlySet ActiveButtons => _buttonStates; - public ControllerInfo Info { get; } + public ControllerInfo? Info { get; } public abstract string Name { get; } public virtual BatteryStatus BatteryStatus @@ -38,7 +38,7 @@ internal ControllerDriver(ControllerInfo info) Info = info; } - public T As() where T : ControllerDriver + public T? As() where T : ControllerDriver => this as T; public bool Is() where T : ControllerDriver @@ -143,6 +143,12 @@ public virtual string RetrieveMapping() internal unsafe void SendEffect(byte* ptr, int length) { + if (Info == null) + { + _log.Error("Controller information structure was null when attempting to send effect to the device."); + return; + } + if (SDL2.SDL_GameControllerSendEffect(Info.InstancePointer, ptr, length) < 0) _log.Error($"Failed to send effect: {SDL2.SDL_GetError()}"); } @@ -163,7 +169,7 @@ public virtual void SendEffect(byte[] data) public virtual void SetLedColor(Color color) { - if (!Info.HasConfigurableLed) + if (Info?.HasConfigurableLed is null or false) return; if (SDL2.SDL_GameControllerSetLED(Info.InstancePointer, color.R, color.G, color.B) < 0) @@ -185,7 +191,7 @@ public override string ToString() var sb = new StringBuilder(); sb.AppendLine($"Driver \"{Name}\":"); - sb.AppendLine(Info.ToString()); + sb.AppendLine(Info?.ToString() ?? ""); return sb.ToString(); } diff --git a/Chroma/Input/GameControllers/ControllerRegistry.cs b/Chroma/Input/GameControllers/ControllerRegistry.cs index db467bb..9aadf68 100644 --- a/Chroma/Input/GameControllers/ControllerRegistry.cs +++ b/Chroma/Input/GameControllers/ControllerRegistry.cs @@ -32,6 +32,9 @@ public void Register(IntPtr instance, ControllerDriver controller) if (_controllers.ContainsKey(instance)) throw new InvalidOperationException("Duplicate controller instance pointer."); + if (controller.Info == null) + throw new InvalidOperationException("Controller driver information structure was null."); + if (_playerMappings[controller.Info.PlayerIndex] != IntPtr.Zero) throw new InvalidOperationException("Duplicate controller player index."); @@ -44,8 +47,13 @@ public void Unregister(IntPtr instance) if (!_controllers.ContainsKey(instance)) throw new InvalidOperationException($"Controller with instance ID {instance} does not exist."); - var playerIndex = _controllers[instance].Info.PlayerIndex; - _playerMappings[playerIndex] = IntPtr.Zero; + var controllerInfo = _controllers[instance].Info; + + if (controllerInfo != null) + { + var playerIndex = controllerInfo.PlayerIndex; + _playerMappings[playerIndex] = IntPtr.Zero; + } _controllers.Remove(instance); } @@ -59,7 +67,7 @@ public int FindFirstFreePlayerSlot() return -1; } - public ControllerDriver GetControllerDriver(int playerIndex) + public ControllerDriver? GetControllerDriver(int playerIndex) { if (!_playerMappings.ContainsKey(playerIndex) || _playerMappings[playerIndex] == IntPtr.Zero) return null; @@ -68,7 +76,7 @@ public ControllerDriver GetControllerDriver(int playerIndex) return _controllers[instancePointer]; } - internal ControllerDriver GetControllerDriverByPointer(IntPtr instancePointer) + internal ControllerDriver? GetControllerDriverByPointer(IntPtr instancePointer) { if (!_controllers.ContainsKey(instancePointer)) return null; diff --git a/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchJoyConControllerDriver.cs b/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchJoyConControllerDriver.cs index 4228ecf..2f0a7cd 100644 --- a/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchJoyConControllerDriver.cs +++ b/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchJoyConControllerDriver.cs @@ -48,26 +48,48 @@ public sealed class SwitchJoyConControllerDriver public bool GyroscopeEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + } set { - if (SDL2.SDL_GameControllerSetSensorEnabled( - Info.InstancePointer, - SDL2.SDL_SensorType.SDL_SENSOR_GYRO, - value - ) < 0) + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, value) < 0) _log.Error($"Failed to enable gyroscope: {SDL2.SDL_GetError()}"); } } public bool AccelerometerEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + } + set { - if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, - value) < 0) + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, value) < 0) _log.Error($"Failed to enable accelerometer: {SDL2.SDL_GetError()}"); } } @@ -122,14 +144,18 @@ public override int GetRawAxisValue(ControllerAxis axis) public Vector3 ReadGyroscopeSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe { fixed (float* dataPtr = &data[0]) { - if (SDL2.SDL_GameControllerGetSensorData(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, - dataPtr, 3) < 0) + if (SDL2.SDL_GameControllerGetSensorData(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, dataPtr, 3) < 0) { _log.Error($"Failed to retrieve gyroscope data: {SDL2.SDL_GetError()}"); return Vector3.Zero; @@ -146,14 +172,18 @@ public Vector3 ReadGyroscopeSensor() public Vector3 ReadAccelerometerSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe { fixed (float* dataPtr = &data[0]) { - if (SDL2.SDL_GameControllerGetSensorData(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, - dataPtr, 3) < 0) + if (SDL2.SDL_GameControllerGetSensorData(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, dataPtr, 3) < 0) { _log.Error($"Failed to retrieve accelerometer data: {SDL2.SDL_GetError()}"); return Vector3.Zero; diff --git a/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchProControllerDriver.cs b/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchProControllerDriver.cs index 094c0a3..a0f4611 100644 --- a/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchProControllerDriver.cs +++ b/Chroma/Input/GameControllers/Drivers/Nintendo/SwitchProControllerDriver.cs @@ -30,10 +30,23 @@ public sealed class SwitchProControllerDriver public bool GyroscopeEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + } set { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, value) < 0) _log.Error($"Failed to enable gyroscope: {SDL2.SDL_GetError()}"); } @@ -41,14 +54,27 @@ public bool GyroscopeEnabled public bool AccelerometerEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + } set { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, value) < 0) _log.Error($"Failed to enable accelerometer: {SDL2.SDL_GetError()}"); } } - + public SwitchProControllerDriver(ControllerInfo info) : base(info) { UseXboxButtonLayout = false; @@ -80,6 +106,11 @@ internal override void OnButtonReleased(ControllerButtonEventArgs e) public Vector3 ReadGyroscopeSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe @@ -103,6 +134,11 @@ public Vector3 ReadGyroscopeSensor() public Vector3 ReadAccelerometerSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe diff --git a/Chroma/Input/GameControllers/Drivers/Sony/DualSense/TriggerEffect.cs b/Chroma/Input/GameControllers/Drivers/Sony/DualSense/TriggerEffect.cs index 7e3026d..5310859 100644 --- a/Chroma/Input/GameControllers/Drivers/Sony/DualSense/TriggerEffect.cs +++ b/Chroma/Input/GameControllers/Drivers/Sony/DualSense/TriggerEffect.cs @@ -73,7 +73,7 @@ public void Linear(byte leftActuationPoint, byte leftForce, } } - public void Linear(LinearTriggerEffectPreset leftPreset, LinearTriggerEffectPreset rightPreset) + public void Linear(LinearTriggerEffectPreset? leftPreset, LinearTriggerEffectPreset? rightPreset) { unsafe { diff --git a/Chroma/Input/GameControllers/Drivers/Sony/SonyControllerDriver.cs b/Chroma/Input/GameControllers/Drivers/Sony/SonyControllerDriver.cs index 4f15877..d5a7ad5 100644 --- a/Chroma/Input/GameControllers/Drivers/Sony/SonyControllerDriver.cs +++ b/Chroma/Input/GameControllers/Drivers/Sony/SonyControllerDriver.cs @@ -20,23 +20,47 @@ public abstract class SonyControllerDriver public bool GyroscopeEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO); + } set { - if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, - value) < 0) + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_GYRO, value) < 0) _log.Error($"Failed to enable gyroscope: {SDL2.SDL_GetError()}"); } } public bool AccelerometerEnabled { - get => SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + get + { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + return SDL2.SDL_GameControllerIsSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL); + } set { - if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, - value) < 0) + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + + if (SDL2.SDL_GameControllerSetSensorEnabled(Info.InstancePointer, SDL2.SDL_SensorType.SDL_SENSOR_ACCEL, value) < 0) _log.Error($"Failed to enable accelerometer: {SDL2.SDL_GetError()}"); } } @@ -51,6 +75,11 @@ internal SonyControllerDriver(ControllerInfo info) public Vector3 ReadGyroscopeSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe @@ -75,6 +104,11 @@ public Vector3 ReadGyroscopeSensor() public Vector3 ReadAccelerometerSensor() { + if (Info == null) + { + throw new FrameworkException("Driver information structure was null. This is an internal error. Report pwease? >w<"); + } + var data = new float[3]; unsafe diff --git a/Chroma/MemoryManagement/DisposableResource.cs b/Chroma/MemoryManagement/DisposableResource.cs index 9fb0ce8..bd29dd8 100644 --- a/Chroma/MemoryManagement/DisposableResource.cs +++ b/Chroma/MemoryManagement/DisposableResource.cs @@ -7,7 +7,7 @@ public class DisposableResource : IDisposable { public bool Disposed { get; private set; } - public event EventHandler Disposing; + public event EventHandler? Disposing; ~DisposableResource() { diff --git a/Chroma/MemoryManagement/SdlRwOps.cs b/Chroma/MemoryManagement/SdlRwOps.cs index 00c3430..0d9e769 100644 --- a/Chroma/MemoryManagement/SdlRwOps.cs +++ b/Chroma/MemoryManagement/SdlRwOps.cs @@ -7,7 +7,7 @@ namespace Chroma.MemoryManagement; internal sealed class SdlRwOps : DisposableResource { - private Stream _stream; + private Stream? _stream; private unsafe SDL2.SDL_RWops* _rwOps; private delegate long SdlRwOpsSizeDelegate(IntPtr context); @@ -20,11 +20,11 @@ internal sealed class SdlRwOps : DisposableResource private delegate int SdlRwOpsCloseDelegate(IntPtr context); - private SdlRwOpsSizeDelegate _size; - private SdlRwOpsSeekDelegate _seek; - private SdlRwOpsReadDelegate _read; - private SdlRwOpsWriteDelegate _write; - private SdlRwOpsCloseDelegate _close; + private SdlRwOpsSizeDelegate? _size; + private SdlRwOpsSeekDelegate? _seek; + private SdlRwOpsReadDelegate? _read; + private SdlRwOpsWriteDelegate? _write; + private SdlRwOpsCloseDelegate? _close; internal IntPtr RwOpsHandle { @@ -75,7 +75,7 @@ private long Size(IntPtr context) try { - length = _stream.Length; + length = _stream?.Length ?? throw new InvalidOperationException("Underlying stream was null."); } catch (Exception e) { @@ -88,6 +88,11 @@ private long Size(IntPtr context) private long Seek(IntPtr context, long offset, int whence) { + if (_stream == null) + { + throw new InvalidOperationException("Underlying stream was null."); + } + if (!_stream.CanSeek) { SDL2.SDL_SetError("This does not support seeking."); @@ -100,6 +105,11 @@ private long Seek(IntPtr context, long offset, int whence) private unsafe ulong Read(IntPtr context, void* ptr, ulong size, ulong maxnum) { + if (_stream == null) + { + throw new InvalidOperationException("Underlying stream was null."); + } + if (!_stream.CanRead) { SDL2.SDL_SetError("Attempted to read from a write-only stream."); @@ -123,6 +133,11 @@ private unsafe ulong Read(IntPtr context, void* ptr, ulong size, ulong maxnum) private unsafe ulong Write(IntPtr context, void* ptr, ulong size, ulong maxnum) { + if (_stream == null) + { + throw new InvalidOperationException("Underlying stream was null."); + } + if (!_stream.CanWrite) { SDL2.SDL_SetError("Attempted to write to a read-only stream."); diff --git a/Chroma/README.nuget.md b/Chroma/README.nuget.md index dd61caa..eb6bb6b 100644 --- a/Chroma/README.nuget.md +++ b/Chroma/README.nuget.md @@ -7,7 +7,8 @@ Chroma is a 2D game development framework focused on quick prototyping. - SDL updated to 2.30.5 - Fixed a bug where garbage collected `DisposableResource`objects would be disposed outside of the main thread. -- Drop support for .NET 6, move to .NET 7. +- Dropped support for .NET 6, move to .NET 7. +- Added nullability annotations to the API. #### Changes in release 0.64.0 - `Game.LoadContent()` callback has been refactored to `Game.Initialize(IContentPipeline)` @@ -32,6 +33,7 @@ Chroma is a 2D game development framework focused on quick prototyping. #### Changes in release 0.62.2 - `Window.SaveScreenshot(Stream)` no longer destroys the stream after it's done taking a screenshot. + #### Changes in release 0.62.1 - Actions returning values can now also use non-reference types. diff --git a/Chroma/Threading/Dispatcher.cs b/Chroma/Threading/Dispatcher.cs index 5e2c53e..13b6ea6 100644 --- a/Chroma/Threading/Dispatcher.cs +++ b/Chroma/Threading/Dispatcher.cs @@ -19,7 +19,7 @@ internal Dispatcher() public static Task RunOnMainThread(Action action, bool immediateStart = false) { - var scheduledAction = new ScheduledAction { Action = action }; + var scheduledAction = new ScheduledAction(action); ActionQueue.Enqueue(scheduledAction); var task = new Task((a) => @@ -38,19 +38,19 @@ public static Task RunOnMainThread(Action action, bool immediateStart = false) return task; } - public static Task RunOnMainThread(Func valueAction, bool immediateStart = false) + public static Task RunOnMainThread(Func valueAction, bool immediateStart = false) { var scheduledAction = new ScheduledValueAction { ValueAction = valueAction }; ActionQueue.Enqueue(scheduledAction); - var task = new Task((a) => + var task = new Task((a) => { var sched = a as ScheduledValueAction; while (!sched!.Completed) Task.Delay(1); - return (T)sched.ReturnValue; + return (T?)sched.ReturnValue; }, scheduledAction); if (immediateStart) diff --git a/Chroma/Threading/ScheduledAction.cs b/Chroma/Threading/ScheduledAction.cs index 12a8692..0733fa4 100644 --- a/Chroma/Threading/ScheduledAction.cs +++ b/Chroma/Threading/ScheduledAction.cs @@ -4,5 +4,8 @@ internal sealed class ScheduledAction : SchedulerEntry { - public Action Action { get; set; } + public Action Action { get; } + + public ScheduledAction(Action action) + => Action = action; } \ No newline at end of file diff --git a/Chroma/Threading/ScheduledValueAction.cs b/Chroma/Threading/ScheduledValueAction.cs index 329c588..23541a0 100644 --- a/Chroma/Threading/ScheduledValueAction.cs +++ b/Chroma/Threading/ScheduledValueAction.cs @@ -4,6 +4,6 @@ namespace Chroma.Threading; internal class ScheduledValueAction : SchedulerEntry { - public Func ValueAction { get; set; } - public object ReturnValue { get; set; } + public Func? ValueAction { get; set; } + public object? ReturnValue { get; set; } } \ No newline at end of file diff --git a/Chroma/Windowing/DragDrop/DragDropManager.cs b/Chroma/Windowing/DragDrop/DragDropManager.cs index 2a61def..73a0468 100644 --- a/Chroma/Windowing/DragDrop/DragDropManager.cs +++ b/Chroma/Windowing/DragDrop/DragDropManager.cs @@ -38,7 +38,8 @@ internal void OnFileDropped(IntPtr stringPtr) if (_isFileDrop != true) throw new FrameworkException("Unexpected operation type change during a drop operation."); - _fileList.Add(Marshal.PtrToStringAnsi(stringPtr)); + _fileList.Add(Marshal.PtrToStringAnsi(stringPtr)!); + SDL2.SDL_free(stringPtr); } diff --git a/Chroma/Windowing/EventHandling/Specialized/InputEventHandlers.cs b/Chroma/Windowing/EventHandling/Specialized/InputEventHandlers.cs index fc9b159..93be703 100644 --- a/Chroma/Windowing/EventHandling/Specialized/InputEventHandlers.cs +++ b/Chroma/Windowing/EventHandling/Specialized/InputEventHandlers.cs @@ -52,8 +52,11 @@ internal InputEventHandlers(EventDispatcher dispatcher) private void ControllerSensorStateChanged(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.ctouchpad.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); + + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); + if (ev.csensor.sensor == SDL2.SDL_SensorType.SDL_SENSOR_GYRO) { Controller.OnGyroscopeStateChanged( @@ -73,7 +76,8 @@ private void ControllerSensorStateChanged(Window owner, SDL2.SDL_Event ev) private void ControllerTouchpadReleased(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.ctouchpad.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); Controller.OnTouchpadReleased( owner.Game, @@ -84,8 +88,9 @@ private void ControllerTouchpadReleased(Window owner, SDL2.SDL_Event ev) private void ControllerTouchpadTouched(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.ctouchpad.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); - + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); + Controller.OnTouchpadTouched( owner.Game, new(controller, ev.ctouchpad.touchpad, ev.ctouchpad.finger, new(ev.ctouchpad.x, ev.ctouchpad.y), ev.ctouchpad.pressure) @@ -95,7 +100,8 @@ private void ControllerTouchpadTouched(Window owner, SDL2.SDL_Event ev) private void ControllerTouchpadMoved(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.ctouchpad.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); Controller.OnTouchpadMoved( owner.Game, @@ -106,7 +112,8 @@ private void ControllerTouchpadMoved(Window owner, SDL2.SDL_Event ev) private void ControllerAxisMoved(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.caxis.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); var axis = (ControllerAxis)ev.caxis.axis; @@ -119,8 +126,9 @@ private void ControllerAxisMoved(Window owner, SDL2.SDL_Event ev) private void ControllerButtonPressed(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.cbutton.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); - + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); + var button = (ControllerButton)ev.cbutton.button; Controller.OnButtonPressed( @@ -132,8 +140,9 @@ private void ControllerButtonPressed(Window owner, SDL2.SDL_Event ev) private void ControllerButtonReleased(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.cbutton.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); - + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); + var button = (ControllerButton)ev.cbutton.button; Controller.OnButtonReleased( @@ -226,8 +235,9 @@ private void ControllerConnected(Window owner, SDL2.SDL_Event ev) private void ControllerDisconnected(Window owner, SDL2.SDL_Event ev) { var instance = SDL2.SDL_GameControllerFromInstanceID(ev.cdevice.which); - var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance); - + var controller = ControllerRegistry.Instance.GetControllerDriverByPointer(instance) + ?? throw new FrameworkException("Controller driver lookup failed. This is an internal error. Pwease report? >w<"); + ControllerRegistry.Instance.Unregister(instance); Controller.OnDisconnected( @@ -269,7 +279,7 @@ private void TextInput(Window owner, SDL2.SDL_Event ev) { textInput = Marshal.PtrToStringUTF8( new IntPtr(ev.text.text) - ); + ) ?? throw new FrameworkException("Text pointer was null. This is an internal error. Pwease report? >w<"); } owner.Game.OnTextInput(new TextInputEventArgs(textInput)); diff --git a/Chroma/Windowing/MessageBox.cs b/Chroma/Windowing/MessageBox.cs index b602d18..4d8e7a3 100644 --- a/Chroma/Windowing/MessageBox.cs +++ b/Chroma/Windowing/MessageBox.cs @@ -14,8 +14,8 @@ public sealed class MessageBox private List Buttons { get; } private string Title { get; set; } - private string Message { get; set; } - private Action AbnormalClosureAction { get; set; } + private string Message { get; set; } = string.Empty; + private Action? AbnormalClosureAction { get; set; } public MessageBox(MessageBoxSeverity severity) { @@ -37,7 +37,7 @@ public MessageBox WithMessage(string message) return this; } - public MessageBox WithButton(string text, Action action = null) + public MessageBox WithButton(string text, Action? action = null) { Buttons.Add( new MessageBoxButton @@ -56,7 +56,7 @@ public MessageBox HandleAbnormalClosureWith(Action abnormalClosureAction) return this; } - public int Show(Window owner = null) + public int Show(Window? owner = null) { var buttonData = Buttons.Select(button => new SDL2.SDL_MessageBoxButtonData { @@ -96,7 +96,7 @@ public int Show(Window owner = null) return result; } - public static void Show(MessageBoxSeverity severity, string title, string message, Window owner = null) + public static void Show(MessageBoxSeverity severity, string title, string message, Window? owner = null) { if (SDL2.SDL_ShowSimpleMessageBox( (SDL2.SDL_MessageBoxFlags)severity, diff --git a/Chroma/Windowing/MessageBoxButton.cs b/Chroma/Windowing/MessageBoxButton.cs index 59bfd62..57522ab 100644 --- a/Chroma/Windowing/MessageBoxButton.cs +++ b/Chroma/Windowing/MessageBoxButton.cs @@ -5,8 +5,8 @@ namespace Chroma.Windowing; public sealed class MessageBoxButton { public int ID { get; internal set; } - public string Text { get; internal set; } - public Action Action { get; internal set; } + public string Text { get; internal set; } = string.Empty; + public Action? Action { get; internal set; } internal MessageBoxButton() {} } \ No newline at end of file diff --git a/Chroma/Windowing/Window.cs b/Chroma/Windowing/Window.cs index 5fd3b76..6933d9f 100644 --- a/Chroma/Windowing/Window.cs +++ b/Chroma/Windowing/Window.cs @@ -31,7 +31,7 @@ public sealed class Window : DisposableResource private Vector2 _position = new(SDL2.SDL_WINDOWPOS_CENTERED); private WindowState _state = WindowState.Normal; private bool _enableDragDrop; - private WindowHitTestDelegate _hitTestDelegate; + private WindowHitTestDelegate? _hitTestDelegate; private IntPtr _windowHandle; private IntPtr _currentIconPtr; @@ -42,18 +42,18 @@ public sealed class Window : DisposableResource internal delegate void DrawDelegate(RenderContext context); - internal StateUpdateDelegate FixedUpdate; - internal StateUpdateDelegate Update; - internal DrawDelegate Draw; + internal StateUpdateDelegate? FixedUpdate; + internal StateUpdateDelegate? Update; + internal DrawDelegate? Draw; internal Game Game { get; } - internal EventDispatcher EventDispatcher { get; private set; } + internal EventDispatcher? EventDispatcher { get; private set; } internal DragDropManager DragDropManager { get; } internal IntPtr Handle => _windowHandle; internal IntPtr RenderTargetHandle { get; private set; } - internal static Window Instance { get; private set; } + internal static Window Instance { get; private set; } = null!; public delegate WindowHitTestResult WindowHitTestDelegate(Window window, Vector2 position); @@ -332,7 +332,7 @@ public bool EnableDragDrop } } - public WindowHitTestDelegate HitTest + public WindowHitTestDelegate? HitTest { get => _hitTestDelegate; set @@ -359,23 +359,23 @@ public WindowHitTestDelegate HitTest public WindowMode Mode { get; } - public event EventHandler Shown; - public event EventHandler Hidden; - public event EventHandler Invalidated; - public event EventHandler Moved; - public event EventHandler Resized; - public event EventHandler SizeChanged; - public event EventHandler StateChanged; - public event EventHandler MouseEntered; - public event EventHandler MouseLeft; - public event EventHandler Focused; - public event EventHandler Unfocused; - public event EventHandler Closed; - public event EventHandler DisplayChanged; - public event EventHandler QuitRequested; - - public event EventHandler FilesDropped; - public event EventHandler TextDropped; + public event EventHandler? Shown; + public event EventHandler? Hidden; + public event EventHandler? Invalidated; + public event EventHandler? Moved; + public event EventHandler? Resized; + public event EventHandler? SizeChanged; + public event EventHandler? StateChanged; + public event EventHandler? MouseEntered; + public event EventHandler? MouseLeft; + public event EventHandler? Focused; + public event EventHandler? Unfocused; + public event EventHandler? Closed; + public event EventHandler? DisplayChanged; + public event EventHandler? QuitRequested; + + public event EventHandler? FilesDropped; + public event EventHandler? TextDropped; internal Window(Game game) { @@ -677,7 +677,7 @@ private void ExecuteScheduledActions() { try { - scheduledValueAction.ReturnValue = scheduledValueAction.ValueAction.Invoke(); + scheduledValueAction.ReturnValue = scheduledValueAction.ValueAction?.Invoke(); scheduledValueAction.Completed = true; } catch (Exception e)