diff --git a/CodeAnalysis/BannedSymbols.txt b/CodeAnalysis/BannedSymbols.txt index 93011093ee..a0a86a2fe5 100644 --- a/CodeAnalysis/BannedSymbols.txt +++ b/CodeAnalysis/BannedSymbols.txt @@ -2,6 +2,7 @@ M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use obj M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable or EqualityComparer.Default instead. M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable or EqualityComparer.Default instead. T:System.IComparable;Don't use non-generic IComparable. Use generic version instead. +M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast() instead. F:System.UriKind.RelativeOrAbsolute;Incompatible results when run on mono (see https://www.mono-project.com/docs/faq/known-issues/urikind-relativeorabsolute/). Use Validation.TryParseUri(string, out Uri?) instead. M:System.Threading.Tasks.Task.Wait();Don't use Task.Wait. Use Task.WaitSafely() to ensure we avoid deadlocks. M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty. diff --git a/osu.Framework.Android/Graphics/Video/AndroidVideoDecoder.cs b/osu.Framework.Android/Graphics/Video/AndroidVideoDecoder.cs index 35cb601939..1c26d7cc8d 100644 --- a/osu.Framework.Android/Graphics/Video/AndroidVideoDecoder.cs +++ b/osu.Framework.Android/Graphics/Video/AndroidVideoDecoder.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using FFmpeg.AutoGen; using Java.Interop; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Video; using osu.Framework.Logging; @@ -154,7 +155,7 @@ public AndroidVideoDecoder(IRenderer renderer, Stream videoStream) HardwareVideoDecoder targetHwDecoders ) { - if (targetHwDecoders.HasFlag(HardwareVideoDecoder.MediaCodec)) + if (targetHwDecoders.HasFlagFast(HardwareVideoDecoder.MediaCodec)) { string? formatName = Marshal.PtrToStringAnsi((IntPtr)inputFormat->name); diff --git a/osu.Framework.Benchmarks/BenchmarkEnum.cs b/osu.Framework.Benchmarks/BenchmarkEnum.cs new file mode 100644 index 0000000000..530bc6b07b --- /dev/null +++ b/osu.Framework.Benchmarks/BenchmarkEnum.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using BenchmarkDotNet.Attributes; +using osu.Framework.Extensions.EnumExtensions; + +namespace osu.Framework.Benchmarks +{ + [MemoryDiagnoser] + public class BenchmarkEnum + { + [Benchmark] + public bool HasFlag() + { +#pragma warning disable RS0030 // (banned API) + return (FlagsEnum.Flag2 | FlagsEnum.Flag3).HasFlag(FlagsEnum.Flag2); +#pragma warning restore RS0030 + } + + [Benchmark] + public bool BitwiseAnd() + { + return ((FlagsEnum.Flag2 | FlagsEnum.Flag3) & FlagsEnum.Flag2) > 0; + } + + [Benchmark] + public bool HasFlagFast() + { + return (FlagsEnum.Flag2 | FlagsEnum.Flag3).HasFlagFast(FlagsEnum.Flag2); + } + + [Flags] + private enum FlagsEnum + { + Flag1 = 1, + Flag2 = 2, + Flag3 = 4, + Flag4 = 8 + } + } +} diff --git a/osu.Framework.Tests/Visual/Containers/TestSceneSafeAreaOverrides.cs b/osu.Framework.Tests/Visual/Containers/TestSceneSafeAreaOverrides.cs index c3074db0a3..2c77397310 100644 --- a/osu.Framework.Tests/Visual/Containers/TestSceneSafeAreaOverrides.cs +++ b/osu.Framework.Tests/Visual/Containers/TestSceneSafeAreaOverrides.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -50,10 +51,10 @@ public TestSceneSafeAreaOverrides() private void addBoxAssert(OverrideTestContainer container) { - bool leftOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlag(Edges.Left); - bool topOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlag(Edges.Top); - bool rightOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlag(Edges.Right); - bool bottomOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlag(Edges.Bottom); + bool leftOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlagFast(Edges.Left); + bool topOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlagFast(Edges.Top); + bool rightOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlagFast(Edges.Right); + bool bottomOverridden = container.SafeAreaContainer.SafeAreaOverrideEdges.HasFlagFast(Edges.Bottom); AddAssert($"\"{container.Name}\" overrides correctly", () => leftOverridden == container.SafeAreaContainer.Padding.Left < 0 diff --git a/osu.Framework.Tests/Visual/Drawables/TestSceneIsMaskedAway.cs b/osu.Framework.Tests/Visual/Drawables/TestSceneIsMaskedAway.cs index 0bdfdf8c7d..c4d1aca68b 100644 --- a/osu.Framework.Tests/Visual/Drawables/TestSceneIsMaskedAway.cs +++ b/osu.Framework.Tests/Visual/Drawables/TestSceneIsMaskedAway.cs @@ -4,6 +4,7 @@ #nullable disable using NUnit.Framework; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -84,7 +85,7 @@ public void TestBoxSlightlyOutOfBoundsMasking(Anchor anchor) Anchor = anchor, Origin = anchor, Size = new Vector2(10), - Position = new Vector2(anchor.HasFlag(Anchor.x0) ? -5 : 5, anchor.HasFlag(Anchor.y0) ? -5 : 5), + Position = new Vector2(anchor.HasFlagFast(Anchor.x0) ? -5 : 5, anchor.HasFlagFast(Anchor.y0) ? -5 : 5), } }; }); @@ -117,7 +118,7 @@ public void TestBoxFullyOutOfBoundsMasking(Anchor anchor) Anchor = anchor, Origin = anchor, Size = new Vector2(10), - Position = new Vector2(anchor.HasFlag(Anchor.x0) ? -20 : 20, anchor.HasFlag(Anchor.y0) ? -20 : 20), + Position = new Vector2(anchor.HasFlagFast(Anchor.x0) ? -20 : 20, anchor.HasFlagFast(Anchor.y0) ? -20 : 20), } }; }); diff --git a/osu.Framework.Tests/Visual/UserInterface/TestSceneContextMenu.cs b/osu.Framework.Tests/Visual/UserInterface/TestSceneContextMenu.cs index 28241a50ab..20bd8ac97a 100644 --- a/osu.Framework.Tests/Visual/UserInterface/TestSceneContextMenu.cs +++ b/osu.Framework.Tests/Visual/UserInterface/TestSceneContextMenu.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -199,14 +200,14 @@ public void TestMenuNotOnScreenWhenTargetSignificantlyOffScreen(Anchor anchor) { box.Anchor = anchor; - if (anchor.HasFlag(Anchor.x0)) + if (anchor.HasFlagFast(Anchor.x0)) box.X -= contextMenuContainer.CurrentMenu.DrawWidth + 10; - else if (anchor.HasFlag(Anchor.x2)) + else if (anchor.HasFlagFast(Anchor.x2)) box.X += 10; - if (anchor.HasFlag(Anchor.y0)) + if (anchor.HasFlagFast(Anchor.y0)) box.Y -= contextMenuContainer.CurrentMenu.DrawHeight + 10; - else if (anchor.HasFlag(Anchor.y2)) + else if (anchor.HasFlagFast(Anchor.y2)) box.Y += 10; }); diff --git a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs index a564a6b20a..07147af650 100644 --- a/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs +++ b/osu.Framework/Audio/Mixing/Bass/BassAudioMixer.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using ManagedBass; using ManagedBass.Mix; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Statistics; namespace osu.Framework.Audio.Mixing.Bass @@ -158,7 +159,7 @@ public PlaybackState ChannelIsActive(IBassAudioChannel channel) // The channel is always in a playing state unless stopped or stalled as it's a decoding channel. Retrieve the true playing state from the mixer channel. if (state == PlaybackState.Playing) - state = BassMix.ChannelFlags(channel.Handle, BassFlags.Default, BassFlags.Default).HasFlag(BassFlags.MixerChanPause) ? PlaybackState.Paused : state; + state = BassMix.ChannelFlags(channel.Handle, BassFlags.Default, BassFlags.Default).HasFlagFast(BassFlags.MixerChanPause) ? PlaybackState.Paused : state; return state; } diff --git a/osu.Framework/Extensions/AnchorExtensions.cs b/osu.Framework/Extensions/AnchorExtensions.cs index fcc64f4f63..08aee6e906 100644 --- a/osu.Framework/Extensions/AnchorExtensions.cs +++ b/osu.Framework/Extensions/AnchorExtensions.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Primitives; using osuTK; @@ -21,9 +22,9 @@ public static Anchor Opposite(this Anchor anchor) if (anchor == Anchor.Custom) throw new ArgumentException($"{nameof(Anchor.Custom)} is not supported.", nameof(anchor)); - if (anchor.HasFlag(Anchor.x0) || anchor.HasFlag(Anchor.x2)) + if (anchor.HasFlagFast(Anchor.x0) || anchor.HasFlagFast(Anchor.x2)) anchor ^= Anchor.x0 | Anchor.x2; - if (anchor.HasFlag(Anchor.y0) || anchor.HasFlag(Anchor.y2)) + if (anchor.HasFlagFast(Anchor.y0) || anchor.HasFlagFast(Anchor.y2)) anchor ^= Anchor.y0 | Anchor.y2; return anchor; @@ -39,18 +40,18 @@ public static Vector2 PositionOnQuad(this Anchor anchor, Quad quad) Vector2 position = new Vector2(); - if (anchor.HasFlag(Anchor.x0)) + if (anchor.HasFlagFast(Anchor.x0)) position.X = quad.TopLeft.X; - else if (anchor.HasFlag(Anchor.x1)) + else if (anchor.HasFlagFast(Anchor.x1)) position.X = quad.Centre.X; - else if (anchor.HasFlag(Anchor.x2)) + else if (anchor.HasFlagFast(Anchor.x2)) position.X = quad.BottomRight.X; - if (anchor.HasFlag(Anchor.y0)) + if (anchor.HasFlagFast(Anchor.y0)) position.Y = quad.TopLeft.Y; - else if (anchor.HasFlag(Anchor.y1)) + else if (anchor.HasFlagFast(Anchor.y1)) position.Y = quad.Centre.Y; - else if (anchor.HasFlag(Anchor.y2)) + else if (anchor.HasFlagFast(Anchor.y2)) position.Y = quad.BottomRight.Y; return position; diff --git a/osu.Framework/Extensions/EnumExtensions/EnumExtensions.cs b/osu.Framework/Extensions/EnumExtensions/EnumExtensions.cs index 8d573b8c2e..ea3e599b35 100644 --- a/osu.Framework/Extensions/EnumExtensions/EnumExtensions.cs +++ b/osu.Framework/Extensions/EnumExtensions/EnumExtensions.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Utils; @@ -54,5 +55,48 @@ public static IEnumerable GetValuesInOrder(this IEnumerable items) throw new ArgumentException($"Not all values of {typeof(T)} have {nameof(OrderAttribute)} specified."); }); } + +#pragma warning disable RS0030 // (banned API) + /// + /// A fast alternative functionally equivalent to , eliminating boxing in all scenarios. + /// + /// The enum to check. + /// The flag to check for. +#pragma warning restore RS0030 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe bool HasFlagFast(this T enumValue, T flag) where T : unmanaged, Enum + { + // Note: Using a switch statement would eliminate inlining. + + if (sizeof(T) == 1) + { + byte value1 = Unsafe.As(ref enumValue); + byte value2 = Unsafe.As(ref flag); + return (value1 & value2) == value2; + } + + if (sizeof(T) == 2) + { + short value1 = Unsafe.As(ref enumValue); + short value2 = Unsafe.As(ref flag); + return (value1 & value2) == value2; + } + + if (sizeof(T) == 4) + { + int value1 = Unsafe.As(ref enumValue); + int value2 = Unsafe.As(ref flag); + return (value1 & value2) == value2; + } + + if (sizeof(T) == 8) + { + long value1 = Unsafe.As(ref enumValue); + long value2 = Unsafe.As(ref flag); + return (value1 & value2) == value2; + } + + throw new ArgumentException($"Invalid enum type provided: {typeof(T)}."); + } } } diff --git a/osu.Framework/Extensions/TaskExtensions.cs b/osu.Framework/Extensions/TaskExtensions.cs index 1fe6b8551b..7f27bb3755 100644 --- a/osu.Framework/Extensions/TaskExtensions.cs +++ b/osu.Framework/Extensions/TaskExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Framework.Extensions { @@ -42,7 +43,7 @@ public static T GetResultSafely(this Task task) private static bool isWaitingValid(Task task) { // In the case the task has been started with the LongRunning flag, it will not be in the TPL thread pool and we can allow waiting regardless. - if (task.CreationOptions.HasFlag(TaskCreationOptions.LongRunning)) + if (task.CreationOptions.HasFlagFast(TaskCreationOptions.LongRunning)) return true; // Otherwise only allow waiting from a non-TPL thread pool thread. diff --git a/osu.Framework/Graphics/Containers/AudioContainer.cs b/osu.Framework/Graphics/Containers/AudioContainer.cs index 526659581b..f07c5dfcc7 100644 --- a/osu.Framework/Graphics/Containers/AudioContainer.cs +++ b/osu.Framework/Graphics/Containers/AudioContainer.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using osu.Framework.Audio; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Effects; using osuTK; @@ -40,8 +41,8 @@ public override Vector2 Size set { base.Size = new Vector2( - RelativeSizeAxes.HasFlag(Axes.X) ? 1 : value.X, - RelativeSizeAxes.HasFlag(Axes.Y) ? 1 : value.Y); + RelativeSizeAxes.HasFlagFast(Axes.X) ? 1 : value.X, + RelativeSizeAxes.HasFlagFast(Axes.Y) ? 1 : value.Y); container.Size = value; } diff --git a/osu.Framework/Graphics/Containers/CompositeDrawable.cs b/osu.Framework/Graphics/Containers/CompositeDrawable.cs index b76a5d96d5..b113bfb35e 100644 --- a/osu.Framework/Graphics/Containers/CompositeDrawable.cs +++ b/osu.Framework/Graphics/Containers/CompositeDrawable.cs @@ -23,6 +23,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Development; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ExceptionExtensions; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Primitives; @@ -722,9 +723,9 @@ protected virtual bool CheckChildrenLife() { var state = checkChildLife(internalChildren[i]); - anyAliveChanged |= state.HasFlag(ChildLifeStateChange.MadeAlive) || state.HasFlag(ChildLifeStateChange.MadeDead); + anyAliveChanged |= state.HasFlagFast(ChildLifeStateChange.MadeAlive) || state.HasFlagFast(ChildLifeStateChange.MadeDead); - if (state.HasFlag(ChildLifeStateChange.Removed)) + if (state.HasFlagFast(ChildLifeStateChange.Removed)) i--; } @@ -1839,7 +1840,7 @@ public override float Width { get { - if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.X)) + if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlagFast(Axes.X)) updateChildrenSizeDependencies(); return base.Width; } @@ -1857,7 +1858,7 @@ public override float Height { get { - if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.Y)) + if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlagFast(Axes.Y)) updateChildrenSizeDependencies(); return base.Height; } @@ -1913,16 +1914,16 @@ private Vector2 computeAutoSize() Vector2 cBound = c.RequiredParentSizeToFit; - if (!c.BypassAutoSizeAxes.HasFlag(Axes.X)) + if (!c.BypassAutoSizeAxes.HasFlagFast(Axes.X)) maxBoundSize.X = Math.Max(maxBoundSize.X, cBound.X); - if (!c.BypassAutoSizeAxes.HasFlag(Axes.Y)) + if (!c.BypassAutoSizeAxes.HasFlagFast(Axes.Y)) maxBoundSize.Y = Math.Max(maxBoundSize.Y, cBound.Y); } - if (!AutoSizeAxes.HasFlag(Axes.X)) + if (!AutoSizeAxes.HasFlagFast(Axes.X)) maxBoundSize.X = DrawSize.X; - if (!AutoSizeAxes.HasFlag(Axes.Y)) + if (!AutoSizeAxes.HasFlagFast(Axes.Y)) maxBoundSize.Y = DrawSize.Y; return new Vector2(maxBoundSize.X, maxBoundSize.Y); @@ -1942,8 +1943,8 @@ private void updateAutoSize() Vector2 b = computeAutoSize() + Padding.Total; autoSizeResizeTo(new Vector2( - AutoSizeAxes.HasFlag(Axes.X) ? b.X : base.Width, - AutoSizeAxes.HasFlag(Axes.Y) ? b.Y : base.Height + AutoSizeAxes.HasFlagFast(Axes.X) ? b.X : base.Width, + AutoSizeAxes.HasFlagFast(Axes.Y) ? b.Y : base.Height ), AutoSizeDuration, AutoSizeEasing); //note that this is called before autoSize becomes valid. may be something to consider down the line. diff --git a/osu.Framework/Graphics/Containers/ContainerExtensions.cs b/osu.Framework/Graphics/Containers/ContainerExtensions.cs index 546a33ec1e..72730b190c 100644 --- a/osu.Framework/Graphics/Containers/ContainerExtensions.cs +++ b/osu.Framework/Graphics/Containers/ContainerExtensions.cs @@ -4,6 +4,7 @@ using osuTK; using System; using System.Collections.Generic; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Framework.Graphics.Containers { @@ -41,13 +42,13 @@ public static TContainer Wrap(this TContainer container, TCh // For anchor/origin positioning to be preserved correctly, // relatively sized axes must be lifted to the wrapping container. - if (container.RelativeSizeAxes.HasFlag(Axes.X)) + if (container.RelativeSizeAxes.HasFlagFast(Axes.X)) { container.Width = drawable.Width; drawable.Width = 1; } - if (container.RelativeSizeAxes.HasFlag(Axes.Y)) + if (container.RelativeSizeAxes.HasFlagFast(Axes.Y)) { container.Height = drawable.Height; drawable.Height = 1; diff --git a/osu.Framework/Graphics/Containers/FillFlowContainer.cs b/osu.Framework/Graphics/Containers/FillFlowContainer.cs index b93a8e71d7..b06852e85c 100644 --- a/osu.Framework/Graphics/Containers/FillFlowContainer.cs +++ b/osu.Framework/Graphics/Containers/FillFlowContainer.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections.Generic; using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Utils; using osuTK; @@ -88,9 +89,9 @@ public Vector2 Spacing private Vector2 spacingFactor(Drawable c) { Vector2 result = c.RelativeOriginPosition; - if (c.Anchor.HasFlag(Anchor.x2)) + if (c.Anchor.HasFlagFast(Anchor.x2)) result.X = 1 - result.X; - if (c.Anchor.HasFlag(Anchor.y2)) + if (c.Anchor.HasFlagFast(Anchor.y2)) result.Y = 1 - result.Y; return result; } @@ -105,8 +106,8 @@ protected override IEnumerable ComputeLayoutPositions() // If we are autosize and haven't specified a maximum size, we should allow infinite expansion. // If we are inheriting then we need to use the parent size (our ActualSize). - max.X = AutoSizeAxes.HasFlag(Axes.X) ? float.MaxValue : s.X; - max.Y = AutoSizeAxes.HasFlag(Axes.Y) ? float.MaxValue : s.Y; + max.X = AutoSizeAxes.HasFlagFast(Axes.X) ? float.MaxValue : s.X; + max.Y = AutoSizeAxes.HasFlagFast(Axes.Y) ? float.MaxValue : s.Y; } var children = FlowingChildren.ToArray(); @@ -269,17 +270,17 @@ static Axes toAxes(FillDirection direction) } var layoutPosition = layoutPositions[i]; - if (c.Anchor.HasFlag(Anchor.x1)) + if (c.Anchor.HasFlagFast(Anchor.x1)) // Begin flow at centre of row layoutPosition.X += rowOffsetsToMiddle[rowIndices[i]]; - else if (c.Anchor.HasFlag(Anchor.x2)) + else if (c.Anchor.HasFlagFast(Anchor.x2)) // Flow right-to-left layoutPosition.X = -layoutPosition.X; - if (c.Anchor.HasFlag(Anchor.y1)) + if (c.Anchor.HasFlagFast(Anchor.y1)) // Begin flow at centre of total height layoutPosition.Y -= height / 2; - else if (c.Anchor.HasFlag(Anchor.y2)) + else if (c.Anchor.HasFlagFast(Anchor.y2)) // Flow bottom-to-top layoutPosition.Y = -layoutPosition.Y; diff --git a/osu.Framework/Graphics/Containers/GridContainer.cs b/osu.Framework/Graphics/Containers/GridContainer.cs index d247f5acf0..b7d53df875 100644 --- a/osu.Framework/Graphics/Containers/GridContainer.cs +++ b/osu.Framework/Graphics/Containers/GridContainer.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Caching; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Layout; using osuTK; @@ -283,7 +284,7 @@ private float[] getCellSizesAlongAxis(Axes axis, float spanLength) for (int r = 0; r < cellRows; r++) { var cell = Content[r]?[i]; - if (cell == null || cell.RelativeSizeAxes.HasFlag(axis)) + if (cell == null || cell.RelativeSizeAxes.HasFlagFast(axis)) continue; size = Math.Max(size, getCellWidth(cell)); @@ -295,7 +296,7 @@ private float[] getCellSizesAlongAxis(Axes axis, float spanLength) for (int c = 0; c < cellColumns; c++) { var cell = Content[i]?[c]; - if (cell == null || cell.RelativeSizeAxes.HasFlag(axis)) + if (cell == null || cell.RelativeSizeAxes.HasFlagFast(axis)) continue; size = Math.Max(size, getCellHeight(cell)); diff --git a/osu.Framework/Graphics/Containers/Markdown/MarkdownContainer.cs b/osu.Framework/Graphics/Containers/Markdown/MarkdownContainer.cs index c66237e1a6..203773e4f5 100644 --- a/osu.Framework/Graphics/Containers/Markdown/MarkdownContainer.cs +++ b/osu.Framework/Graphics/Containers/Markdown/MarkdownContainer.cs @@ -13,6 +13,7 @@ using Markdig.Syntax.Inlines; using osu.Framework.Allocation; using osu.Framework.Caching; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers.Markdown.Footnotes; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; @@ -40,7 +41,7 @@ public partial class MarkdownContainer : CompositeDrawable, IMarkdownTextCompone get => base.AutoSizeAxes; set { - if (value.HasFlag(Axes.X)) + if (value.HasFlagFast(Axes.X)) throw new ArgumentException($"{nameof(MarkdownContainer)} does not support an {nameof(AutoSizeAxes)} of {value}"); base.AutoSizeAxes = value; diff --git a/osu.Framework/Graphics/Containers/SafeAreaContainer.cs b/osu.Framework/Graphics/Containers/SafeAreaContainer.cs index c8e521b0bb..40647ffd55 100644 --- a/osu.Framework/Graphics/Containers/SafeAreaContainer.cs +++ b/osu.Framework/Graphics/Containers/SafeAreaContainer.cs @@ -6,6 +6,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Layout; using osuTK; @@ -81,10 +82,10 @@ private MarginPadding getPadding() return new MarginPadding { - Left = SafeAreaOverrideEdges.HasFlag(Edges.Left) ? nonSafeLocalSpace.TopLeft.X : Math.Clamp(localTopLeft.X, 0, absDrawSize.X), - Right = SafeAreaOverrideEdges.HasFlag(Edges.Right) ? DrawRectangle.BottomRight.X - nonSafeLocalSpace.BottomRight.X : Math.Clamp(absDrawSize.X - localBottomRight.X, 0, absDrawSize.X), - Top = SafeAreaOverrideEdges.HasFlag(Edges.Top) ? nonSafeLocalSpace.TopLeft.Y : Math.Clamp(localTopLeft.Y, 0, absDrawSize.Y), - Bottom = SafeAreaOverrideEdges.HasFlag(Edges.Bottom) ? DrawRectangle.BottomRight.Y - nonSafeLocalSpace.BottomRight.Y : Math.Clamp(absDrawSize.Y - localBottomRight.Y, 0, absDrawSize.Y) + Left = SafeAreaOverrideEdges.HasFlagFast(Edges.Left) ? nonSafeLocalSpace.TopLeft.X : Math.Clamp(localTopLeft.X, 0, absDrawSize.X), + Right = SafeAreaOverrideEdges.HasFlagFast(Edges.Right) ? DrawRectangle.BottomRight.X - nonSafeLocalSpace.BottomRight.X : Math.Clamp(absDrawSize.X - localBottomRight.X, 0, absDrawSize.X), + Top = SafeAreaOverrideEdges.HasFlagFast(Edges.Top) ? nonSafeLocalSpace.TopLeft.Y : Math.Clamp(localTopLeft.Y, 0, absDrawSize.Y), + Bottom = SafeAreaOverrideEdges.HasFlagFast(Edges.Bottom) ? DrawRectangle.BottomRight.Y - nonSafeLocalSpace.BottomRight.Y : Math.Clamp(absDrawSize.Y - localBottomRight.Y, 0, absDrawSize.Y) }; } } diff --git a/osu.Framework/Graphics/Containers/TextFlowContainer.cs b/osu.Framework/Graphics/Containers/TextFlowContainer.cs index bcb098b8d8..1b8ed811b3 100644 --- a/osu.Framework/Graphics/Containers/TextFlowContainer.cs +++ b/osu.Framework/Graphics/Containers/TextFlowContainer.cs @@ -10,6 +10,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Localisation; @@ -206,7 +207,7 @@ protected override int Compare(Drawable x, Drawable y) { // FillFlowContainer will reverse the ordering of right-anchored words such that the (previously) first word would be // the right-most word, whereas it should still be flowed left-to-right. This is achieved by reversing the comparator. - if (TextAnchor.HasFlag(Anchor.x2)) + if (TextAnchor.HasFlagFast(Anchor.x2)) return base.Compare(y, x); return base.Compare(x, y); diff --git a/osu.Framework/Graphics/Cursor/PopoverContainer.cs b/osu.Framework/Graphics/Cursor/PopoverContainer.cs index 86efdee13a..87cb32cc5d 100644 --- a/osu.Framework/Graphics/Cursor/PopoverContainer.cs +++ b/osu.Framework/Graphics/Cursor/PopoverContainer.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; @@ -156,18 +157,18 @@ private Vector2 availableSizeAroundTargetForAnchor(Quad targetLocalQuad, Anchor // left anchor = area to the left of the quad, right anchor = area to the right of the quad. // for horizontal centre assume we have the whole quad width to work with. - if (anchor.HasFlag(Anchor.x0)) + if (anchor.HasFlagFast(Anchor.x0)) availableSize.X = MathF.Max(0, targetLocalQuad.TopLeft.X); - else if (anchor.HasFlag(Anchor.x2)) + else if (anchor.HasFlagFast(Anchor.x2)) availableSize.X = MathF.Max(0, DrawWidth - targetLocalQuad.BottomRight.X); else availableSize.X = DrawWidth; // top anchor = area above quad, bottom anchor = area below quad. // for vertical centre assume we have the whole quad height to work with. - if (anchor.HasFlag(Anchor.y0)) + if (anchor.HasFlagFast(Anchor.y0)) availableSize.Y = MathF.Max(0, targetLocalQuad.TopLeft.Y); - else if (anchor.HasFlag(Anchor.y2)) + else if (anchor.HasFlagFast(Anchor.y2)) availableSize.Y = MathF.Max(0, DrawHeight - targetLocalQuad.BottomRight.Y); else availableSize.Y = DrawHeight; diff --git a/osu.Framework/Graphics/Drawable.cs b/osu.Framework/Graphics/Drawable.cs index 4ef0ec1a8b..0ef037c605 100644 --- a/osu.Framework/Graphics/Drawable.cs +++ b/osu.Framework/Graphics/Drawable.cs @@ -28,6 +28,7 @@ using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Development; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Input.Events; using osu.Framework.Input.States; @@ -682,10 +683,10 @@ public Vector2 DrawPosition { offset = Parent.RelativeChildOffset; - if (!RelativePositionAxes.HasFlag(Axes.X)) + if (!RelativePositionAxes.HasFlagFast(Axes.X)) offset.X = 0; - if (!RelativePositionAxes.HasFlag(Axes.Y)) + if (!RelativePositionAxes.HasFlagFast(Axes.Y)) offset.Y = 0; } @@ -811,8 +812,8 @@ public virtual Axes RelativeSizeAxes relativeSizeAxes = value; - if (relativeSizeAxes.HasFlag(Axes.X) && Width == 0) Width = 1; - if (relativeSizeAxes.HasFlag(Axes.Y) && Height == 0) Height = 1; + if (relativeSizeAxes.HasFlagFast(Axes.X) && Width == 0) Width = 1; + if (relativeSizeAxes.HasFlagFast(Axes.Y) && Height == 0) Height = 1; updateBypassAutoSizeAxes(); @@ -906,9 +907,9 @@ protected Vector2 ApplyRelativeAxes(Axes relativeAxes, Vector2 v, FillMode fillM { Vector2 conversion = relativeToAbsoluteFactor; - if (relativeAxes.HasFlag(Axes.X)) + if (relativeAxes.HasFlagFast(Axes.X)) v.X *= conversion.X; - if (relativeAxes.HasFlag(Axes.Y)) + if (relativeAxes.HasFlagFast(Axes.Y)) v.Y *= conversion.Y; // FillMode only makes sense if both axes are relatively sized as the general rule @@ -1136,14 +1137,14 @@ public Vector2 RelativeOriginPosition throw new InvalidOperationException(@"Can not obtain relative origin position for custom origins."); Vector2 result = Vector2.Zero; - if (origin.HasFlag(Anchor.x1)) + if (origin.HasFlagFast(Anchor.x1)) result.X = 0.5f; - else if (origin.HasFlag(Anchor.x2)) + else if (origin.HasFlagFast(Anchor.x2)) result.X = 1; - if (origin.HasFlag(Anchor.y1)) + if (origin.HasFlagFast(Anchor.y1)) result.Y = 0.5f; - else if (origin.HasFlag(Anchor.y2)) + else if (origin.HasFlagFast(Anchor.y2)) result.Y = 1; return result; @@ -1224,14 +1225,14 @@ public Vector2 RelativeAnchorPosition return customRelativeAnchorPosition; Vector2 result = Vector2.Zero; - if (anchor.HasFlag(Anchor.x1)) + if (anchor.HasFlagFast(Anchor.x1)) result.X = 0.5f; - else if (anchor.HasFlag(Anchor.x2)) + else if (anchor.HasFlagFast(Anchor.x2)) result.X = 1; - if (anchor.HasFlag(Anchor.y1)) + if (anchor.HasFlagFast(Anchor.y1)) result.Y = 0.5f; - else if (anchor.HasFlag(Anchor.y2)) + else if (anchor.HasFlagFast(Anchor.y2)) result.Y = 1; return result; @@ -1269,14 +1270,14 @@ private static Vector2 computeAnchorPosition(Vector2 size, Anchor anchor) { Vector2 result = Vector2.Zero; - if (anchor.HasFlag(Anchor.x1)) + if (anchor.HasFlagFast(Anchor.x1)) result.X = size.X / 2f; - else if (anchor.HasFlag(Anchor.x2)) + else if (anchor.HasFlagFast(Anchor.x2)) result.X = size.X; - if (anchor.HasFlag(Anchor.y1)) + if (anchor.HasFlagFast(Anchor.y1)) result.Y = size.Y / 2f; - else if (anchor.HasFlag(Anchor.y2)) + else if (anchor.HasFlagFast(Anchor.y2)) result.Y = size.Y; return result; diff --git a/osu.Framework/Graphics/Lines/Path.cs b/osu.Framework/Graphics/Lines/Path.cs index 4045252b9e..85c69475d9 100644 --- a/osu.Framework/Graphics/Lines/Path.cs +++ b/osu.Framework/Graphics/Lines/Path.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using System.Collections.Generic; using osu.Framework.Caching; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Layout; using osuTK.Graphics; @@ -117,7 +118,7 @@ public override float Width { get { - if (AutoSizeAxes.HasFlag(Axes.X)) + if (AutoSizeAxes.HasFlagFast(Axes.X)) return base.Width = vertexBounds.Width; return base.Width; @@ -135,7 +136,7 @@ public override float Height { get { - if (AutoSizeAxes.HasFlag(Axes.Y)) + if (AutoSizeAxes.HasFlagFast(Axes.Y)) return base.Height = vertexBounds.Height; return base.Height; diff --git a/osu.Framework/Graphics/OpenGL/GLRenderer.cs b/osu.Framework/Graphics/OpenGL/GLRenderer.cs index d6e2e9cc17..338cff27f3 100644 --- a/osu.Framework/Graphics/OpenGL/GLRenderer.cs +++ b/osu.Framework/Graphics/OpenGL/GLRenderer.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text; using System.Runtime.InteropServices; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.OpenGL.Buffers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.OpenGL.Batches; @@ -324,10 +325,10 @@ protected override void SetBlendImplementation(BlendingParameters blendingParame protected override void SetBlendMaskImplementation(BlendingMask blendingMask) { - GL.ColorMask(blendingMask.HasFlag(BlendingMask.Red), - blendingMask.HasFlag(BlendingMask.Green), - blendingMask.HasFlag(BlendingMask.Blue), - blendingMask.HasFlag(BlendingMask.Alpha)); + GL.ColorMask(blendingMask.HasFlagFast(BlendingMask.Red), + blendingMask.HasFlagFast(BlendingMask.Green), + blendingMask.HasFlagFast(BlendingMask.Blue), + blendingMask.HasFlagFast(BlendingMask.Alpha)); } protected override void SetViewportImplementation(RectangleI viewport) => GL.Viewport(viewport.Left, viewport.Top, viewport.Width, viewport.Height); diff --git a/osu.Framework/Graphics/Pooling/PoolableDrawable.cs b/osu.Framework/Graphics/Pooling/PoolableDrawable.cs index ee0492b6e2..d5e8f831b9 100644 --- a/osu.Framework/Graphics/Pooling/PoolableDrawable.cs +++ b/osu.Framework/Graphics/Pooling/PoolableDrawable.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Layout; @@ -117,7 +118,7 @@ protected override void Update() protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) { - if (source != InvalidationSource.Child && invalidation.HasFlag(Invalidation.Parent)) + if (source != InvalidationSource.Child && invalidation.HasFlagFast(Invalidation.Parent)) { if (IsInUse && Parent == null) Return(); diff --git a/osu.Framework/Graphics/Textures/Texture.cs b/osu.Framework/Graphics/Textures/Texture.cs index 45da895294..5b1efd149e 100644 --- a/osu.Framework/Graphics/Textures/Texture.cs +++ b/osu.Framework/Graphics/Textures/Texture.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Visualisation; @@ -95,8 +96,8 @@ public Texture Crop(RectangleF cropRectangle, Axes relativeSizeAxes = Axes.None, if (relativeSizeAxes != Axes.None) { Vector2 scale = new Vector2( - relativeSizeAxes.HasFlag(Axes.X) ? Width : 1, - relativeSizeAxes.HasFlag(Axes.Y) ? Height : 1 + relativeSizeAxes.HasFlagFast(Axes.X) ? Width : 1, + relativeSizeAxes.HasFlagFast(Axes.Y) ? Height : 1 ); cropRectangle *= scale; } diff --git a/osu.Framework/Graphics/UserInterface/BasicPopover.cs b/osu.Framework/Graphics/UserInterface/BasicPopover.cs index 1defca3b8f..39514d0e6b 100644 --- a/osu.Framework/Graphics/UserInterface/BasicPopover.cs +++ b/osu.Framework/Graphics/UserInterface/BasicPopover.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Shapes; using osuTK; @@ -24,7 +25,7 @@ protected override void AnchorUpdated(Anchor anchor) { base.AnchorUpdated(anchor); - bool isCenteredAnchor = anchor.HasFlag(Anchor.x1) || anchor.HasFlag(Anchor.y1); + bool isCenteredAnchor = anchor.HasFlagFast(Anchor.x1) || anchor.HasFlagFast(Anchor.y1); Body.Margin = new MarginPadding(isCenteredAnchor ? 10 : 3); Arrow.Size = new Vector2(isCenteredAnchor ? 12 : 15); } diff --git a/osu.Framework/Graphics/UserInterface/DirectorySelector.cs b/osu.Framework/Graphics/UserInterface/DirectorySelector.cs index e6dbcb31a5..228e5a7d87 100644 --- a/osu.Framework/Graphics/UserInterface/DirectorySelector.cs +++ b/osu.Framework/Graphics/UserInterface/DirectorySelector.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Platform; using osuTK; @@ -205,7 +206,7 @@ protected virtual bool TryGetEntriesForPath(DirectoryInfo path, out ICollection< { foreach (var dir in path.GetDirectories().OrderBy(d => d.Name)) { - if (ShowHiddenItems.Value || !dir.Attributes.HasFlag(FileAttributes.Hidden)) + if (ShowHiddenItems.Value || !dir.Attributes.HasFlagFast(FileAttributes.Hidden)) items.Add(CreateDirectoryItem(dir)); } diff --git a/osu.Framework/Graphics/UserInterface/DirectorySelectorDirectory.cs b/osu.Framework/Graphics/UserInterface/DirectorySelectorDirectory.cs index 9f278a252e..2fd30a135e 100644 --- a/osu.Framework/Graphics/UserInterface/DirectorySelectorDirectory.cs +++ b/osu.Framework/Graphics/UserInterface/DirectorySelectorDirectory.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Events; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Framework.Graphics.UserInterface { @@ -26,7 +27,7 @@ protected DirectorySelectorDirectory(DirectoryInfo directory, string displayName try { - bool isHidden = directory?.Attributes.HasFlag(FileAttributes.Hidden) == true; + bool isHidden = directory?.Attributes.HasFlagFast(FileAttributes.Hidden) == true; // On Windows, system drives are returned with `System | Hidden | Directory` file attributes, // but the expectation is that they shouldn't be shown in a hidden state. diff --git a/osu.Framework/Graphics/UserInterface/FileSelector.cs b/osu.Framework/Graphics/UserInterface/FileSelector.cs index 0040132616..774053c556 100644 --- a/osu.Framework/Graphics/UserInterface/FileSelector.cs +++ b/osu.Framework/Graphics/UserInterface/FileSelector.cs @@ -9,6 +9,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Input.Events; namespace osu.Framework.Graphics.UserInterface @@ -48,7 +49,7 @@ protected override bool TryGetEntriesForPath(DirectoryInfo path, out ICollection foreach (var file in files.OrderBy(d => d.Name)) { - if (ShowHiddenItems.Value || !file.Attributes.HasFlag(FileAttributes.Hidden)) + if (ShowHiddenItems.Value || !file.Attributes.HasFlagFast(FileAttributes.Hidden)) items.Add(CreateFileItem(file)); } @@ -73,7 +74,7 @@ protected DirectoryListingFile(FileInfo file) try { - if (File?.Attributes.HasFlag(FileAttributes.Hidden) == true) + if (File?.Attributes.HasFlagFast(FileAttributes.Hidden) == true) ApplyHiddenState(); } catch (UnauthorizedAccessException) diff --git a/osu.Framework/Graphics/UserInterface/Menu.cs b/osu.Framework/Graphics/UserInterface/Menu.cs index 72049a38e5..ac178cf5ba 100644 --- a/osu.Framework/Graphics/UserInterface/Menu.cs +++ b/osu.Framework/Graphics/UserInterface/Menu.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.ObjectExtensions; using osuTK.Graphics; @@ -497,8 +498,8 @@ protected override void UpdateAfterChildren() height = Math.Min(MaxHeight, height); // Regardless of the above result, if we are relative-sizing, just use the stored width/height - width = RelativeSizeAxes.HasFlag(Axes.X) ? Width : width; - height = RelativeSizeAxes.HasFlag(Axes.Y) ? Height : height; + width = RelativeSizeAxes.HasFlagFast(Axes.X) ? Width : width; + height = RelativeSizeAxes.HasFlagFast(Axes.Y) ? Height : height; if (State == MenuState.Closed && Direction == Direction.Horizontal) width = 0; diff --git a/osu.Framework/Graphics/UserInterface/Popover.cs b/osu.Framework/Graphics/UserInterface/Popover.cs index b224d16d0b..48e60e5ee0 100644 --- a/osu.Framework/Graphics/UserInterface/Popover.cs +++ b/osu.Framework/Graphics/UserInterface/Popover.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using osu.Framework.Extensions; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Primitives; @@ -191,7 +192,7 @@ private float getRotationFor(Anchor anchor) get => Body.Width; set { - if (Body.AutoSizeAxes.HasFlag(Axes.X)) + if (Body.AutoSizeAxes.HasFlagFast(Axes.X)) Body.AutoSizeAxes &= ~Axes.X; Body.Width = value; @@ -203,7 +204,7 @@ private float getRotationFor(Anchor anchor) get => Body.Height; set { - if (Body.AutoSizeAxes.HasFlag(Axes.Y)) + if (Body.AutoSizeAxes.HasFlagFast(Axes.Y)) Body.AutoSizeAxes &= ~Axes.Y; Body.Height = value; diff --git a/osu.Framework/Graphics/UserInterface/TabControl.cs b/osu.Framework/Graphics/UserInterface/TabControl.cs index aea353056d..e4cc0dad5c 100644 --- a/osu.Framework/Graphics/UserInterface/TabControl.cs +++ b/osu.Framework/Graphics/UserInterface/TabControl.cs @@ -9,6 +9,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -171,8 +172,8 @@ protected TabControl() AddInternal(Dropdown); - Trace.Assert(Dropdown.Header.Anchor.HasFlag(Anchor.x2), $@"The {nameof(Dropdown)} implementation should use a right-based anchor inside a TabControl."); - Trace.Assert(!Dropdown.Header.RelativeSizeAxes.HasFlag(Axes.X), $@"The {nameof(Dropdown)} implementation's header should have a specific size."); + Trace.Assert(Dropdown.Header.Anchor.HasFlagFast(Anchor.x2), $@"The {nameof(Dropdown)} implementation should use a right-based anchor inside a TabControl."); + Trace.Assert(!Dropdown.Header.RelativeSizeAxes.HasFlagFast(Axes.X), $@"The {nameof(Dropdown)} implementation's header should have a specific size."); } AddInternal(TabContainer = CreateTabFlow()); diff --git a/osu.Framework/Graphics/Veldrid/VeldridExtensions.cs b/osu.Framework/Graphics/Veldrid/VeldridExtensions.cs index e0000401de..272452a14d 100644 --- a/osu.Framework/Graphics/Veldrid/VeldridExtensions.cs +++ b/osu.Framework/Graphics/Veldrid/VeldridExtensions.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Textures; @@ -105,10 +106,10 @@ public static ColorWriteMask ToColorWriteMask(this BlendingMask mask) { ColorWriteMask writeMask = ColorWriteMask.None; - if (mask.HasFlag(BlendingMask.Red)) writeMask |= ColorWriteMask.Red; - if (mask.HasFlag(BlendingMask.Green)) writeMask |= ColorWriteMask.Green; - if (mask.HasFlag(BlendingMask.Blue)) writeMask |= ColorWriteMask.Blue; - if (mask.HasFlag(BlendingMask.Alpha)) writeMask |= ColorWriteMask.Alpha; + if (mask.HasFlagFast(BlendingMask.Red)) writeMask |= ColorWriteMask.Red; + if (mask.HasFlagFast(BlendingMask.Green)) writeMask |= ColorWriteMask.Green; + if (mask.HasFlagFast(BlendingMask.Blue)) writeMask |= ColorWriteMask.Blue; + if (mask.HasFlagFast(BlendingMask.Alpha)) writeMask |= ColorWriteMask.Alpha; return writeMask; } diff --git a/osu.Framework/Graphics/Video/VideoDecoder.cs b/osu.Framework/Graphics/Video/VideoDecoder.cs index d9ff7fdbbb..62c87389ff 100644 --- a/osu.Framework/Graphics/Video/VideoDecoder.cs +++ b/osu.Framework/Graphics/Video/VideoDecoder.cs @@ -18,6 +18,7 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Rendering; using osu.Framework.Logging; using osu.Framework.Platform; @@ -795,7 +796,7 @@ HardwareVideoDecoder targetHwDecoders { var hwVideoDecoder = hwDeviceType.ToHardwareVideoDecoder(); - if (!hwVideoDecoder.HasValue || !targetHwDecoders.HasFlag(hwVideoDecoder.Value)) + if (!hwVideoDecoder.HasValue || !targetHwDecoders.HasFlagFast(hwVideoDecoder.Value)) continue; codecs.Add((codec, hwDeviceType)); diff --git a/osu.Framework/Input/Handlers/Mouse/MouseHandler.cs b/osu.Framework/Input/Handlers/Mouse/MouseHandler.cs index 2fc3d5fa1b..aa480152c3 100644 --- a/osu.Framework/Input/Handlers/Mouse/MouseHandler.cs +++ b/osu.Framework/Input/Handlers/Mouse/MouseHandler.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using osu.Framework.Bindables; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Input.StateChanges; using osu.Framework.Platform; using osu.Framework.Statistics; @@ -57,7 +58,7 @@ public class MouseHandler : InputHandler, IHasCursorSensitivity, INeedsMousePosi /// /// Whether the application should be handling the cursor. /// - private bool cursorCaptured => isActive.Value && (window.CursorInWindow.Value || window.CursorState.HasFlag(CursorState.Confined)); + private bool cursorCaptured => isActive.Value && (window.CursorInWindow.Value || window.CursorState.HasFlagFast(CursorState.Confined)); /// /// Whether the last position (as reported by ) @@ -163,7 +164,7 @@ public virtual void FeedbackMousePositionChange(Vector2 position, bool isSelfFee // handle the case where relative / raw input is active, but the cursor may have exited the window // bounds and is not intended to be confined. - if (!window.CursorState.HasFlag(CursorState.Confined) && positionOutsideWindow && !previousPositionOutsideWindow) + if (!window.CursorState.HasFlagFast(CursorState.Confined) && positionOutsideWindow && !previousPositionOutsideWindow) { // setting relative mode to false will allow the window manager to take control until the next // updateRelativeMode() call succeeds (likely from the cursor returning inside the window). @@ -193,7 +194,7 @@ private void updateRelativeMode() // relative mode only works when the window is active and the cursor is contained. aka the OS cursor isn't being displayed outside the window. && cursorCaptured // relative mode shouldn't ever be enabled if the framework or a consumer has chosen not to hide the cursor. - && window.CursorState.HasFlag(CursorState.Hidden); + && window.CursorState.HasFlagFast(CursorState.Hidden); if (!window.RelativeMouseMode) transferLastPositionToHostCursor(); diff --git a/osu.Framework/Input/MouseButtonEventManager.cs b/osu.Framework/Input/MouseButtonEventManager.cs index 4694adda77..988f69e8bf 100644 --- a/osu.Framework/Input/MouseButtonEventManager.cs +++ b/osu.Framework/Input/MouseButtonEventManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -217,7 +218,7 @@ private void handleDragDrawableBegin(Drawable drawable) private void draggedDrawableInvalidated(Drawable drawable, Invalidation invalidation) { - if (invalidation.HasFlag(Invalidation.Parent)) + if (invalidation.HasFlagFast(Invalidation.Parent)) { // end drag if no longer rooted. if (!drawable.IsRootedAt(InputManager)) diff --git a/osu.Framework/Input/UserInputManager.cs b/osu.Framework/Input/UserInputManager.cs index e0274014ac..c08f0b2f63 100644 --- a/osu.Framework/Input/UserInputManager.cs +++ b/osu.Framework/Input/UserInputManager.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Drawing; using osu.Framework.Configuration; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Input.Handlers; using osu.Framework.Input.StateChanges; using osu.Framework.Input.StateChanges.Events; @@ -44,7 +45,7 @@ public override void HandleInputStateChange(InputStateChangeEvent inputStateChan var clientSize = Host.Window.ClientSize; var windowRect = new RectangleF(0, 0, clientSize.Width, clientSize.Height); - if (Host.Window.CursorState.HasFlag(CursorState.Confined)) + if (Host.Window.CursorState.HasFlagFast(CursorState.Confined)) { cursorConfineRect = Host.Window.CursorConfineRect ?? windowRect; } diff --git a/osu.Framework/Platform/SDL2/SDL2Extensions.cs b/osu.Framework/Platform/SDL2/SDL2Extensions.cs index 9ce9978116..edfd63b6f8 100644 --- a/osu.Framework/Platform/SDL2/SDL2Extensions.cs +++ b/osu.Framework/Platform/SDL2/SDL2Extensions.cs @@ -6,6 +6,7 @@ using System; using System.Drawing; using System.Runtime.InteropServices; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -20,7 +21,7 @@ public static Key ToKey(this SDL_Keysym sdlKeysym) { // Apple devices don't have the notion of NumLock (they have a Clear key instead). // treat them as if they always have NumLock on (the numpad always performs its primary actions). - bool numLockOn = sdlKeysym.mod.HasFlag(SDL_Keymod.KMOD_NUM) || RuntimeInfo.IsApple; + bool numLockOn = sdlKeysym.mod.HasFlagFast(SDL_Keymod.KMOD_NUM) || RuntimeInfo.IsApple; switch (sdlKeysym.scancode) { @@ -863,17 +864,17 @@ public static SDL_Scancode ToScancode(this InputKey inputKey) public static WindowState ToWindowState(this SDL_WindowFlags windowFlags) { - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP) || - windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_BORDERLESS)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP) || + windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_BORDERLESS)) return WindowState.FullscreenBorderless; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) return WindowState.Minimised; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN)) return WindowState.Fullscreen; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MAXIMIZED)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MAXIMIZED)) return WindowState.Maximised; return WindowState.Normal; diff --git a/osu.Framework/Platform/SDL2/SDL2Window.cs b/osu.Framework/Platform/SDL2/SDL2Window.cs index 2f787a506f..7a62f84d7c 100644 --- a/osu.Framework/Platform/SDL2/SDL2Window.cs +++ b/osu.Framework/Platform/SDL2/SDL2Window.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ImageExtensions; using osu.Framework.Logging; using osu.Framework.Threading; @@ -162,7 +163,7 @@ internal SDL_SysWMinfo GetWindowSystemInformation() return wmInfo; } - public bool CapsLockPressed => SDL_GetModState().HasFlag(SDL_Keymod.KMOD_CAPS); + public bool CapsLockPressed => SDL_GetModState().HasFlagFast(SDL_Keymod.KMOD_CAPS); // references must be kept to avoid GC, see https://stackoverflow.com/a/6193914 @@ -203,7 +204,7 @@ protected SDL2Window(GraphicsSurfaceType surfaceType, string appName) CursorStateBindable.ValueChanged += evt => { - updateCursorVisibility(!evt.NewValue.HasFlag(CursorState.Hidden)); + updateCursorVisibility(!evt.NewValue.HasFlagFast(CursorState.Hidden)); updateCursorConfinement(); }; @@ -406,7 +407,7 @@ public void Raise() => ScheduleCommand(() => { var flags = (SDL_WindowFlags)SDL_GetWindowFlags(SDLWindowHandle); - if (flags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + if (flags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) SDL_RestoreWindow(SDLWindowHandle); SDL_RaiseWindow(SDLWindowHandle); diff --git a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs index 343ae2b4d0..43a62dd8ba 100644 --- a/osu.Framework/Platform/SDL2/SDL2Window_Input.cs +++ b/osu.Framework/Platform/SDL2/SDL2Window_Input.cs @@ -7,6 +7,7 @@ using System.Drawing; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.States; @@ -43,7 +44,7 @@ public bool RelativeMouseMode if (relativeMouseMode == value) return; - if (value && !CursorState.HasFlag(CursorState.Hidden)) + if (value && !CursorState.HasFlagFast(CursorState.Hidden)) throw new InvalidOperationException($"Cannot set {nameof(RelativeMouseMode)} to true when the cursor is not hidden via {nameof(CursorState)}."); relativeMouseMode = value; @@ -101,7 +102,7 @@ private void updateCursorVisibility(bool cursorVisible) => /// private void updateCursorConfinement() { - bool confined = CursorState.HasFlag(CursorState.Confined); + bool confined = CursorState.HasFlagFast(CursorState.Confined); ScheduleCommand(() => SDL_SetWindowGrab(SDLWindowHandle, confined ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE)); @@ -163,11 +164,11 @@ private void pollMouse() // the outer if just optimises for the common case that there are no buttons to release. if (buttonsToRelease != SDLButtonMask.None) { - if (buttonsToRelease.HasFlag(SDLButtonMask.Left)) MouseUp?.Invoke(MouseButton.Left); - if (buttonsToRelease.HasFlag(SDLButtonMask.Middle)) MouseUp?.Invoke(MouseButton.Middle); - if (buttonsToRelease.HasFlag(SDLButtonMask.Right)) MouseUp?.Invoke(MouseButton.Right); - if (buttonsToRelease.HasFlag(SDLButtonMask.X1)) MouseUp?.Invoke(MouseButton.Button1); - if (buttonsToRelease.HasFlag(SDLButtonMask.X2)) MouseUp?.Invoke(MouseButton.Button2); + if (buttonsToRelease.HasFlagFast(SDLButtonMask.Left)) MouseUp?.Invoke(MouseButton.Left); + if (buttonsToRelease.HasFlagFast(SDLButtonMask.Middle)) MouseUp?.Invoke(MouseButton.Middle); + if (buttonsToRelease.HasFlagFast(SDLButtonMask.Right)) MouseUp?.Invoke(MouseButton.Right); + if (buttonsToRelease.HasFlagFast(SDLButtonMask.X1)) MouseUp?.Invoke(MouseButton.Button1); + if (buttonsToRelease.HasFlagFast(SDLButtonMask.X2)) MouseUp?.Invoke(MouseButton.Button2); } } diff --git a/osu.Framework/Platform/SDL3/SDL3Extensions.cs b/osu.Framework/Platform/SDL3/SDL3Extensions.cs index 7e729cd5b6..711d57fe1b 100644 --- a/osu.Framework/Platform/SDL3/SDL3Extensions.cs +++ b/osu.Framework/Platform/SDL3/SDL3Extensions.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Drawing; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -18,7 +19,7 @@ public static Key ToKey(this SDL_KeyboardEvent sdlKeyboardEvent) { // Apple devices don't have the notion of NumLock (they have a Clear key instead). // treat them as if they always have NumLock on (the numpad always performs its primary actions). - bool numLockOn = sdlKeyboardEvent.mod.HasFlag(SDL_Keymod.SDL_KMOD_NUM) || RuntimeInfo.IsApple; + bool numLockOn = sdlKeyboardEvent.mod.HasFlagFast(SDL_Keymod.SDL_KMOD_NUM) || RuntimeInfo.IsApple; switch (sdlKeyboardEvent.scancode) { @@ -861,16 +862,16 @@ public static SDL_Scancode ToScancode(this InputKey inputKey) public static WindowState ToWindowState(this SDL_WindowFlags windowFlags) { - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_BORDERLESS)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_BORDERLESS)) return WindowState.FullscreenBorderless; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) return WindowState.Minimised; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_FULLSCREEN)) return WindowState.Fullscreen; - if (windowFlags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MAXIMIZED)) + if (windowFlags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MAXIMIZED)) return WindowState.Maximised; return WindowState.Normal; diff --git a/osu.Framework/Platform/SDL3/SDL3Window.cs b/osu.Framework/Platform/SDL3/SDL3Window.cs index b525030365..cdcd8f7549 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Extensions.ImageExtensions; using osu.Framework.Logging; using osu.Framework.Threading; @@ -142,7 +143,7 @@ public IntPtr DisplayHandle [SupportedOSPlatform("android")] public virtual IntPtr SurfaceHandle => throw new PlatformNotSupportedException(); - public bool CapsLockPressed => SDL_GetModState().HasFlag(SDL_Keymod.SDL_KMOD_CAPS); + public bool CapsLockPressed => SDL_GetModState().HasFlagFast(SDL_Keymod.SDL_KMOD_CAPS); /// /// Represents a handle to this instance, used for unmanaged callbacks. @@ -173,7 +174,7 @@ protected SDL3Window(GraphicsSurfaceType surfaceType, string appName) CursorStateBindable.ValueChanged += evt => { - updateCursorVisibility(!evt.NewValue.HasFlag(CursorState.Hidden)); + updateCursorVisibility(!evt.NewValue.HasFlagFast(CursorState.Hidden)); updateCursorConfinement(); }; @@ -373,7 +374,7 @@ public void Raise() => ScheduleCommand(() => { var flags = SDL_GetWindowFlags(SDLWindowHandle); - if (flags.HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) + if (flags.HasFlagFast(SDL_WindowFlags.SDL_WINDOW_MINIMIZED)) SDL_RestoreWindow(SDLWindowHandle); SDL_RaiseWindow(SDLWindowHandle); diff --git a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs index 37696bb99f..ba7bb28ae2 100644 --- a/osu.Framework/Platform/SDL3/SDL3Window_Input.cs +++ b/osu.Framework/Platform/SDL3/SDL3Window_Input.cs @@ -7,6 +7,7 @@ using System.Drawing; using osu.Framework.Bindables; using osu.Framework.Configuration; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics.Primitives; using osu.Framework.Input; using osu.Framework.Input.States; @@ -44,7 +45,7 @@ public bool RelativeMouseMode if (relativeMouseMode == value) return; - if (value && !CursorState.HasFlag(CursorState.Hidden)) + if (value && !CursorState.HasFlagFast(CursorState.Hidden)) throw new InvalidOperationException($"Cannot set {nameof(RelativeMouseMode)} to true when the cursor is not hidden via {nameof(CursorState)}."); relativeMouseMode = value; @@ -108,7 +109,7 @@ private void updateCursorVisibility(bool cursorVisible) => /// private void updateCursorConfinement() { - bool confined = CursorState.HasFlag(CursorState.Confined); + bool confined = CursorState.HasFlagFast(CursorState.Confined); ScheduleCommand(() => SDL_SetWindowMouseGrab(SDLWindowHandle, confined ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE)); @@ -174,11 +175,11 @@ private void pollMouse() // the outer if just optimises for the common case that there are no buttons to release. if (buttonsToRelease != 0) { - if (buttonsToRelease.HasFlag(SDL_MouseButtonFlags.SDL_BUTTON_LMASK)) MouseUp?.Invoke(MouseButton.Left); - if (buttonsToRelease.HasFlag(SDL_MouseButtonFlags.SDL_BUTTON_MMASK)) MouseUp?.Invoke(MouseButton.Middle); - if (buttonsToRelease.HasFlag(SDL_MouseButtonFlags.SDL_BUTTON_RMASK)) MouseUp?.Invoke(MouseButton.Right); - if (buttonsToRelease.HasFlag(SDL_MouseButtonFlags.SDL_BUTTON_X1MASK)) MouseUp?.Invoke(MouseButton.Button1); - if (buttonsToRelease.HasFlag(SDL_MouseButtonFlags.SDL_BUTTON_X2MASK)) MouseUp?.Invoke(MouseButton.Button2); + if (buttonsToRelease.HasFlagFast(SDL_MouseButtonFlags.SDL_BUTTON_LMASK)) MouseUp?.Invoke(MouseButton.Left); + if (buttonsToRelease.HasFlagFast(SDL_MouseButtonFlags.SDL_BUTTON_MMASK)) MouseUp?.Invoke(MouseButton.Middle); + if (buttonsToRelease.HasFlagFast(SDL_MouseButtonFlags.SDL_BUTTON_RMASK)) MouseUp?.Invoke(MouseButton.Right); + if (buttonsToRelease.HasFlagFast(SDL_MouseButtonFlags.SDL_BUTTON_X1MASK)) MouseUp?.Invoke(MouseButton.Button1); + if (buttonsToRelease.HasFlagFast(SDL_MouseButtonFlags.SDL_BUTTON_X2MASK)) MouseUp?.Invoke(MouseButton.Button2); } } diff --git a/osu.Framework/Platform/Windows/Native/Imm.cs b/osu.Framework/Platform/Windows/Native/Imm.cs index b4a6bfd555..912ade6b00 100644 --- a/osu.Framework/Platform/Windows/Native/Imm.cs +++ b/osu.Framework/Platform/Windows/Native/Imm.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.Text; using Microsoft.Win32.SafeHandles; +using osu.Framework.Extensions.EnumExtensions; namespace osu.Framework.Platform.Windows.Native { @@ -139,7 +140,7 @@ private bool tryGetCompositionSize(CompositionString compositionString, out int { size = -1; - if (!lParam.HasFlag(compositionString)) + if (!lParam.HasFlagFast(compositionString)) return false; size = ImmGetCompositionString(handle, compositionString, null, 0); diff --git a/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs b/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs index 1af7283455..2d941ec173 100644 --- a/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs +++ b/osu.Framework/Platform/Windows/WindowsMouseHandler_SDL2.cs @@ -3,6 +3,7 @@ using System; using System.Drawing; +using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Input.StateChanges; using osu.Framework.Platform.Windows.Native; using osu.Framework.Statistics; @@ -91,9 +92,9 @@ private unsafe IntPtr onWndProcSDL2(IntPtr userData, IntPtr hWnd, uint message, var position = new Vector2(mouse.LastX, mouse.LastY); float sensitivity = (float)Sensitivity.Value; - if (mouse.Flags.HasFlag(RawMouseFlags.MoveAbsolute)) + if (mouse.Flags.HasFlagFast(RawMouseFlags.MoveAbsolute)) { - var screenRect = mouse.Flags.HasFlag(RawMouseFlags.VirtualDesktop) ? Native.Input.VirtualScreenRect : new Rectangle(window.Position, window.ClientSize); + var screenRect = mouse.Flags.HasFlagFast(RawMouseFlags.VirtualDesktop) ? Native.Input.VirtualScreenRect : new Rectangle(window.Position, window.ClientSize); Vector2 screenSize = new Vector2(screenRect.Width, screenRect.Height);