@ -17,20 +17,20 @@ using Avalonia.Utilities;
namespace Avalonia.Headless
namespace Avalonia.Headless
{
{
class HeadlessWindowImpl : IWindowImpl , IPopupImpl , IFramebufferPlatformSurface , IHeadlessWindow
internal class HeadlessWindowImpl : IWindowImpl , IPopupImpl , IFramebufferPlatformSurface , IHeadlessWindow
{
{
private IKeyboardDevice _ keyboard ;
private readonly IKeyboardDevice _ keyboard ;
private Stopwatch _ st = Stopwatch . StartNew ( ) ;
private readonly Stopwatch _ st = Stopwatch . StartNew ( ) ;
private Pointer _ mousePointer ;
private readonly Pointer _ mousePointer ;
private WriteableBitmap _l astRenderedFrame ;
private WriteableBitmap ? _l astRenderedFrame ;
private object _ sync = new object ( ) ;
private readonly object _ sync = new object ( ) ;
public bool IsPopup { get ; }
public bool IsPopup { get ; }
public HeadlessWindowImpl ( bool isPopup )
public HeadlessWindowImpl ( bool isPopup )
{
{
IsPopup = isPopup ;
IsPopup = isPopup ;
Surfaces = new object [ ] { this } ;
Surfaces = new object [ ] { this } ;
_ keyboard = AvaloniaLocator . Current . GetService < IKeyboardDevice > ( ) ;
_ keyboard = AvaloniaLocator . Current . GetRequired Service < IKeyboardDevice > ( ) ;
_ mousePointer = new Pointer ( Pointer . GetNextFreeId ( ) , PointerType . Mouse , true ) ;
_ mousePointer = new Pointer ( Pointer . GetNextFreeId ( ) , PointerType . Mouse , true ) ;
MouseDevice = new MouseDevice ( _ mousePointer ) ;
MouseDevice = new MouseDevice ( _ mousePointer ) ;
ClientSize = new Size ( 1 0 2 4 , 7 6 8 ) ;
ClientSize = new Size ( 1 0 2 4 , 7 6 8 ) ;
@ -48,13 +48,13 @@ namespace Avalonia.Headless
public double RenderScaling { get ; } = 1 ;
public double RenderScaling { get ; } = 1 ;
public double DesktopScaling = > RenderScaling ;
public double DesktopScaling = > RenderScaling ;
public IEnumerable < object > Surfaces { get ; }
public IEnumerable < object > Surfaces { get ; }
public Action < RawInputEventArgs > Input { get ; set ; }
public Action < RawInputEventArgs > ? Input { get ; set ; }
public Action < Rect > Paint { get ; set ; }
public Action < Rect > ? Paint { get ; set ; }
public Action < Size , WindowResizeReason > Resized { get ; set ; }
public Action < Size , WindowResizeReason > ? Resized { get ; set ; }
public Action < double > ScalingChanged { get ; set ; }
public Action < double > ? ScalingChanged { get ; set ; }
public IRenderer CreateRenderer ( IRenderRoot root ) = >
public IRenderer CreateRenderer ( IRenderRoot root ) = >
new CompositingRenderer ( root , AvaloniaHeadlessPlatform . Compositor , ( ) = > Surfaces ) ;
new CompositingRenderer ( root , AvaloniaHeadlessPlatform . Compositor ! , ( ) = > Surfaces ) ;
public void Invalidate ( Rect rect )
public void Invalidate ( Rect rect )
{
{
@ -65,18 +65,18 @@ namespace Avalonia.Headless
InputRoot = inputRoot ;
InputRoot = inputRoot ;
}
}
public IInputRoot InputRoot { get ; set ; }
public IInputRoot ? InputRoot { get ; set ; }
public Point PointToClient ( PixelPoint point ) = > point . ToPoint ( RenderScaling ) ;
public Point PointToClient ( PixelPoint point ) = > point . ToPoint ( RenderScaling ) ;
public PixelPoint PointToScreen ( Point point ) = > PixelPoint . FromPoint ( point , RenderScaling ) ;
public PixelPoint PointToScreen ( Point point ) = > PixelPoint . FromPoint ( point , RenderScaling ) ;
public void SetCursor ( ICursorImpl cursor )
public void SetCursor ( ICursorImpl ? cursor )
{
{
}
}
public Action Closed { get ; set ; }
public Action ? Closed { get ; set ; }
public IMouseDevice MouseDevice { get ; }
public IMouseDevice MouseDevice { get ; }
public void Show ( bool activate , bool isDialog )
public void Show ( bool activate , bool isDialog )
@ -101,14 +101,14 @@ namespace Avalonia.Headless
}
}
public PixelPoint Position { get ; set ; }
public PixelPoint Position { get ; set ; }
public Action < PixelPoint > PositionChanged { get ; set ; }
public Action < PixelPoint > ? PositionChanged { get ; set ; }
public void Activate ( )
public void Activate ( )
{
{
Dispatcher . UIThread . Post ( ( ) = > Activated ? . Invoke ( ) , DispatcherPriority . Input ) ;
Dispatcher . UIThread . Post ( ( ) = > Activated ? . Invoke ( ) , DispatcherPriority . Input ) ;
}
}
public Action Deactivated { get ; set ; }
public Action ? Deactivated { get ; set ; }
public Action Activated { get ; set ; }
public Action ? Activated { get ; set ; }
public IPlatformHandle Handle { get ; } = new PlatformHandle ( IntPtr . Zero , "STUB" ) ;
public IPlatformHandle Handle { get ; } = new PlatformHandle ( IntPtr . Zero , "STUB" ) ;
public Size MaxClientSize { get ; } = new Size ( 1 9 2 0 , 1 2 8 0 ) ;
public Size MaxClientSize { get ; } = new Size ( 1 9 2 0 , 1 2 8 0 ) ;
public void Resize ( Size clientSize , WindowResizeReason reason )
public void Resize ( Size clientSize , WindowResizeReason reason )
@ -123,7 +123,7 @@ namespace Avalonia.Headless
} ) ;
} ) ;
}
}
void DoResize ( Size clientSize )
private void DoResize ( Size clientSize )
{
{
// Uncomment this check and experience a weird bug in layout engine
// Uncomment this check and experience a weird bug in layout engine
if ( ClientSize ! = clientSize )
if ( ClientSize ! = clientSize )
@ -145,8 +145,8 @@ namespace Avalonia.Headless
public IScreenImpl Screen { get ; } = new HeadlessScreensStub ( ) ;
public IScreenImpl Screen { get ; } = new HeadlessScreensStub ( ) ;
public WindowState WindowState { get ; set ; }
public WindowState WindowState { get ; set ; }
public Action < WindowState > WindowStateChanged { get ; set ; }
public Action < WindowState > ? WindowStateChanged { get ; set ; }
public void SetTitle ( string title )
public void SetTitle ( string? title )
{
{
}
}
@ -156,7 +156,7 @@ namespace Avalonia.Headless
}
}
public void SetIcon ( IWindowIconImpl icon )
public void SetIcon ( IWindowIconImpl ? icon )
{
{
}
}
@ -171,9 +171,9 @@ namespace Avalonia.Headless
}
}
public Func < WindowCloseReason , bool > Closing { get ; set ; }
public Func < WindowCloseReason , bool > ? Closing { get ; set ; }
class FramebufferProxy : ILockedFramebuffer
private class FramebufferProxy : ILockedFramebuffer
{
{
private readonly ILockedFramebuffer _f b ;
private readonly ILockedFramebuffer _f b ;
private readonly Action _ onDispose ;
private readonly Action _ onDispose ;
@ -214,28 +214,37 @@ namespace Avalonia.Headless
} ) ;
} ) ;
}
}
public IRef < IWriteableBitmapImpl > GetLastRenderedFrame ( )
public Bitmap ? GetLastRenderedFrame ( )
{
{
lock ( _ sync )
lock ( _ sync )
return _l astRenderedFrame ? . PlatformImpl ? . CloneAs < IWriteableBitmapImpl > ( ) ;
{
if ( _l astRenderedFrame is null )
{
return null ;
}
using var lockedFramebuffer = _l astRenderedFrame . Lock ( ) ;
return new Bitmap ( lockedFramebuffer . Format , AlphaFormat . Opaque , lockedFramebuffer . Address ,
lockedFramebuffer . Size , lockedFramebuffer . Dpi , lockedFramebuffer . RowBytes ) ;
}
}
}
private ulong Timestamp = > ( ulong ) _ st . ElapsedMilliseconds ;
private ulong Timestamp = > ( ulong ) _ st . ElapsedMilliseconds ;
// TODO: Hook recent Popup changes.
// TODO: Hook recent Popup changes.
IPopupPositioner IPopupImpl . PopupPositioner = > null ;
IPopupPositioner IPopupImpl . PopupPositioner = > null ! ;
public Size MaxAutoSizeHint = > new Size ( 1 9 2 0 , 1 0 8 0 ) ;
public Size MaxAutoSizeHint = > new Size ( 1 9 2 0 , 1 0 8 0 ) ;
public Action < WindowTransparencyLevel > TransparencyLevelChanged { get ; set ; }
public Action < WindowTransparencyLevel > ? TransparencyLevelChanged { get ; set ; }
public WindowTransparencyLevel TransparencyLevel = > WindowTransparencyLevel . None ;
public WindowTransparencyLevel TransparencyLevel = > WindowTransparencyLevel . None ;
public Action GotInputWhenDisabled { get ; set ; }
public Action ? GotInputWhenDisabled { get ; set ; }
public bool IsClientAreaExtendedToDecorations = > false ;
public bool IsClientAreaExtendedToDecorations = > false ;
public Action < bool > ExtendClientAreaToDecorationsChanged { get ; set ; }
public Action < bool > ? ExtendClientAreaToDecorationsChanged { get ; set ; }
public bool NeedsManagedDecorations = > false ;
public bool NeedsManagedDecorations = > false ;
@ -243,17 +252,12 @@ namespace Avalonia.Headless
public Thickness OffScreenMargin = > new Thickness ( ) ;
public Thickness OffScreenMargin = > new Thickness ( ) ;
public Action LostFocus { get ; set ; }
public Action ? LostFocus { get ; set ; }
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels = > new AcrylicPlatformCompensationLevels ( 1 , 1 , 1 ) ;
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels = > new AcrylicPlatformCompensationLevels ( 1 , 1 , 1 ) ;
public object TryGetFeature ( Type featureType )
public object? TryGetFeature ( Type featureType )
{
{
if ( featureType = = typeof ( IStorageProvider ) )
if ( featureType = = typeof ( IClipboard ) )
{
return new NoopStorageProvider ( ) ;
}
if ( featureType = = typeof ( IClipboard ) )
{
{
return AvaloniaLocator . Current . GetRequiredService < IClipboard > ( ) ;
return AvaloniaLocator . Current . GetRequiredService < IClipboard > ( ) ;
}
}
@ -263,46 +267,58 @@ namespace Avalonia.Headless
void IHeadlessWindow . KeyPress ( Key key , RawInputModifiers modifiers )
void IHeadlessWindow . KeyPress ( Key key , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawKeyEventArgs ( _ keyboard , Timestamp , InputRoot , RawKeyEventType . KeyDown , key , modifiers ) ) ;
Input ? . Invoke ( new RawKeyEventArgs ( _ keyboard , Timestamp , InputRoot ! , RawKeyEventType . KeyDown , key , modifiers ) ) ;
}
}
void IHeadlessWindow . KeyRelease ( Key key , RawInputModifiers modifiers )
void IHeadlessWindow . KeyRelease ( Key key , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawKeyEventArgs ( _ keyboard , Timestamp , InputRoot , RawKeyEventType . KeyUp , key , modifiers ) ) ;
Input ? . Invoke ( new RawKeyEventArgs ( _ keyboard , Timestamp , InputRoot ! , RawKeyEventType . KeyUp , key , modifiers ) ) ;
}
}
void IHeadlessWindow . MouseDown ( Point point , int button , RawInputModifiers modifiers )
void IHeadlessWindow . MouseDown ( Point point , MouseButton button , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ,
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ! ,
button = = 0 ? RawPointerEventType . LeftButtonDown :
button switch
button = = 1 ? RawPointerEventType . MiddleButtonDown : RawPointerEventType . RightButtonDown ,
{
point , modifiers ) ) ;
MouseButton . Left = > RawPointerEventType . LeftButtonDown ,
MouseButton . Right = > RawPointerEventType . RightButtonDown ,
MouseButton . Middle = > RawPointerEventType . MiddleButtonDown ,
MouseButton . XButton1 = > RawPointerEventType . XButton1Down ,
MouseButton . XButton2 = > RawPointerEventType . XButton2Down ,
_ = > RawPointerEventType . Move ,
} , point , modifiers ) ) ;
}
}
void IHeadlessWindow . MouseMove ( Point point , RawInputModifiers modifiers )
void IHeadlessWindow . MouseMove ( Point point , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ,
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ! ,
RawPointerEventType . Move , point , modifiers ) ) ;
RawPointerEventType . Move , point , modifiers ) ) ;
}
}
void IHeadlessWindow . MouseUp ( Point point , int button , RawInputModifiers modifiers )
void IHeadlessWindow . MouseUp ( Point point , MouseButton button , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ,
Input ? . Invoke ( new RawPointerEventArgs ( MouseDevice , Timestamp , InputRoot ! ,
button = = 0 ? RawPointerEventType . LeftButtonUp :
button switch
button = = 1 ? RawPointerEventType . MiddleButtonUp : RawPointerEventType . RightButtonUp ,
{
point , modifiers ) ) ;
MouseButton . Left = > RawPointerEventType . LeftButtonUp ,
MouseButton . Right = > RawPointerEventType . RightButtonUp ,
MouseButton . Middle = > RawPointerEventType . MiddleButtonUp ,
MouseButton . XButton1 = > RawPointerEventType . XButton1Up ,
MouseButton . XButton2 = > RawPointerEventType . XButton2Up ,
_ = > RawPointerEventType . Move ,
} , point , modifiers ) ) ;
}
}
void IHeadlessWindow . MouseWheel ( Point point , Vector delta , RawInputModifiers modifiers )
void IHeadlessWindow . MouseWheel ( Point point , Vector delta , RawInputModifiers modifiers )
{
{
Input ? . Invoke ( new RawMouseWheelEventArgs ( MouseDevice , Timestamp , InputRoot ,
Input ? . Invoke ( new RawMouseWheelEventArgs ( MouseDevice , Timestamp , InputRoot ! ,
point , delta , modifiers ) ) ;
point , delta , modifiers ) ) ;
}
}
void IHeadlessWindow . DragDrop ( Point point , RawDragEventType type , IDataObject data , DragDropEffects effects , RawInputModifiers modifiers )
void IHeadlessWindow . DragDrop ( Point point , RawDragEventType type , IDataObject data , DragDropEffects effects , RawInputModifiers modifiers )
{
{
var device = AvaloniaLocator . Current . GetRequiredService < IDragDropDevice > ( ) ;
var device = AvaloniaLocator . Current . GetRequiredService < IDragDropDevice > ( ) ;
Input ? . Invoke ( new RawDragEvent ( device , type , InputRoot , point , data , effects , modifiers ) ) ;
Input ? . Invoke ( new RawDragEvent ( device , type , InputRoot ! , point , data , effects , modifiers ) ) ;
}
}
void IWindowImpl . Move ( PixelPoint point )
void IWindowImpl . Move ( PixelPoint point )
@ -310,7 +326,7 @@ namespace Avalonia.Headless
}
}
public IPopupImpl CreatePopup ( )
public IPopupImpl ? CreatePopup ( )
{
{
// TODO: Hook recent Popup changes.
// TODO: Hook recent Popup changes.
return null ;
return null ;