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 : 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; set; } = 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(); EventQueue.EventRaised += OnEventRaised; } public override void RunEvents(bool wait) { 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(PalHandle? handle, PlatformEventType type, EventArgs args) { if (handle is WindowHandle window) { OnWindowEventRaised(window, type, args); return; } else { System.Diagnostics.Debugger.Break(); } } private void OnWindowEventRaised(WindowHandle handle, PlatformEventType type, EventArgs args) { if (!_windowHandleWindowMap.TryGetValue(handle, out WindowExtraInfo? info)) { Debugger?.LogDebug($"Unknown window handle {handle} received from OpenTK."); return; } switch (type) { case PlatformEventType.MouseDown: { MouseButtonDownEventArgs down = (MouseButtonDownEventArgs)args; MouseButtons buttons = (MouseButtons)(1 << (int)down.Button); ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers); // TODO: modifier keys MouseButtonEventArgs down2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, false); info.Window.SendEvent(this, down2); break; } case PlatformEventType.MouseUp: { MouseButtonUpEventArgs up = (MouseButtonUpEventArgs)args; MouseButtons buttons = (MouseButtons)(1 << (int)up.Button); ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers); // TODO: modifier keys MouseButtonEventArgs up2 = new MouseButtonEventArgs(info.MousePosition, buttons, modifierKeys, true); info.Window.SendEvent(this, up2); break; } case PlatformEventType.MouseMove: { OPENTK.MouseMoveEventArgs move = (OPENTK.MouseMoveEventArgs)args; Vector2 position = new Vector2(move.ClientPosition.X, move.ClientPosition.Y); DB.MouseMoveEventArgs move2 = new DB.MouseMoveEventArgs(position, position - info.MousePosition); info.MousePosition = position; info.Window.SendEvent(this, move2); break; } case PlatformEventType.Scroll: { ScrollEventArgs scroll = (ScrollEventArgs)args; Vector2 distance = new Vector2(scroll.Distance.X, scroll.Distance.Y); Vector2 delta = new Vector2(scroll.Delta.X, scroll.Delta.Y); MouseScrollEventArgs scroll2 = new MouseScrollEventArgs(distance, delta); info.Window.SendEvent(this, scroll2); break; } case PlatformEventType.KeyDown: { KeyDownEventArgs down = (KeyDownEventArgs)args; ModifierKeys modifierKeys = GetModifierKeys(down.Modifiers); KeyCode keyCode = GetKeyCode(down.Key); ScanCode scanCode = GetScanCode(down.Scancode); KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, false); info.Window.SendEvent(this, up2); break; } case PlatformEventType.KeyUp: { KeyUpEventArgs up = (KeyUpEventArgs)args; ModifierKeys modifierKeys = GetModifierKeys(up.Modifiers); KeyCode keyCode = GetKeyCode(up.Key); ScanCode scanCode = GetScanCode(up.Scancode); KeyboardButtonEventArgs up2 = new KeyboardButtonEventArgs(keyCode, scanCode, modifierKeys, true); info.Window.SendEvent(this, up2); break; } case PlatformEventType.Close: { info.Window.SendEvent(this, new WindowCloseEvent()); break; } default: Debugger?.LogDebug($"Unknown event type {type} with \"{args}\"."); break; } } private static ModifierKeys GetModifierKeys(KeyModifier modifier) { ModifierKeys keys = 0; keys |= modifier.HasFlag(KeyModifier.NumLock) ? ModifierKeys.NumLock : 0; keys |= modifier.HasFlag(KeyModifier.CapsLock) ? ModifierKeys.CapsLock : 0; keys |= modifier.HasFlag(KeyModifier.ScrollLock) ? ModifierKeys.ScrollLock : 0; keys |= modifier.HasFlag(KeyModifier.LeftShift) ? ModifierKeys.LeftShift : 0; keys |= modifier.HasFlag(KeyModifier.LeftControl) ? ModifierKeys.LeftControl : 0; keys |= modifier.HasFlag(KeyModifier.LeftAlt) ? ModifierKeys.LeftAlt : 0; keys |= modifier.HasFlag(KeyModifier.LeftGUI) ? ModifierKeys.LeftMeta : 0; keys |= modifier.HasFlag(KeyModifier.RightShift) ? ModifierKeys.RightShift : 0; keys |= modifier.HasFlag(KeyModifier.RightControl) ? ModifierKeys.RightControl : 0; keys |= modifier.HasFlag(KeyModifier.RightAlt) ? ModifierKeys.RightAlt : 0; keys |= modifier.HasFlag(KeyModifier.RightGUI) ? ModifierKeys.RightMeta : 0; keys |= modifier.HasFlag(KeyModifier.Shift) ? ModifierKeys.Shift : 0; keys |= modifier.HasFlag(KeyModifier.Control) ? ModifierKeys.Control : 0; keys |= modifier.HasFlag(KeyModifier.Alt) ? ModifierKeys.Alt : 0; keys |= modifier.HasFlag(KeyModifier.GUI) ? ModifierKeys.Meta : 0; // C# makes this cast as annoying as possible. keys |= (ModifierKeys)((((int)keys >> (int)ModifierKeys.RightBitPos) & 0xF) | (((int)keys >> (int)ModifierKeys.LeftBitPos) & 0xF)); return keys; } private record WindowExtraInfo(PhysicalWindow Window) { public Vector2 MousePosition { get; set; } = Vector2.Zero; } // TODO: Keycode and scancode tables. private static KeyCode GetKeyCode(Key key) => key switch { _ => (KeyCode)0, }; private static ScanCode GetScanCode(Scancode scanCode) => scanCode switch { _ => (ScanCode)0, }; } }