using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using Dashboard.Events; using Dashboard.OpenGL; using Dashboard.Pal; using Dashboard.Windowing; using OpenTK.Platform; using TK = OpenTK.Platform.Toolkit; using OPENTK = OpenTK.Platform; using DB = Dashboard.Events; namespace Dashboard.OpenTK.PAL2 { public class Pal2Application(ToolkitOptions? options = null) : Application { public override string DriverName => "Dashboard OpenTK PAL2.0 Driver"; public override string DriverVendor => "Dashboard"; public override Version DriverVersion => new Version(0, 1); public GraphicsApiHints GraphicsApiHints { get; init; } = new OpenGLGraphicsApiHints(); private readonly List _windows = new List(); private readonly ConditionalWeakTable _windowHandleWindowMap = new ConditionalWeakTable(); private long _tick = Stopwatch.GetTimestamp(); public override IPhysicalWindow CreatePhysicalWindow() { PhysicalWindow window = new PhysicalWindow(this, GraphicsApiHints); _windows.Add(window); _windowHandleWindowMap.Add(window.WindowHandle, new WindowExtraInfo(window)); return window; } public override IWindow CreateWindow() { return CreatePhysicalWindow(); } protected override void InitializeInternal() { base.InitializeInternal(); CancellationToken?.Register(() => { TK.Window.PostUserEvent(new ApplicationQuitEventArgs()); }); TK.Event.EventRaised += OnEventRaised; TK.Init(options ?? new ToolkitOptions()); } internal void RemoveWindow(PhysicalWindow window) { _windows.Remove(window); } public override void RunEvents(bool wait) { if (_windows.Count == 0) { Dispose(); return; } TK.Window.ProcessEvents(wait); long tock = Stopwatch.GetTimestamp(); long elapsed = _tick - tock; float delta = (float)elapsed / Stopwatch.Frequency; TickEventArgs tickEvent = new TickEventArgs(delta); _tick = tock; for (int i = 0; i < _windows.Count; i++) { PhysicalWindow window = _windows[i]; if (window.IsDisposed) { _windows.RemoveAt(i); continue; } window.SendEvent(this, tickEvent); window.SendEvent(this, new PaintEventArgs(window.DeviceContext)); // For now we swap each window individually. ((GLDeviceContext)window.DeviceContext).GLContext.SwapGroup.Swap(); } } private void OnEventRaised(EventArgs args) { if (args is WindowEventArgs window) { OnWindowEventRaised(window.Window, window); return; } else { // System.Diagnostics.Debugger.Break(); } } private void OnWindowEventRaised(WindowHandle handle, EventArgs args) { if (!_windowHandleWindowMap.TryGetValue(handle, out WindowExtraInfo? info)) { Debugger?.LogDebug($"Unknown window handle {handle} received from OpenTK."); return; } // TODO: fix this. switch (args) { case ApplicationQuitEventArgs: { Quit = true; return; } // Mouse Events case MouseButtonDownEventArgs mdown: { MouseButtons buttons = (MouseButtons)(1 << (int)mdown.Button); ModifierKeys modifierKeys = mdown.Modifiers.ToDashboard(); // TODO: modifier keys MouseButtonEventArgs down2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, false); info.Window.SendEvent(this, down2); break; } case MouseButtonUpEventArgs mup: { MouseButtons buttons = (MouseButtons)(1 << (int)mup.Button); ModifierKeys modifierKeys = mup.Modifiers.ToDashboard(); // TODO: modifier keys MouseButtonEventArgs up2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, true); info.Window.SendEvent(this, up2); break; } case OPENTK.MouseMoveEventArgs mmove: { Vector2 position = new Vector2(mmove.ClientPosition.X, mmove.ClientPosition.Y); DB.MouseMoveEventArgs move2 = new DB.MouseMoveEventArgs(position, position - info.MousePosition); info.MousePosition = position; info.Window.SendEvent(this, move2); break; } case ScrollEventArgs mscroll: { Vector2 distance = new Vector2(mscroll.Distance.X, mscroll.Distance.Y); Vector2 delta = new Vector2(mscroll.Delta.X, mscroll.Delta.Y); MouseScrollEventArgs scroll2 = new MouseScrollEventArgs(distance, delta); info.Window.SendEvent(this, scroll2); break; } // Keyboard & Text Events case KeyDownEventArgs kdown: { ModifierKeys modifierKeys = kdown.Modifiers.ToDashboard(); KeyCode keyCode = kdown.Key.ToDashboard(); ScanCode scanCode = kdown.Scancode.ToDashboard(); KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, false); info.Window.SendEvent(this, up2); break; } case KeyUpEventArgs kup: { ModifierKeys modifierKeys = kup.Modifiers.ToDashboard(); KeyCode keyCode = kup.Key.ToDashboard(); ScanCode scanCode = kup.Scancode.ToDashboard(); KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, true); info.Window.SendEvent(this, up2); break; } case OPENTK.TextInputEventArgs textInput: { DB.TextInputEventArgs textInput2 = new DB.TextInputEventArgs(textInput.Text); info.Window.SendEvent(this, textInput2); break; } case TextEditingEventArgs textEditing: { TextEditEventArgs textEditing2 = new TextEditEventArgs(textEditing.Candidate, textEditing.Cursor, textEditing.Length); info.Window.SendEvent(this, textEditing2); break; } // Window/Surface related events. case CloseEventArgs: { info.Window.SendEvent(this, new WindowCloseEvent()); break; } case WindowFramebufferResizeEventArgs: { info.Window.SendEvent(this, new ResizeEventArgs()); info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext)); break; } case WindowResizeEventArgs: { info.Window.SendEvent(this, new ResizeEventArgs()); info.Window.SendEvent(this, new PaintEventArgs(info.Window.DeviceContext)); break; } default: Debugger?.LogDebug($"Unknown event type {args.GetType().Name} with \"{args}\"."); break; } } private record WindowExtraInfo(PhysicalWindow Window) { public Vector2 MousePosition { get; set; } = Vector2.Zero; } } internal class ApplicationQuitEventArgs() : EventArgs { } }