using System.Collections.Concurrent; using System.Drawing; using Dashboard.Drawing; using Dashboard.Events; 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, IDrawQueuePaintable, IEventListener { public DrawQueue DrawQueue { get; } = new DrawQueue(); public WindowHandle WindowHandle { get; } public IDeviceContext DeviceContext { get; } public bool DoubleBuffered => true; // Always true for OpenTK windows. 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? Painting; public event EventHandler? AnimationTimerEvent; public event EventHandler? MouseMoved; public event EventHandler? MouseButtonDown; public event EventHandler? MouseButtonUp; public event EventHandler? MouseScroll; public PhysicalWindow(WindowHandle window, IDeviceContext dc) { WindowHandle = window; DeviceContext = dc; AddWindow(this); } public PhysicalWindow(WindowHandle window, OpenGLContextHandle context, ISwapGroup? swapGroup = null) { WindowHandle = window; DeviceContext = new OpenGLDeviceContext(window, context, swapGroup); AddWindow(this); } public PhysicalWindow(GraphicsApiHints hints) { WindowHandle = TK.Window.Create(hints); DeviceContext = CreateDeviceContext(WindowHandle, hints); AddWindow(this); } private static IDeviceContext CreateDeviceContext(WindowHandle window, GraphicsApiHints hints) { switch (hints.Api) { case GraphicsApi.OpenGL: case GraphicsApi.OpenGLES: OpenGLContextHandle context = TK.OpenGL.CreateFromWindow(window); return new OpenGLDeviceContext(window, context); default: throw new Exception($"Unknown graphics API {hints.Api}."); } } private bool _isDisposed = false; public void Dispose() { if (_isDisposed) return; _isDisposed = true; RemoveWindow(this); (DeviceContext as IDisposable)?.Dispose(); TK.Window.Destroy(WindowHandle); } protected virtual void OnPaint() { WindowManager?.Paint(); Painting?.Invoke(this, EventArgs.Empty); } public void Paint() { DrawQueue.Clear(); OnPaint(); } protected virtual void OnAnimationTimerEvent(AnimationTickEventArgs ea) => AnimationTimerEvent?.Invoke(this, ea); protected virtual void OnMouseMoved(MouseMoveEventArgs ea) => MouseMoved?.Invoke(this, ea); protected virtual void OnMouseButtonDown(MouseButtonEventArgs ea) => MouseButtonDown?.Invoke(this, ea); protected virtual void OnMouseButtonUp(MouseButtonEventArgs ea) => MouseButtonUp?.Invoke(this, ea); protected virtual void OnMouseScroll(MouseScrollEventArgs ea) => MouseScroll?.Invoke(this, ea); public void SendEvent(EventArgs args) { } 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; } } } }