From 1635986f05972853a992e1eaa9cd2e7611a89be6 Mon Sep 17 00:00:00 2001 From: Nelson Carrillo Date: Wed, 25 Jul 2018 00:08:56 -0400 Subject: [PATCH] Implement invalid rect coalescing to work around the lack of an OS driven invalidation mechanism in winit --- .../AvaloniaSynchronizationContext.cs | 0 .../Bindings/EventNotifier.cs | 5 +- src/Avalonia.Windowing/Bindings/Events.cs | 12 +- src/Avalonia.Windowing/Bindings/EventsLoop.cs | 2 + src/Avalonia.Windowing/KeyTransform.cs | 211 ++++++++++++++++++ src/Avalonia.Windowing/WIndowingPlatform.cs | 35 ++- src/Avalonia.Windowing/WindowImpl.cs | 37 ++- src/Skia/Avalonia.Skia/GlRenderTarget.cs | 8 +- 8 files changed, 282 insertions(+), 28 deletions(-) rename src/Avalonia.Base/{Threading => Platform}/AvaloniaSynchronizationContext.cs (100%) create mode 100644 src/Avalonia.Windowing/KeyTransform.cs diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Platform/AvaloniaSynchronizationContext.cs similarity index 100% rename from src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs rename to src/Avalonia.Base/Platform/AvaloniaSynchronizationContext.cs diff --git a/src/Avalonia.Windowing/Bindings/EventNotifier.cs b/src/Avalonia.Windowing/Bindings/EventNotifier.cs index 5e58f6cdfc..719f120420 100644 --- a/src/Avalonia.Windowing/Bindings/EventNotifier.cs +++ b/src/Avalonia.Windowing/Bindings/EventNotifier.cs @@ -1,13 +1,16 @@ using System; using System.Runtime.InteropServices; +using System.Security; namespace Avalonia.Windowing.Bindings { public delegate void MouseEventCallback(IntPtr windowId, MouseEvent mouseEvent); public delegate void ResizeEventCallback(IntPtr windowId, ResizeEvent resizeEvent); public delegate void KeyboardEventCallback(IntPtr windowId, KeyboardEvent keyboardEvent); - public delegate void AwakenedEventCallback(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void AwakenedEventCallback(); + [StructLayout(LayoutKind.Sequential)] public struct EventNotifier { diff --git a/src/Avalonia.Windowing/Bindings/Events.cs b/src/Avalonia.Windowing/Bindings/Events.cs index 486e9f4e71..0201ff3aab 100644 --- a/src/Avalonia.Windowing/Bindings/Events.cs +++ b/src/Avalonia.Windowing/Bindings/Events.cs @@ -27,12 +27,12 @@ namespace Avalonia.Windowing.Bindings [StructLayout(LayoutKind.Sequential)] public struct KeyboardEvent { - public bool Pressed { get; set; } - public bool Shift { get; set; } - public bool Control { get; set; } - public bool Alt { get; set; } - public bool Logo { get; set; } - UInt32 VirtualKeyCode { get; set; } + public byte Pressed { get; set; } + public byte Shift { get; set; } + public byte Control { get; set; } + public byte Alt { get; set; } + public byte Logo { get; set; } + public VirtualKeyCode VirtualKeyCode { get; set; } } [StructLayout(LayoutKind.Sequential)] diff --git a/src/Avalonia.Windowing/Bindings/EventsLoop.cs b/src/Avalonia.Windowing/Bindings/EventsLoop.cs index 925f4771a7..c217aa937f 100644 --- a/src/Avalonia.Windowing/Bindings/EventsLoop.cs +++ b/src/Avalonia.Windowing/Bindings/EventsLoop.cs @@ -33,7 +33,9 @@ namespace Avalonia.Windowing.Bindings public event KeyboardEventCallback OnKeyboardEvent; public event MouseEventCallback OnMouseEvent; + public event AwakenedEventCallback OnAwakened; + public event ResizeEventCallback OnResized; public EventsLoop() diff --git a/src/Avalonia.Windowing/KeyTransform.cs b/src/Avalonia.Windowing/KeyTransform.cs new file mode 100644 index 0000000000..81fb4cdedf --- /dev/null +++ b/src/Avalonia.Windowing/KeyTransform.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using Avalonia.Input; + +namespace Avalonia.Windowing +{ + public enum VirtualKeyCode : UInt32 + { + /// The '1' key over the letters. + Key1, + /// The '2' key over the letters. + Key2, + /// The '3' key over the letters. + Key3, + /// The '4' key over the letters. + Key4, + /// The '5' key over the letters. + Key5, + /// The '6' key over the letters. + Key6, + /// The '7' key over the letters. + Key7, + /// The '8' key over the letters. + Key8, + /// The '9' key over the letters. + Key9, + /// The '0' key over the 'O' and 'P' keys. + Key0, + + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + + /// The Escape key, next to F1. + Escape, + + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + + /// Print Screen/SysRq. + Snapshot, + /// Scroll Lock. + Scroll, + /// Pause/Break key, next to Scroll lock. + Pause, + + /// `Insert`, next to Backspace. + Insert, + Home, + Delete, + End, + PageDown, + PageUp, + + Left, + Up, + Right, + Down, + + /// The Backspace key, right over Enter. + // TODO: rename + Back, + /// The Enter key. + Return, + /// The space bar. + Space, + + /// The "Compose" key on Linux. + Compose, + + Caret, + + Numlock, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + + AbntC1, + AbntC2, + Add, + Apostrophe, + Apps, + At, + Ax, + Backslash, + Calculator, + Capital, + Colon, + Comma, + Convert, + Decimal, + Divide, + Equals, + Grave, + Kana, + Kanji, + LAlt, + LBracket, + LControl, + LShift, + LWin, + Mail, + MediaSelect, + MediaStop, + Minus, + Multiply, + Mute, + MyComputer, + NavigateForward, // also called "Prior" + NavigateBackward, // also called "Next" + NextTrack, + NoConvert, + NumpadComma, + NumpadEnter, + NumpadEquals, + OEM102, + Period, + PlayPause, + Power, + PrevTrack, + RAlt, + RBracket, + RControl, + RShift, + RWin, + Semicolon, + Slash, + Sleep, + Stop, + Subtract, + Sysrq, + Tab, + Underline, + Unlabeled, + VolumeDown, + VolumeUp, + Wake, + WebBack, + WebFavorites, + WebForward, + WebHome, + WebRefresh, + WebSearch, + WebStop, + Yen, + Copy, + Paste, + Cut, + } + + public static class KeyTransform + { + static readonly Dictionary Keys = new Dictionary + { + [VirtualKeyCode.Key0] = Key.D0, + [VirtualKeyCode.Key1] = Key.D1, + + [VirtualKeyCode.A] = Key.A + }; + + public static Key? TransformKeyCode(VirtualKeyCode code) + { + if (Keys.TryGetValue(code, out var rv)) + return rv; + return null; + } + } +} diff --git a/src/Avalonia.Windowing/WIndowingPlatform.cs b/src/Avalonia.Windowing/WIndowingPlatform.cs index bdede4008e..85b1726708 100644 --- a/src/Avalonia.Windowing/WIndowingPlatform.cs +++ b/src/Avalonia.Windowing/WIndowingPlatform.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Reactive.Disposables; using System.Runtime.InteropServices; using System.Threading; @@ -69,11 +70,17 @@ namespace Avalonia.Windowing } } + private bool _signaled; private void _eventsLoop_Awakened() { - Signaled?.Invoke(DispatcherPriority.Input); - Signaled?.Invoke(DispatcherPriority.Layout); - Signaled?.Invoke(DispatcherPriority.Render); + lock (this) + { + if (!_signaled) + return; + _signaled = false; + } + + Signaled?.Invoke(null); } @@ -83,11 +90,15 @@ namespace Avalonia.Windowing Instance.DoInitialize(); } + private readonly IKeyboardDevice keyboardDevice = new KeyboardDevice(); + private readonly IMouseDevice mouseDevice = new MouseDevice(); + public void DoInitialize() { AvaloniaLocator.CurrentMutable .Bind().ToConstant(this) - .Bind().ToConstant(new MouseDevice()) + .Bind().ToConstant(keyboardDevice) + .Bind().ToConstant(mouseDevice) .Bind().ToConstant(new IconLoader()) .Bind().ToConstant(new CursorFactory()) .Bind().ToConstant(new PlatformSettings()) @@ -127,7 +138,13 @@ namespace Avalonia.Windowing public void Signal(DispatcherPriority priority) { - // We need to run some sort of callback on wakeup don't we? + lock (this) + { + if (_signaled) + return; + _signaled = true; + } + _eventsLoop.Wakeup(); } @@ -135,10 +152,12 @@ namespace Avalonia.Windowing public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) { - var x = new TimerDel(tick); - timerTickers.Add(x); + // TODO: Need a way to cancel a timer when Dispose is called. + + // var x = new TimerDel(tick); + // timerTickers.Add(x); - _eventsLoop.RunTimer(x); + // _eventsLoop.RunTimer(x); return Disposable.Create(() => { // timerTickers.Remove(x); }); diff --git a/src/Avalonia.Windowing/WindowImpl.cs b/src/Avalonia.Windowing/WindowImpl.cs index b0f29ac06e..2b39bef55e 100644 --- a/src/Avalonia.Windowing/WindowImpl.cs +++ b/src/Avalonia.Windowing/WindowImpl.cs @@ -1,5 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; using Avalonia.Controls; using Avalonia.Gpu; using Avalonia.Input; @@ -19,6 +23,17 @@ namespace Avalonia.Windowing public WindowImpl(IWindowWrapper wrapper) { _windowWrapper = wrapper; + + // TODO: This is only necessary when using ImmediateRenderer + Observable.Repeat(Observable.Timer(TimeSpan.FromMilliseconds(16))) + .SubscribeOn(AvaloniaScheduler.Instance) + .Subscribe((x) => + { + if (coalescedRect != Rect.Empty) { + Dispatcher.UIThread.Post(() => Paint(coalescedRect), DispatcherPriority.Render); + coalescedRect = Rect.Empty; + } + }); } public WindowState WindowState { get; set; } @@ -37,6 +52,12 @@ namespace Avalonia.Windowing } } + public bool timeToPaint = false; + + public void Test() { + timeToPaint = true; + } + public Action PositionChanged { get; set; } public Action Deactivated { get; set; } public Action Activated { get; set; } @@ -66,7 +87,8 @@ namespace Avalonia.Windowing public Action ScalingChanged { get; set; } public Action Closed { get; set; } - public IMouseDevice MouseDevice => new MouseDevice(); + public IMouseDevice MouseDevice => AvaloniaLocator.Current.GetService(); + public IKeyboardDevice KeyboardDevice => AvaloniaLocator.Current.GetService(); public void Activate() { @@ -99,9 +121,10 @@ namespace Avalonia.Windowing _windowWrapper.Hide(); } + private Rect coalescedRect = Rect.Empty; public void Invalidate(Rect rect) { - Paint?.Invoke(rect); + coalescedRect = coalescedRect.Union(rect); } public Point PointToClient(Point point) @@ -121,9 +144,7 @@ namespace Avalonia.Windowing if (clientSize == ClientSize) return; - // This is where we size the window accordingly.. _windowWrapper.SetSize(clientSize.Width, clientSize.Height); - //Resized(clientSize); } public void SetCursor(IPlatformHandle cursor) @@ -171,13 +192,13 @@ namespace Avalonia.Windowing public void OnKeyboardEvent (KeyboardEvent evt) { - + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); + Input(new RawKeyEventArgs(KeyboardDevice, (uint)Environment.TickCount, evt.Pressed == 1 ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, KeyTransform.TransformKeyCode(evt.VirtualKeyCode).Value, InputModifiers.None)); } public void OnMouseEvent(MouseEvent evt) { - Dispatcher.UIThread.RunJobs(DispatcherPriority.Input); - + Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1); switch(evt.EventType) { case MouseEventType.Move: @@ -185,7 +206,7 @@ namespace Avalonia.Windowing break; case MouseEventType.Wheel: - Input(new RawMouseWheelEventArgs(MouseDevice, (uint)Environment.TickCount, _inputRoot, new Point(_lastPosition.X, _lastPosition.Y), new Point(evt.Position.X / 50, evt.Position.Y / 50), InputModifiers.None)); + Input(new RawMouseWheelEventArgs(MouseDevice, (uint)Environment.TickCount, _inputRoot, new Point(_lastPosition.X, _lastPosition.Y), new Point(evt.Position.X / 50, evt.Position.Y), InputModifiers.None)); return; } diff --git a/src/Skia/Avalonia.Skia/GlRenderTarget.cs b/src/Skia/Avalonia.Skia/GlRenderTarget.cs index 4c1bb8def5..26b5435a8d 100644 --- a/src/Skia/Avalonia.Skia/GlRenderTarget.cs +++ b/src/Skia/Avalonia.Skia/GlRenderTarget.cs @@ -41,19 +41,17 @@ namespace Avalonia.Skia Width = (int)width * 2, SampleCount = 1, StencilBits = 8, - Config = GRPixelConfig.Rgba8888, + Config = GRPixelConfig.Bgra8888, Origin = GRSurfaceOrigin.BottomLeft, RenderTargetHandle = IntPtr.Zero }; + _canvas?.Dispose(); _surface?.Dispose(); _surface = SKSurface.Create(_grContext, _desc); - _canvas?.Dispose(); _canvas = _surface.Canvas; } - _canvas.Clear(SKColors.Orange); - var createInfo = new DrawingContextImpl.CreateInfo { Canvas = _canvas, @@ -64,7 +62,7 @@ namespace Avalonia.Skia return new DrawingContextImpl(createInfo, Disposable.Create(() => { - _canvas.Flush(); + // _canvas.Flush(); _grContext.Flush(); _context.Present(); // Swap Buffers }));