< Summary

Class:Imagini.Window
Assembly:Imagini.Core
File(s):/home/razer/vscode-projects/project-grove/imagini/Imagini.Core/Window.cs
Covered lines:101
Uncovered lines:23
Coverable lines:124
Total lines:359
Line coverage:81.4% (101 of 124)
Covered branches:19
Total branches:34
Branch coverage:55.8% (19 of 34)

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
.cctor()1050%100%
OverrideCurrentWith(...)10100%100%
GetByID(...)2066.66%50%
.ctor(...)1063.63%100%
.ctor(...)100%100%
CheckWindowHandle()4066.66%50%
Register()2080%50%
Apply(...)4090%75%
Show()10100%100%
Hide()10100%100%
Minimize()10100%100%
Maximize()10100%100%
Restore()10100%100%
Raise()1080%100%
HasFlag(...)10100%100%
Destroy()20100%50%
GetSubsystem()60100%100%

File(s)

/home/razer/vscode-projects/project-grove/imagini/Imagini.Core/Window.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Collections.Generic;
 4using System.Diagnostics;
 5using System.Drawing;
 6using System.Linq;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using static Imagini.ErrorHandler;
 10using static SDL2.SDL_error;
 11using static SDL2.SDL_hints;
 12using static SDL2.SDL_render;
 13using static SDL2.SDL_syswm;
 14using static SDL2.SDL_version;
 15using static SDL2.SDL_video;
 16
 17namespace Imagini
 18{
 19    /// <summary>
 20    /// An app window.
 21    /// </summary>
 22    public sealed class Window : Resource
 23    {
 24        /// <summary>
 25        /// Gets the handle of this window.
 26        /// </summary>
 93927        public IntPtr Handle { get; private set; }
 28        internal uint ID;
 29
 30
 031        private static Dictionary<uint, Window> _windows =
 032            new Dictionary<uint, Window>();
 33        /// <summary>
 34        /// Returns all existing app windows.
 35        /// </summary>
 536        public static IReadOnlyCollection<Window> Windows => _windows.Values;
 37
 38        internal static Window Current
 39        {
 40            get
 41            {
 2342                if (_overridedCurrent != null && !_overridedCurrent.IsDisposed)
 2243                    return _overridedCurrent;
 244                if (_windows.Count == 0) return null;
 045                return _windows.Values
 046                    .Where(window => !window.IsDisposed && window.IsVisible && window.IsFocused)
 047                    .FirstOrDefault();
 48            }
 49        }
 50
 151        private static Window _overridedCurrent = null;
 52        /// <summary>
 53        /// Sets the specified window as the current one no matter
 54        /// if it's focused and visible or not. If a null is passed,
 55        /// then the overriding is disabled. If the specified window is
 56        /// disposed, then the overriding is disabled.
 57        /// </summary>
 958        public static void OverrideCurrentWith(Window window) => _overridedCurrent = window;
 59
 60        internal static Window GetByID(uint id)
 61        {
 5862            if (_windows.ContainsKey(id))
 5863                return _windows[id];
 064            return null;
 65        }
 66
 67        /// <summary>
 68        /// Gets or sets the size of the window's client area. Affects only
 69        /// non-fullscreen modes.
 70        /// </summary>
 71        /// <remarks>
 72        /// The window size in screen coordinates may differ from the size in
 73        /// pixels, if the window was created with <see cref="WindowSettings.AllowHighDpi" />.
 74        /// </remarks>
 75        public Size Size
 76        {
 77            get
 78            {
 379                CheckIfNotDisposed();
 380                SDL_GetWindowSize(Handle, out int w, out int h);
 381                return new Size(w, h);
 82            }
 83            set
 84            {
 185                CheckIfNotDisposed();
 186                SDL_SetWindowSize(Handle, value.Width, value.Height);
 187            }
 88        }
 89
 90        /// <summary>
 91        /// Gets the size of the window's client area in pixels.
 92        /// </summary>
 93        /// <seealso cref="Size" />
 94        public Size SizeInPixels
 95        {
 96            get
 97            {
 198                CheckIfNotDisposed();
 199                SDL_GL_GetDrawableSize(Handle, out int w, out int h);
 1100                return new Size(w, h);
 101            }
 102        }
 103
 104        /// <summary>
 105        /// Gets or sets the window minimum size.
 106        /// </summary>
 107        public Size MinimumSize
 108        {
 109            get
 110            {
 1111                CheckIfNotDisposed();
 1112                SDL_GetWindowMinimumSize(Handle, out int w, out int h);
 1113                return new Size(w, h);
 114            }
 115            set
 116            {
 1117                CheckIfNotDisposed();
 1118                SDL_SetWindowMinimumSize(Handle, value.Width, value.Height);
 1119            }
 120        }
 121
 122        /// <summary>
 123        /// Gets or sets the window minimum size.
 124        /// </summary>
 125        public Size MaximumSize
 126        {
 127            get
 128            {
 1129                CheckIfNotDisposed();
 1130                SDL_GetWindowMaximumSize(Handle, out int w, out int h);
 1131                return new Size(w, h);
 132            }
 133            set
 134            {
 1135                CheckIfNotDisposed();
 1136                SDL_SetWindowMaximumSize(Handle, value.Width, value.Height);
 1137            }
 138        }
 139
 140        /// <summary>
 141        /// Returns the settings for this window.
 142        /// </summary>
 159143        public WindowSettings Settings { get; private set; }
 144
 145        /// <summary>
 146        /// Returns the <see cref="WindowMode" /> for this window.
 147        /// </summary>
 1148        public WindowMode Mode => Settings.WindowMode;
 149
 76150        public Window(WindowSettings settings, uint additionalFlags = 0)
 151        {
 0152            Handle = SDL_CreateWindow(settings.Title,
 0153                SDL_WINDOWPOS_CENTERED_DISPLAY(settings.DisplayIndex),
 0154                SDL_WINDOWPOS_CENTERED_DISPLAY(settings.DisplayIndex),
 0155                settings.WindowWidth, settings.WindowHeight, settings.GetFlags() | additionalFlags);
 76156            CheckWindowHandle();
 76157            Title = settings.Title;
 76158            Apply(settings);
 76159            Register();
 76160            Raise();
 76161        }
 162
 0163        public Window(IntPtr handle)
 164        {
 0165            Handle = handle;
 0166            CheckWindowHandle();
 0167            Register();
 0168        }
 169
 170        private void CheckWindowHandle()
 171        {
 76172            if (Handle == IntPtr.Zero)
 0173                throw new ImaginiException($"Could not create window: {SDL_GetError()}");
 76174            ID = SDL_GetWindowID(Handle);
 76175            if (ID == 0)
 0176                throw new ImaginiException($"Could not get the window ID: {SDL_GetError()}");
 76177        }
 178
 179        private void Register()
 180        {
 181            // Generally should not happen, but it's better to check anyways
 76182            if (_windows.ContainsKey(ID))
 0183                throw new ImaginiException("Window with the specified ID already exists");
 184            else
 76185                _windows.Add(ID, this);
 76186            GetSubsystem();
 76187        }
 188
 189        /// <summary>
 190        /// Applies the specified settings to this window.
 191        /// </summary>
 192        public void Apply(WindowSettings settings)
 193        {
 77194            CheckIfNotDisposed();
 77195            settings.Apply(Handle);
 77196            Settings = settings.Clone() as WindowSettings;
 77197            if (settings.WindowMode == WindowMode.Fullscreen)
 1198                settings.FullscreenDisplayMode = this.Display.CurrentMode;
 77199            var h = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
 77200            Settings.VSync = SDL_GetHint(SDL_HINT_RENDER_VSYNC) == "1";
 77201            Title = settings.Title;
 77202            OnSettingsChanged?.Invoke(this, settings);
 0203        }
 204
 205        /// <summary>
 206        /// Fires after <see cref="Apply()">.
 207        /// </summary>
 208        public event EventHandler<WindowSettings> OnSettingsChanged;
 209
 210        /// <summary>
 211        /// Shows this window.
 212        /// </summary>
 2213        public void Show() => NotDisposed(() => SDL_ShowWindow(Handle));
 214        /// <summary>
 215        /// Hides this window.
 216        /// </summary>
 2217        public void Hide() => NotDisposed(() => SDL_HideWindow(Handle));
 218
 219        /// <summary>
 220        /// Indicates if the window is visible.
 221        /// </summary>
 222        public bool IsVisible
 223        {
 1224            get => HasFlag(SDL_WindowFlags.SDL_WINDOW_SHOWN);
 225        }
 226
 227        /// <summary>
 228        /// Returns true if this window has input focus.
 229        /// </summary>
 0230        public bool IsFocused => HasFlag(SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS) ||
 0231            HasFlag(SDL_WindowFlags.SDL_WINDOW_MOUSE_FOCUS) ||
 0232            HasFlag(SDL_WindowFlags.SDL_WINDOW_INPUT_GRABBED);
 233
 234        /// <summary>
 235        /// Minimizes this window.
 236        /// </summary>
 2237        public void Minimize() => NotDisposed(() => SDL_MinimizeWindow(Handle));
 238
 239        /// <summary>
 240        /// Indicates if the window is minimized.
 241        /// </summary>
 242        public bool IsMinimized
 243        {
 8244            get => HasFlag(SDL_WindowFlags.SDL_WINDOW_MINIMIZED);
 245        }
 246
 247        /// <summary>
 248        /// Maximizes this window.
 249        /// </summary>
 2250        public void Maximize() => NotDisposed(() => SDL_MaximizeWindow(Handle));
 251
 252        /// <summary>
 253        /// Indicates if the window is maximized.
 254        /// </summary>
 255        public bool IsMaximized
 256        {
 2257            get => HasFlag(SDL_WindowFlags.SDL_WINDOW_MAXIMIZED);
 258        }
 259
 260        /// <summary>
 261        /// Restores the window. Called when IsMinimized or IsMaximized is set
 262        /// to false.
 263        /// </summary>
 2264        public void Restore() => NotDisposed(() => SDL_RestoreWindow(Handle));
 265
 266        /// <summary>
 267        /// Raises this window above other windows and sets the input focus.
 268        /// </summary>
 0269        public void Raise() => NotDisposed(() =>
 79270        {
 79271            SDL_RaiseWindow(Handle);
 79272            SDL_SetWindowInputFocus(Handle); // X11 only
 79273        });
 274
 275        /// <summary>
 276        /// Gets or sets the window title.
 277        /// </summary>
 278        public string Title
 279        {
 2280            get => NotDisposed(() => SDL_GetWindowTitle(Handle));
 306281            set => NotDisposed(() => SDL_SetWindowTitle(Handle, value));
 282        }
 283
 284        /// <summary>
 285        /// Returns the display index of this window.
 286        /// </summary>
 1287        public int DisplayIndex => NotDisposed(() =>
 1288            Display.GetCurrentDisplayIndexForWindow(Handle));
 289        /// <summary>
 290        /// Returns the display of this window.
 291        /// </summary>
 2292        public Display Display => NotDisposed(() =>
 2293            Display.GetCurrentDisplayForWindow(Handle));
 294
 11295        private uint _flags => SDL_GetWindowFlags(Handle);
 296
 297        private bool HasFlag(SDL_WindowFlags flag) =>
 22298            NotDisposed(() => (_flags & (uint)flag) == (uint)flag);
 299
 1300        static Window() => Lifecycle.TryInitialize();
 301
 302        internal override void Destroy()
 303        {
 76304            if (IsDisposed) return;
 76305            base.Destroy();
 76306            SDL_DestroyWindow(Handle);
 76307            _windows.Remove(ID);
 76308            Handle = IntPtr.Zero;
 76309        }
 310
 311        /// <summary>
 312        /// Returns the underlying window subsystem type.
 313        /// </summary>
 152314        public WindowSubsystem Subsystem { get; private set; }
 315
 316        private unsafe void GetSubsystem()
 317        {
 76318            SDL_GetVersion(out SDL_Version version);
 76319            var container = Marshal.AllocHGlobal(32);
 76320            var p = (byte*)container;
 76321            *p = version.major;
 76322            *(p + 1) = version.minor;
 76323            *(p + 2) = version.patch;
 76324            SDL_GetWindowWMInfo(Handle, container);
 76325            Subsystem = WindowSubsystem.Unknown;
 76326            var min = Enum.GetValues(typeof(WindowSubsystem)).Cast<int>().Min();
 76327            var max = Enum.GetValues(typeof(WindowSubsystem)).Cast<int>().Max();
 304328            for (int i = 0; i < 4; i++)
 329            {
 330                // run through enum bytes because the num size is not fixed
 152331                var value = *(p + 3 + i);
 152332                if (value >= min && value <= max)
 333                {
 76334                    Subsystem = (WindowSubsystem)value;
 76335                    break;
 336                }
 337            }
 76338            Marshal.FreeHGlobal(container);
 76339        }
 340    }
 341
 342    /// <summary>
 343    /// Defines the windowing system type.
 344    /// </summary>
 345    public enum WindowSubsystem
 346    {
 347        Unknown,
 348        Windows,
 349        X11,
 350        DirectFB,
 351        Cocoa,
 352        UIKit,
 353        Wayland,
 354        Mir,
 355        WinRT,
 356        Android,
 357        Vivante
 358    }
 359}