| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- using System;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using Avalonia.Controls.Embedding.Offscreen;
- using Avalonia.Controls.Platform.Surfaces;
- using Avalonia.Input;
- using Avalonia.Input.Raw;
- using Avalonia.Layout;
- using Avalonia.Platform;
- using Avalonia.Remote.Protocol;
- using Avalonia.Remote.Protocol.Input;
- using Avalonia.Remote.Protocol.Viewport;
- using Avalonia.Threading;
- using InputModifiers = Avalonia.Input.InputModifiers;
- using Key = Avalonia.Input.Key;
- using PixelFormat = Avalonia.Platform.PixelFormat;
- using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat;
- namespace Avalonia.Controls.Remote.Server
- {
- public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface
- {
- private readonly IAvaloniaRemoteTransportConnection _transport;
- private LockedFramebuffer _framebuffer;
- private object _lock = new object();
- private long _lastSentFrame = -1;
- private long _lastReceivedFrame = -1;
- private long _nextFrameNumber = 1;
- private ClientViewportAllocatedMessage _pendingAllocation;
- private bool _invalidated;
- private Vector _dpi = new Vector(96, 96);
- private ProtocolPixelFormat[] _supportedFormats;
- public RemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport)
- {
- _transport = transport;
- _transport.OnMessage += OnMessage;
- KeyboardDevice = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
- }
- private static RawMouseEventType GetAvaloniaEventType (Avalonia.Remote.Protocol.Input.MouseButton button, bool pressed)
- {
- switch (button)
- {
- case Avalonia.Remote.Protocol.Input.MouseButton.Left:
- return pressed ? RawMouseEventType.LeftButtonDown : RawMouseEventType.LeftButtonUp;
- case Avalonia.Remote.Protocol.Input.MouseButton.Middle:
- return pressed ? RawMouseEventType.MiddleButtonDown : RawMouseEventType.MiddleButtonUp;
- case Avalonia.Remote.Protocol.Input.MouseButton.Right:
- return pressed ? RawMouseEventType.RightButtonDown : RawMouseEventType.RightButtonUp;
- default:
- return RawMouseEventType.Move;
- }
- }
- private static InputModifiers GetAvaloniaInputModifiers (Avalonia.Remote.Protocol.Input.InputModifiers[] modifiers)
- {
- var result = InputModifiers.None;
- foreach(var modifier in modifiers)
- {
- switch (modifier)
- {
- case Avalonia.Remote.Protocol.Input.InputModifiers.Control:
- result |= InputModifiers.Control;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.Alt:
- result |= InputModifiers.Alt;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.Shift:
- result |= InputModifiers.Shift;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.Windows:
- result |= InputModifiers.Windows;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.LeftMouseButton:
- result |= InputModifiers.LeftMouseButton;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.MiddleMouseButton:
- result |= InputModifiers.MiddleMouseButton;
- break;
- case Avalonia.Remote.Protocol.Input.InputModifiers.RightMouseButton:
- result |= InputModifiers.RightMouseButton;
- break;
- }
- }
- return result;
- }
- protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
- {
- lock (_lock)
- {
- if (obj is FrameReceivedMessage lastFrame)
- {
- lock (_lock)
- {
- _lastReceivedFrame = lastFrame.SequenceId;
- }
- Dispatcher.UIThread.Post(RenderIfNeeded);
- }
- if(obj is ClientRenderInfoMessage renderInfo)
- {
- lock(_lock)
- {
- _dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY);
- _invalidated = true;
- }
-
- Dispatcher.UIThread.Post(RenderIfNeeded);
- }
- if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
- {
- lock (_lock)
- _supportedFormats = supportedFormats.Formats;
- Dispatcher.UIThread.Post(RenderIfNeeded);
- }
- if (obj is MeasureViewportMessage measure)
- Dispatcher.UIThread.Post(() =>
- {
- var m = Measure(new Size(measure.Width, measure.Height));
- _transport.Send(new MeasureViewportMessage
- {
- Width = m.Width,
- Height = m.Height
- });
- });
- if (obj is ClientViewportAllocatedMessage allocated)
- {
- lock (_lock)
- {
- if (_pendingAllocation == null)
- Dispatcher.UIThread.Post(() =>
- {
- ClientViewportAllocatedMessage allocation;
- lock (_lock)
- {
- allocation = _pendingAllocation;
- _pendingAllocation = null;
- }
- _dpi = new Vector(allocation.DpiX, allocation.DpiY);
- ClientSize = new Size(allocation.Width, allocation.Height);
- RenderIfNeeded();
- });
- _pendingAllocation = allocated;
- }
- }
- if(obj is PointerMovedEventMessage pointer)
- {
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawMouseEventArgs(
- MouseDevice,
- 0,
- InputRoot,
- RawMouseEventType.Move,
- new Point(pointer.X, pointer.Y),
- GetAvaloniaInputModifiers(pointer.Modifiers)));
- }, DispatcherPriority.Input);
- }
- if(obj is PointerPressedEventMessage pressed)
- {
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawMouseEventArgs(
- MouseDevice,
- 0,
- InputRoot,
- GetAvaloniaEventType(pressed.Button, true),
- new Point(pressed.X, pressed.Y),
- GetAvaloniaInputModifiers(pressed.Modifiers)));
- }, DispatcherPriority.Input);
- }
- if (obj is PointerPressedEventMessage released)
- {
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawMouseEventArgs(
- MouseDevice,
- 0,
- InputRoot,
- GetAvaloniaEventType(released.Button, false),
- new Point(released.X, released.Y),
- GetAvaloniaInputModifiers(released.Modifiers)));
- }, DispatcherPriority.Input);
- }
- if(obj is ScrollEventMessage scroll)
- {
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawMouseWheelEventArgs(
- MouseDevice,
- 0,
- InputRoot,
- new Point(scroll.X, scroll.Y),
- new Vector(scroll.DeltaX, scroll.DeltaY),
- GetAvaloniaInputModifiers(scroll.Modifiers)));
- }, DispatcherPriority.Input);
- }
- if(obj is KeyEventMessage key)
- {
- Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawKeyEventArgs(
- KeyboardDevice,
- 0,
- key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
- (Key)key.Key,
- GetAvaloniaInputModifiers(key.Modifiers)));
- }, DispatcherPriority.Input);
- }
- if(obj is TextInputEventMessage text)
- {
- Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
- Dispatcher.UIThread.Post(() =>
- {
- Input?.Invoke(new RawTextInputEventArgs(
- KeyboardDevice,
- 0,
- text.Text));
- }, DispatcherPriority.Input);
- }
- }
- }
- protected void SetDpi(Vector dpi)
- {
- _dpi = dpi;
- RenderIfNeeded();
- }
- protected virtual Size Measure(Size constraint)
- {
- var l = (ILayoutable) InputRoot;
- l.Measure(constraint);
- return l.DesiredSize;
- }
- public override IEnumerable<object> Surfaces => new[] { this };
-
- FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format)
- {
- var scalingX = _dpi.X / 96.0;
- var scalingY = _dpi.Y / 96.0;
- width = (int)(width * scalingX);
- height = (int)(height * scalingY);
- var fmt = format ?? ProtocolPixelFormat.Rgba8888;
- var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
- var data = new byte[width * height * bpp];
- var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
- try
- {
- _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), width, height, width * bpp, _dpi, (PixelFormat)fmt,
- null);
- Paint?.Invoke(new Rect(0, 0, width, height));
- }
- finally
- {
- _framebuffer = null;
- handle.Free();
- }
- return new FrameMessage
- {
- Data = data,
- Format = (ProtocolPixelFormat) format,
- Width = width,
- Height = height,
- Stride = width * bpp,
- };
- }
- public ILockedFramebuffer Lock()
- {
- if (_framebuffer == null)
- throw new InvalidOperationException("Paint was not requested, wait for Paint event");
- return _framebuffer;
- }
- protected void RenderIfNeeded()
- {
- lock (_lock)
- {
- if (_lastReceivedFrame != _lastSentFrame || !_invalidated || _supportedFormats == null)
- return;
- }
- if (ClientSize.Width < 1 || ClientSize.Height < 1)
- return;
- var format = ProtocolPixelFormat.Rgba8888;
- foreach(var fmt in _supportedFormats)
- if (fmt <= ProtocolPixelFormat.MaxValue)
- {
- format = fmt;
- break;
- }
-
- var frame = RenderFrame((int) ClientSize.Width, (int) ClientSize.Height, format);
- lock (_lock)
- {
- _lastSentFrame = _nextFrameNumber++;
- frame.SequenceId = _lastSentFrame;
- _invalidated = false;
- }
- _transport.Send(frame);
- }
- public override void Invalidate(Rect rect)
- {
- _invalidated = true;
- Dispatcher.UIThread.Post(RenderIfNeeded);
- }
- public override IMouseDevice MouseDevice { get; } = new MouseDevice();
- public IKeyboardDevice KeyboardDevice { get; }
- }
- }
|