using System.Collections.Concurrent; using System.Drawing; using System.Net; using System.Runtime.CompilerServices; using Dashboard.Events; using Dashboard.OpenGL; using Dashboard.Pal; using Dashboard.Windowing; using OpenTK.Mathematics; using OpenTK.Platform; using MouseMoveEventArgs = Dashboard.Events.MouseMoveEventArgs; using TK = OpenTK.Platform.Toolkit; namespace Dashboard.OpenTK.PAL2 { public class PhysicalWindow : IPhysicalWindow, IEventListener, IDpiAwareWindow { private readonly List _listeners = new List(); public Application Application { get; } public WindowHandle WindowHandle { get; } public DeviceContext DeviceContext { get; } public bool DoubleBuffered => true; // Always true for OpenTK windows. public IForm? Form { get; set; } = null; public IWindowManager? WindowManager { get; set; } public string Title { get => TK.Window.GetTitle(WindowHandle); set => TK.Window.SetTitle(WindowHandle, value); } public SizeF OuterSize { get { TK.Window.GetSize(WindowHandle, out Vector2i size); return new SizeF(size.X, size.Y); } set => TK.Window.SetSize(WindowHandle, new Vector2i((int)value.Width, (int)value.Height)); } public SizeF ClientSize { get { TK.Window.GetClientSize(WindowHandle, out Vector2i size); return new SizeF(size.X, size.Y); } set => TK.Window.SetClientSize(WindowHandle, new Vector2i((int)value.Width, (int)value.Height)); } public event EventHandler? EventRaised; public PhysicalWindow(Application app, WindowHandle window) { Application = app; WindowHandle = window; DeviceContext = CreateDeviceContext(app, this, new OpenGLGraphicsApiHints()); AddWindow(this); } public PhysicalWindow(Application app, WindowHandle window, OpenGLContextHandle context) { Application = app; WindowHandle = window; DeviceContext = new GLDeviceContext(app, this, new Pal2GLContext(window, context)); AddWindow(this); } public PhysicalWindow(Application app, GraphicsApiHints hints) { Application = app; WindowHandle = TK.Window.Create(hints); DeviceContext = CreateDeviceContext(app, this, hints); AddWindow(this); } private static DeviceContext CreateDeviceContext(Application app, PhysicalWindow window, GraphicsApiHints hints) { WindowHandle handle = window.WindowHandle; switch (hints.Api) { case GraphicsApi.OpenGL: case GraphicsApi.OpenGLES: return new GLDeviceContext(app, window, new Pal2GLContext(handle, TK.OpenGL.CreateFromWindow(handle))); default: throw new Exception($"Unknown graphics API {hints.Api}."); } } public bool IsDisposed { get; private set; } = false; public void Dispose() { if (IsDisposed) return; IsDisposed = true; RemoveWindow(this); (DeviceContext as IDisposable)?.Dispose(); TK.Window.Destroy(WindowHandle); } public virtual void SendEvent(object? sender, EventArgs args) { switch (args) { case UiEventArgs ui: switch (ui.Type) { case UiEventType.ControlInvalidateVisual: break; } break; } args = TransformEvent(sender, args); EventRaised?.Invoke(this, args); lock (_listeners) { foreach (IEventListener listener in _listeners) listener.SendEvent(this, args); } } private EventArgs TransformEvent(object? sender, EventArgs args) { // TODO: future return args; } public void SubcribeEvent(IEventListener listener) { lock (_listeners) { _listeners.Add(listener); } } public void UnsubscribeEvent(IEventListener listener) { lock (_listeners) { _listeners.Remove(listener); } } private static readonly ConcurrentDictionary _windows = new ConcurrentDictionary(); static PhysicalWindow() { EventQueue.EventRaised += EventQueueOnEventRaised; } private static void AddWindow(PhysicalWindow window) { _windows.TryAdd(window.WindowHandle, window); } private static void RemoveWindow(PhysicalWindow window) { _windows.TryRemove(window.WindowHandle, out _); } private static void EventQueueOnEventRaised(PalHandle? handle, PlatformEventType type, EventArgs args) { if (handle is not WindowHandle windowHandle) return; if (!_windows.TryGetValue(windowHandle, out PhysicalWindow? window)) return; switch (type) { case PlatformEventType.MouseMove: case PlatformEventType.Scroll: case PlatformEventType.MouseUp: case PlatformEventType.MouseDown: break; } } public float Dpi => Scale * 96f; public float Scale { get { TK.Window.GetScaleFactor(WindowHandle, out float x, out float y); return Math.Max(x, y); } } } }