csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
345 lines
10 KiB
345 lines
10 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using Avalonia.Automation.Peers;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Platform;
|
|
using Avalonia.Controls.Platform.Surfaces;
|
|
using Avalonia.Controls.Primitives.PopupPositioning;
|
|
using Avalonia.Input;
|
|
using Avalonia.Input.Raw;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Platform;
|
|
using Avalonia.Platform.Storage;
|
|
using Avalonia.Rendering;
|
|
using Avalonia.Threading;
|
|
using Avalonia.Utilities;
|
|
|
|
namespace Avalonia.Headless
|
|
{
|
|
class HeadlessWindowImpl : IWindowImpl, IPopupImpl, IFramebufferPlatformSurface, IHeadlessWindow, ITopLevelImplWithStorageProvider
|
|
{
|
|
private IKeyboardDevice _keyboard;
|
|
private Stopwatch _st = Stopwatch.StartNew();
|
|
private Pointer _mousePointer;
|
|
private WriteableBitmap _lastRenderedFrame;
|
|
private object _sync = new object();
|
|
public bool IsPopup { get; }
|
|
|
|
public HeadlessWindowImpl(bool isPopup)
|
|
{
|
|
IsPopup = isPopup;
|
|
Surfaces = new object[] { this };
|
|
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
|
|
_mousePointer = new Pointer(Pointer.GetNextFreeId(), PointerType.Mouse, true);
|
|
MouseDevice = new MouseDevice(_mousePointer);
|
|
ClientSize = new Size(1024, 768);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Closed?.Invoke();
|
|
_lastRenderedFrame?.Dispose();
|
|
_lastRenderedFrame = null;
|
|
}
|
|
|
|
public Size ClientSize { get; set; }
|
|
public Size? FrameSize => null;
|
|
public double RenderScaling { get; } = 1;
|
|
public double DesktopScaling => RenderScaling;
|
|
public IEnumerable<object> Surfaces { get; }
|
|
public Action<RawInputEventArgs> Input { get; set; }
|
|
public Action<Rect> Paint { get; set; }
|
|
public Action<Size, PlatformResizeReason> Resized { get; set; }
|
|
public Action<double> ScalingChanged { get; set; }
|
|
|
|
public IRenderer CreateRenderer(IRenderRoot root)
|
|
=> new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
|
|
|
|
public void Invalidate(Rect rect)
|
|
{
|
|
}
|
|
|
|
public void SetInputRoot(IInputRoot inputRoot)
|
|
{
|
|
InputRoot = inputRoot;
|
|
}
|
|
|
|
public IInputRoot InputRoot { get; set; }
|
|
|
|
public Point PointToClient(PixelPoint point) => point.ToPoint(RenderScaling);
|
|
|
|
public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, RenderScaling);
|
|
|
|
public void SetCursor(ICursorImpl cursor)
|
|
{
|
|
|
|
}
|
|
|
|
public Action Closed { get; set; }
|
|
public IMouseDevice MouseDevice { get; }
|
|
|
|
public void Show(bool activate, bool isDialog)
|
|
{
|
|
if (activate)
|
|
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
|
|
}
|
|
|
|
public void Hide()
|
|
{
|
|
Dispatcher.UIThread.Post(() => Deactivated?.Invoke(), DispatcherPriority.Input);
|
|
}
|
|
|
|
public void BeginMoveDrag()
|
|
{
|
|
|
|
}
|
|
|
|
public void BeginResizeDrag(WindowEdge edge)
|
|
{
|
|
|
|
}
|
|
|
|
public PixelPoint Position { get; set; }
|
|
public Action<PixelPoint> PositionChanged { get; set; }
|
|
public void Activate()
|
|
{
|
|
Dispatcher.UIThread.Post(() => Activated?.Invoke(), DispatcherPriority.Input);
|
|
}
|
|
|
|
public Action Deactivated { get; set; }
|
|
public Action Activated { get; set; }
|
|
public IPlatformHandle Handle { get; } = new PlatformHandle(IntPtr.Zero, "STUB");
|
|
public Size MaxClientSize { get; } = new Size(1920, 1280);
|
|
public void Resize(Size clientSize, PlatformResizeReason reason)
|
|
{
|
|
// Emulate X11 behavior here
|
|
if (IsPopup)
|
|
DoResize(clientSize);
|
|
else
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
DoResize(clientSize);
|
|
});
|
|
}
|
|
|
|
void DoResize(Size clientSize)
|
|
{
|
|
// Uncomment this check and experience a weird bug in layout engine
|
|
if (ClientSize != clientSize)
|
|
{
|
|
ClientSize = clientSize;
|
|
Resized?.Invoke(clientSize, PlatformResizeReason.Unspecified);
|
|
}
|
|
}
|
|
|
|
public void SetMinMaxSize(Size minSize, Size maxSize)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetTopmost(bool value)
|
|
{
|
|
|
|
}
|
|
|
|
public IScreenImpl Screen { get; } = new HeadlessScreensStub();
|
|
public WindowState WindowState { get; set; }
|
|
public Action<WindowState> WindowStateChanged { get; set; }
|
|
public void SetTitle(string title)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetSystemDecorations(bool enabled)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetIcon(IWindowIconImpl icon)
|
|
{
|
|
|
|
}
|
|
|
|
public void ShowTaskbarIcon(bool value)
|
|
{
|
|
|
|
}
|
|
|
|
public void CanResize(bool value)
|
|
{
|
|
|
|
}
|
|
|
|
public Func<bool> Closing { get; set; }
|
|
|
|
class FramebufferProxy : ILockedFramebuffer
|
|
{
|
|
private readonly ILockedFramebuffer _fb;
|
|
private readonly Action _onDispose;
|
|
private bool _disposed;
|
|
|
|
public FramebufferProxy(ILockedFramebuffer fb, Action onDispose)
|
|
{
|
|
_fb = fb;
|
|
_onDispose = onDispose;
|
|
}
|
|
public void Dispose()
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
_disposed = true;
|
|
_fb.Dispose();
|
|
_onDispose();
|
|
}
|
|
|
|
public IntPtr Address => _fb.Address;
|
|
public PixelSize Size => _fb.Size;
|
|
public int RowBytes => _fb.RowBytes;
|
|
public Vector Dpi => _fb.Dpi;
|
|
public PixelFormat Format => _fb.Format;
|
|
}
|
|
|
|
public ILockedFramebuffer Lock()
|
|
{
|
|
var bmp = new WriteableBitmap(PixelSize.FromSize(ClientSize, RenderScaling), new Vector(96, 96) * RenderScaling, PixelFormat.Rgba8888, AlphaFormat.Premul);
|
|
var fb = bmp.Lock();
|
|
return new FramebufferProxy(fb, () =>
|
|
{
|
|
lock (_sync)
|
|
{
|
|
_lastRenderedFrame?.Dispose();
|
|
_lastRenderedFrame = bmp;
|
|
}
|
|
});
|
|
}
|
|
|
|
public IRef<IWriteableBitmapImpl> GetLastRenderedFrame()
|
|
{
|
|
lock (_sync)
|
|
return _lastRenderedFrame?.PlatformImpl?.CloneAs<IWriteableBitmapImpl>();
|
|
}
|
|
|
|
private ulong Timestamp => (ulong)_st.ElapsedMilliseconds;
|
|
|
|
// TODO: Hook recent Popup changes.
|
|
IPopupPositioner IPopupImpl.PopupPositioner => null;
|
|
|
|
public Size MaxAutoSizeHint => new Size(1920, 1080);
|
|
|
|
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
|
|
|
|
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;
|
|
|
|
public Action GotInputWhenDisabled { get; set; }
|
|
|
|
public bool IsClientAreaExtendedToDecorations => false;
|
|
|
|
public Action<bool> ExtendClientAreaToDecorationsChanged { get; set; }
|
|
|
|
public bool NeedsManagedDecorations => false;
|
|
|
|
public Thickness ExtendedMargins => new Thickness();
|
|
|
|
public Thickness OffScreenMargin => new Thickness();
|
|
|
|
public Action LostFocus { get; set; }
|
|
|
|
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels => new AcrylicPlatformCompensationLevels(1, 1, 1);
|
|
|
|
public IStorageProvider StorageProvider => new NoopStorageProvider();
|
|
|
|
void IHeadlessWindow.KeyPress(Key key, RawInputModifiers modifiers)
|
|
{
|
|
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyDown, key, modifiers));
|
|
}
|
|
|
|
void IHeadlessWindow.KeyRelease(Key key, RawInputModifiers modifiers)
|
|
{
|
|
Input?.Invoke(new RawKeyEventArgs(_keyboard, Timestamp, InputRoot, RawKeyEventType.KeyUp, key, modifiers));
|
|
}
|
|
|
|
void IHeadlessWindow.MouseDown(Point point, int button, RawInputModifiers modifiers)
|
|
{
|
|
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot,
|
|
button == 0 ? RawPointerEventType.LeftButtonDown :
|
|
button == 1 ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.RightButtonDown,
|
|
point, modifiers));
|
|
}
|
|
|
|
void IHeadlessWindow.MouseMove(Point point, RawInputModifiers modifiers)
|
|
{
|
|
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot,
|
|
RawPointerEventType.Move, point, modifiers));
|
|
}
|
|
|
|
void IHeadlessWindow.MouseUp(Point point, int button, RawInputModifiers modifiers)
|
|
{
|
|
Input?.Invoke(new RawPointerEventArgs(MouseDevice, Timestamp, InputRoot,
|
|
button == 0 ? RawPointerEventType.LeftButtonUp :
|
|
button == 1 ? RawPointerEventType.MiddleButtonUp : RawPointerEventType.RightButtonUp,
|
|
point, modifiers));
|
|
}
|
|
|
|
void IWindowImpl.Move(PixelPoint point)
|
|
{
|
|
|
|
}
|
|
|
|
public IPopupImpl CreatePopup()
|
|
{
|
|
// TODO: Hook recent Popup changes.
|
|
return null;
|
|
}
|
|
|
|
public void SetWindowManagerAddShadowHint(bool enabled)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetParent(IWindowImpl parent)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetEnabled(bool enable)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetSystemDecorations(SystemDecorations enabled)
|
|
{
|
|
|
|
}
|
|
|
|
public void BeginMoveDrag(PointerPressedEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
public void BeginResizeDrag(WindowEdge edge, PointerPressedEventArgs e)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetExtendClientAreaToDecorationsHint(bool extendIntoClientAreaHint)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetExtendClientAreaChromeHints(ExtendClientAreaChromeHints hints)
|
|
{
|
|
|
|
}
|
|
|
|
public void SetExtendClientAreaTitleBarHeightHint(double titleBarHeight)
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|
|
|