Browse Source

Implement invalid rect coalescing to work around the lack of an OS driven invalidation mechanism in winit

repro-window-close
Nelson Carrillo 8 years ago
parent
commit
1635986f05
  1. 0
      src/Avalonia.Base/Platform/AvaloniaSynchronizationContext.cs
  2. 5
      src/Avalonia.Windowing/Bindings/EventNotifier.cs
  3. 12
      src/Avalonia.Windowing/Bindings/Events.cs
  4. 2
      src/Avalonia.Windowing/Bindings/EventsLoop.cs
  5. 211
      src/Avalonia.Windowing/KeyTransform.cs
  6. 35
      src/Avalonia.Windowing/WIndowingPlatform.cs
  7. 37
      src/Avalonia.Windowing/WindowImpl.cs
  8. 8
      src/Skia/Avalonia.Skia/GlRenderTarget.cs

0
src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs → src/Avalonia.Base/Platform/AvaloniaSynchronizationContext.cs

5
src/Avalonia.Windowing/Bindings/EventNotifier.cs

@ -1,13 +1,16 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security;
namespace Avalonia.Windowing.Bindings namespace Avalonia.Windowing.Bindings
{ {
public delegate void MouseEventCallback(IntPtr windowId, MouseEvent mouseEvent); public delegate void MouseEventCallback(IntPtr windowId, MouseEvent mouseEvent);
public delegate void ResizeEventCallback(IntPtr windowId, ResizeEvent resizeEvent); public delegate void ResizeEventCallback(IntPtr windowId, ResizeEvent resizeEvent);
public delegate void KeyboardEventCallback(IntPtr windowId, KeyboardEvent keyboardEvent); public delegate void KeyboardEventCallback(IntPtr windowId, KeyboardEvent keyboardEvent);
public delegate void AwakenedEventCallback();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void AwakenedEventCallback();
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct EventNotifier public struct EventNotifier
{ {

12
src/Avalonia.Windowing/Bindings/Events.cs

@ -27,12 +27,12 @@ namespace Avalonia.Windowing.Bindings
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct KeyboardEvent public struct KeyboardEvent
{ {
public bool Pressed { get; set; } public byte Pressed { get; set; }
public bool Shift { get; set; } public byte Shift { get; set; }
public bool Control { get; set; } public byte Control { get; set; }
public bool Alt { get; set; } public byte Alt { get; set; }
public bool Logo { get; set; } public byte Logo { get; set; }
UInt32 VirtualKeyCode { get; set; } public VirtualKeyCode VirtualKeyCode { get; set; }
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]

2
src/Avalonia.Windowing/Bindings/EventsLoop.cs

@ -33,7 +33,9 @@ namespace Avalonia.Windowing.Bindings
public event KeyboardEventCallback OnKeyboardEvent; public event KeyboardEventCallback OnKeyboardEvent;
public event MouseEventCallback OnMouseEvent; public event MouseEventCallback OnMouseEvent;
public event AwakenedEventCallback OnAwakened; public event AwakenedEventCallback OnAwakened;
public event ResizeEventCallback OnResized; public event ResizeEventCallback OnResized;
public EventsLoop() public EventsLoop()

211
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<VirtualKeyCode, Key> Keys = new Dictionary<VirtualKeyCode, Key>
{
[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;
}
}
}

35
src/Avalonia.Windowing/WIndowingPlatform.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
@ -69,11 +70,17 @@ namespace Avalonia.Windowing
} }
} }
private bool _signaled;
private void _eventsLoop_Awakened() private void _eventsLoop_Awakened()
{ {
Signaled?.Invoke(DispatcherPriority.Input); lock (this)
Signaled?.Invoke(DispatcherPriority.Layout); {
Signaled?.Invoke(DispatcherPriority.Render); if (!_signaled)
return;
_signaled = false;
}
Signaled?.Invoke(null);
} }
@ -83,11 +90,15 @@ namespace Avalonia.Windowing
Instance.DoInitialize(); Instance.DoInitialize();
} }
private readonly IKeyboardDevice keyboardDevice = new KeyboardDevice();
private readonly IMouseDevice mouseDevice = new MouseDevice();
public void DoInitialize() public void DoInitialize()
{ {
AvaloniaLocator.CurrentMutable AvaloniaLocator.CurrentMutable
.Bind<IWindowingPlatform>().ToConstant(this) .Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IMouseDevice>().ToConstant(new MouseDevice()) .Bind<IKeyboardDevice>().ToConstant(keyboardDevice)
.Bind<IMouseDevice>().ToConstant(mouseDevice)
.Bind<IPlatformIconLoader>().ToConstant(new IconLoader()) .Bind<IPlatformIconLoader>().ToConstant(new IconLoader())
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactory()) .Bind<IStandardCursorFactory>().ToConstant(new CursorFactory())
.Bind<IPlatformSettings>().ToConstant(new PlatformSettings()) .Bind<IPlatformSettings>().ToConstant(new PlatformSettings())
@ -127,7 +138,13 @@ namespace Avalonia.Windowing
public void Signal(DispatcherPriority priority) 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(); _eventsLoop.Wakeup();
} }
@ -135,10 +152,12 @@ namespace Avalonia.Windowing
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{ {
var x = new TimerDel(tick); // TODO: Need a way to cancel a timer when Dispose is called.
timerTickers.Add(x);
// var x = new TimerDel(tick);
// timerTickers.Add(x);
_eventsLoop.RunTimer(x); // _eventsLoop.RunTimer(x);
return Disposable.Create(() => { return Disposable.Create(() => {
// timerTickers.Remove(x); // timerTickers.Remove(x);
}); });

37
src/Avalonia.Windowing/WindowImpl.cs

@ -1,5 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Gpu; using Avalonia.Gpu;
using Avalonia.Input; using Avalonia.Input;
@ -19,6 +23,17 @@ namespace Avalonia.Windowing
public WindowImpl(IWindowWrapper wrapper) public WindowImpl(IWindowWrapper wrapper)
{ {
_windowWrapper = 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; } public WindowState WindowState { get; set; }
@ -37,6 +52,12 @@ namespace Avalonia.Windowing
} }
} }
public bool timeToPaint = false;
public void Test() {
timeToPaint = true;
}
public Action<Point> PositionChanged { get; set; } public Action<Point> PositionChanged { get; set; }
public Action Deactivated { get; set; } public Action Deactivated { get; set; }
public Action Activated { get; set; } public Action Activated { get; set; }
@ -66,7 +87,8 @@ namespace Avalonia.Windowing
public Action<double> ScalingChanged { get; set; } public Action<double> ScalingChanged { get; set; }
public Action Closed { get; set; } public Action Closed { get; set; }
public IMouseDevice MouseDevice => new MouseDevice(); public IMouseDevice MouseDevice => AvaloniaLocator.Current.GetService<IMouseDevice>();
public IKeyboardDevice KeyboardDevice => AvaloniaLocator.Current.GetService<IKeyboardDevice>();
public void Activate() public void Activate()
{ {
@ -99,9 +121,10 @@ namespace Avalonia.Windowing
_windowWrapper.Hide(); _windowWrapper.Hide();
} }
private Rect coalescedRect = Rect.Empty;
public void Invalidate(Rect rect) public void Invalidate(Rect rect)
{ {
Paint?.Invoke(rect); coalescedRect = coalescedRect.Union(rect);
} }
public Point PointToClient(Point point) public Point PointToClient(Point point)
@ -121,9 +144,7 @@ namespace Avalonia.Windowing
if (clientSize == ClientSize) if (clientSize == ClientSize)
return; return;
// This is where we size the window accordingly..
_windowWrapper.SetSize(clientSize.Width, clientSize.Height); _windowWrapper.SetSize(clientSize.Width, clientSize.Height);
//Resized(clientSize);
} }
public void SetCursor(IPlatformHandle cursor) public void SetCursor(IPlatformHandle cursor)
@ -171,13 +192,13 @@ namespace Avalonia.Windowing
public void OnKeyboardEvent (KeyboardEvent evt) 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) public void OnMouseEvent(MouseEvent evt)
{ {
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input); Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
switch(evt.EventType) switch(evt.EventType)
{ {
case MouseEventType.Move: case MouseEventType.Move:
@ -185,7 +206,7 @@ namespace Avalonia.Windowing
break; break;
case MouseEventType.Wheel: 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; return;
} }

8
src/Skia/Avalonia.Skia/GlRenderTarget.cs

@ -41,19 +41,17 @@ namespace Avalonia.Skia
Width = (int)width * 2, Width = (int)width * 2,
SampleCount = 1, SampleCount = 1,
StencilBits = 8, StencilBits = 8,
Config = GRPixelConfig.Rgba8888, Config = GRPixelConfig.Bgra8888,
Origin = GRSurfaceOrigin.BottomLeft, Origin = GRSurfaceOrigin.BottomLeft,
RenderTargetHandle = IntPtr.Zero RenderTargetHandle = IntPtr.Zero
}; };
_canvas?.Dispose();
_surface?.Dispose(); _surface?.Dispose();
_surface = SKSurface.Create(_grContext, _desc); _surface = SKSurface.Create(_grContext, _desc);
_canvas?.Dispose();
_canvas = _surface.Canvas; _canvas = _surface.Canvas;
} }
_canvas.Clear(SKColors.Orange);
var createInfo = new DrawingContextImpl.CreateInfo var createInfo = new DrawingContextImpl.CreateInfo
{ {
Canvas = _canvas, Canvas = _canvas,
@ -64,7 +62,7 @@ namespace Avalonia.Skia
return new DrawingContextImpl(createInfo, Disposable.Create(() => return new DrawingContextImpl(createInfo, Disposable.Create(() =>
{ {
_canvas.Flush(); // _canvas.Flush();
_grContext.Flush(); _grContext.Flush();
_context.Present(); // Swap Buffers _context.Present(); // Swap Buffers
})); }));

Loading…
Cancel
Save