From cb2171616e059147f8008b5cbf9132a674cefff8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 24 Sep 2018 17:53:31 +0100 Subject: [PATCH 1/6] just put interfaces in header file. --- src/Avalonia.Native.OSX/SystemDialogs.mm | 24 ++-- src/Avalonia.Native.OSX/window.h | 156 +--------------------- src/Avalonia.Native.OSX/window.mm | 162 +++++++++++++++++++++++ 3 files changed, 176 insertions(+), 166 deletions(-) diff --git a/src/Avalonia.Native.OSX/SystemDialogs.mm b/src/Avalonia.Native.OSX/SystemDialogs.mm index 3f508ca8f7..4b5a995e56 100644 --- a/src/Avalonia.Native.OSX/SystemDialogs.mm +++ b/src/Avalonia.Native.OSX/SystemDialogs.mm @@ -52,8 +52,8 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); - [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + auto windowHolder = dynamic_cast(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; } return; @@ -66,9 +66,9 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); + auto windowBase = dynamic_cast(parentWindowHandle); - [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler]; } else { @@ -144,8 +144,8 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); - [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + auto windowHolder = dynamic_cast(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; } return; @@ -158,9 +158,9 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); + auto windowHolder = dynamic_cast(parentWindowHandle); - [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + [panel beginSheetModalForWindow:windowHolder->GetNSWindow() completionHandler:handler]; } else { @@ -225,8 +225,8 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); - [windowBase->Window makeKeyAndOrderFront:windowBase->Window]; + auto windowHolder = dynamic_cast(parentWindowHandle); + [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()]; } return; @@ -238,9 +238,9 @@ class SystemDialogs : public ComSingleObject(parentWindowHandle); + auto windowBase = dynamic_cast(parentWindowHandle); - [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler]; + [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler]; } else { diff --git a/src/Avalonia.Native.OSX/window.h b/src/Avalonia.Native.OSX/window.h index ed8406bd5f..11aea0c528 100644 --- a/src/Avalonia.Native.OSX/window.h +++ b/src/Avalonia.Native.OSX/window.h @@ -22,161 +22,9 @@ class WindowBaseImpl; -(void) setCanBecomeKeyAndMain; @end -class WindowBaseImpl : public ComSingleObject +struct INSWindowHolder { -public: - AvnView* View; - AvnWindow* Window; - ComPtr BaseEvents; - AvnPoint lastPositionSet; - WindowBaseImpl(IAvnWindowBaseEvents* events) - { - BaseEvents = events; - View = [[AvnView alloc] initWithParent:this]; - Window = [[AvnWindow alloc] initWithParent:this]; - - lastPositionSet.X = 100; - lastPositionSet.Y = 100; - - [Window setStyleMask:NSWindowStyleMaskBorderless]; - [Window setBackingType:NSBackingStoreBuffered]; - [Window setContentView: View]; - } - - virtual HRESULT Show() - { - SetPosition(lastPositionSet); - UpdateStyle(); - [Window makeKeyAndOrderFront:Window]; - return S_OK; - } - - virtual HRESULT Hide () - { - if(Window != nullptr) - { - [Window orderOut:Window]; - } - return S_OK; - } - - virtual HRESULT Close() - { - [Window close]; - return S_OK; - } - - virtual HRESULT GetClientSize(AvnSize* ret) - { - if(ret == nullptr) - return E_POINTER; - auto frame = [View frame]; - ret->Width = frame.size.width; - ret->Height = frame.size.height; - return S_OK; - } - - virtual HRESULT GetScaling (double* ret) - { - if(ret == nullptr) - return E_POINTER; - - if(Window == nullptr) - { - *ret = 1; - return S_OK; - } - - *ret = [Window backingScaleFactor]; - return S_OK; - } - - virtual HRESULT Resize(double x, double y) - { - [Window setContentSize:NSSize{x, y}]; - return S_OK; - } - - virtual void Invalidate (AvnRect rect) - { - [View setNeedsDisplayInRect:[View frame]]; - } - - virtual void BeginMoveDrag () - { - auto lastEvent = [View lastMouseDownEvent]; - - if(lastEvent == nullptr) - { - return; - } - - [Window performWindowDragWithEvent:lastEvent]; - } - - - virtual HRESULT GetPosition (AvnPoint* ret) - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto frame = [Window frame]; - - ret->X = frame.origin.x; - ret->Y = frame.origin.y + frame.size.height; - - *ret = ConvertPointY(*ret); - - return S_OK; - } - - virtual void SetPosition (AvnPoint point) - { - lastPositionSet = point; - [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; - } - - virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) - { - if(ret == nullptr) - { - return E_POINTER; - } - - point = ConvertPointY(point); - auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; - - *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; - - return S_OK; - } - - virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) - { - if(ret == nullptr) - { - return E_POINTER; - } - - auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; - *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); - - return S_OK; - } - -protected: - virtual NSWindowStyleMask GetStyle() - { - return NSWindowStyleMaskBorderless; - } - - void UpdateStyle() - { - [Window setStyleMask:GetStyle()]; - } + virtual AvnWindow* GetNSWindow () = 0; }; #endif /* window_h */ diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 4995483c71..c97569a548 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -1,6 +1,168 @@ #include "common.h" #include "window.h" +class WindowBaseImpl : public ComSingleObject, public INSWindowHolder +{ +public: + AvnView* View; + AvnWindow* Window; + ComPtr BaseEvents; + AvnPoint lastPositionSet; + WindowBaseImpl(IAvnWindowBaseEvents* events) + { + BaseEvents = events; + View = [[AvnView alloc] initWithParent:this]; + Window = [[AvnWindow alloc] initWithParent:this]; + + lastPositionSet.X = 100; + lastPositionSet.Y = 100; + + [Window setStyleMask:NSWindowStyleMaskBorderless]; + [Window setBackingType:NSBackingStoreBuffered]; + [Window setContentView: View]; + } + + virtual AvnWindow* GetNSWindow() + { + return Window; + } + + virtual HRESULT Show() + { + SetPosition(lastPositionSet); + UpdateStyle(); + [Window makeKeyAndOrderFront:Window]; + return S_OK; + } + + virtual HRESULT Hide () + { + if(Window != nullptr) + { + [Window orderOut:Window]; + } + return S_OK; + } + + virtual HRESULT Close() + { + [Window close]; + return S_OK; + } + + virtual HRESULT GetClientSize(AvnSize* ret) + { + if(ret == nullptr) + return E_POINTER; + auto frame = [View frame]; + ret->Width = frame.size.width; + ret->Height = frame.size.height; + return S_OK; + } + + virtual HRESULT GetScaling (double* ret) + { + if(ret == nullptr) + return E_POINTER; + + if(Window == nullptr) + { + *ret = 1; + return S_OK; + } + + *ret = [Window backingScaleFactor]; + return S_OK; + } + + virtual HRESULT Resize(double x, double y) + { + [Window setContentSize:NSSize{x, y}]; + return S_OK; + } + + virtual void Invalidate (AvnRect rect) + { + [View setNeedsDisplayInRect:[View frame]]; + } + + virtual void BeginMoveDrag () + { + auto lastEvent = [View lastMouseDownEvent]; + + if(lastEvent == nullptr) + { + return; + } + + [Window performWindowDragWithEvent:lastEvent]; + } + + + virtual HRESULT GetPosition (AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } + + auto frame = [Window frame]; + + ret->X = frame.origin.x; + ret->Y = frame.origin.y + frame.size.height; + + *ret = ConvertPointY(*ret); + + return S_OK; + } + + virtual void SetPosition (AvnPoint point) + { + lastPositionSet = point; + [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))]; + } + + virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } + + point = ConvertPointY(point); + auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; + + *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; + + return S_OK; + } + + virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret) + { + if(ret == nullptr) + { + return E_POINTER; + } + + auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); + auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; + *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); + + return S_OK; + } + +protected: + virtual NSWindowStyleMask GetStyle() + { + return NSWindowStyleMaskBorderless; + } + + void UpdateStyle() + { + [Window setStyleMask:GetStyle()]; + } +}; + @implementation AvnView { ComPtr _parent; From 07774ea4be9096a5ba8db2571fff6876c2084895 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 24 Sep 2018 18:05:02 +0100 Subject: [PATCH 2/6] implement max client size and top most. --- src/Avalonia.Native.OSX/window.mm | 20 ++++++++++++++++++++ src/Avalonia.Native/Helpers.cs | 5 +++++ src/Avalonia.Native/WindowImplBase.cs | 15 +++++++++------ src/headers/avalonia-native.h | 3 +++ 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index c97569a548..ec25dcf428 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -44,6 +44,13 @@ public: return S_OK; } + virtual HRESULT SetTopMost (bool value) + { + [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel]; + + return S_OK; + } + virtual HRESULT Close() { [Window close]; @@ -60,6 +67,19 @@ public: return S_OK; } + virtual HRESULT GetMaxClientSize(AvnSize* ret) + { + if(ret == nullptr) + return E_POINTER; + + auto size = [NSScreen.screens objectAtIndex:0].frame.size; + + ret->Height = size.height; + ret->Width = size.width; + + return S_OK; + } + virtual HRESULT GetScaling (double* ret) { if(ret == nullptr) diff --git a/src/Avalonia.Native/Helpers.cs b/src/Avalonia.Native/Helpers.cs index 941dd1dd14..77673edd1b 100644 --- a/src/Avalonia.Native/Helpers.cs +++ b/src/Avalonia.Native/Helpers.cs @@ -14,5 +14,10 @@ namespace Avalonia.Native { return new AvnPoint { X = pt.X, Y = pt.Y }; } + + public static Size ToAvaloniaSize (this AvnSize size) + { + return new Size(size.Width, size.Height); + } } } diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index e6d4af62e1..e8893c0250 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -190,9 +190,17 @@ namespace Avalonia.Native _native.BeginMoveDrag(); } - #region Stubs + public Size MaxClientSize => _native.GetMaxClientSize().ToAvaloniaSize(); + + public void SetTopmost(bool value) + { + _native.SetTopMost(value); + } + public double Scaling => _native.GetScaling(); + #region Stubs + public Action PositionChanged { get; set; } public Action Deactivated { get; set; } public Action Activated { get; set; } @@ -201,16 +209,11 @@ namespace Avalonia.Native Action ScalingChanged { get; set; } public IPlatformHandle Handle => new PlatformHandle(IntPtr.Zero, "NOT SUPPORTED"); - public Size MaxClientSize => new Size(1600, 900); public IScreenImpl Screen => new ScreenImpl(); Action ITopLevelImpl.ScalingChanged { get; set; } - public void SetTopmost(bool value) - { - } - public void SetMinMaxSize(Size minSize, Size maxSize) { } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 4775859395..d69fb1201c 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -73,6 +73,7 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual HRESULT Hide () = 0; virtual HRESULT Close() = 0; virtual HRESULT GetClientSize(AvnSize*ret) = 0; + virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0; virtual HRESULT GetScaling(double*ret)=0; virtual HRESULT Resize(double width, double height) = 0; virtual void Invalidate (AvnRect rect) = 0; @@ -81,6 +82,8 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual void SetPosition (AvnPoint point) = 0; virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0; virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0; + virtual HRESULT SetTopMost (bool value) = 0; + }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase From 79a82e1ea5bf2232eb04f9a9326310ec5cec3e02 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 24 Sep 2018 18:16:05 +0100 Subject: [PATCH 3/6] implement scaling changed callback. --- src/Avalonia.Native.OSX/window.mm | 5 +++++ src/Avalonia.Native/WindowImplBase.cs | 13 ++++++++++--- src/headers/avalonia-native.h | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index ec25dcf428..dd2df1be27 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -418,6 +418,11 @@ protected: return (AvnInputModifiers)rv; } + +- (void)viewDidChangeBackingProperties +{ + _parent->BaseEvents->ScalingChanged(); +} @end diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index e8893c0250..5a37f33cdf 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -99,10 +99,15 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Resized(AvnSize size) => _parent.Resized?.Invoke(new Size(size.Width, size.Height)); - public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) + void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) { _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); } + + void IAvnWindowBaseEvents.ScalingChanged() + { + _parent.ScalingChanged?.Invoke(_parent.Scaling); + } } @@ -199,11 +204,13 @@ namespace Avalonia.Native public double Scaling => _native.GetScaling(); + public Action Deactivated { get; set; } + public Action Activated { get; set; } + #region Stubs public Action PositionChanged { get; set; } - public Action Deactivated { get; set; } - public Action Activated { get; set; } + public Action Input { get; set; } Action ScalingChanged { get; set; } diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index d69fb1201c..1cc4b92de8 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -109,6 +109,8 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) = 0; + + virtual void ScalingChanged () = 0; }; From 006c0af89ad806b563a41df9c04b8076fb63a3c7 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 24 Sep 2018 22:31:09 +0300 Subject: [PATCH 4/6] DeferredRenderer support --- src/Avalonia.Native.OSX/window.h | 1 + src/Avalonia.Native.OSX/window.mm | 96 +++++++++++++++++--- src/Avalonia.Native/DeferredFramebuffer.cs | 77 ++++++++++++++++ src/Avalonia.Native/DeferredRendererProxy.cs | 78 ++++++++++++++++ src/Avalonia.Native/WindowImplBase.cs | 73 ++++++++++++--- src/headers/avalonia-native.h | 22 ++++- 6 files changed, 319 insertions(+), 28 deletions(-) create mode 100644 src/Avalonia.Native/DeferredFramebuffer.cs create mode 100644 src/Avalonia.Native/DeferredRendererProxy.cs diff --git a/src/Avalonia.Native.OSX/window.h b/src/Avalonia.Native.OSX/window.h index 11aea0c528..58dca5bc9a 100644 --- a/src/Avalonia.Native.OSX/window.h +++ b/src/Avalonia.Native.OSX/window.h @@ -15,6 +15,7 @@ class WindowBaseImpl; -(AvnView*) initWithParent: (WindowBaseImpl*) parent; -(NSEvent*) lastMouseDownEvent; -(AvnPoint) translateLocalPoint:(AvnPoint)pt; +-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose; @end @interface AvnWindow : NSWindow diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index c97569a548..2b04fd2e42 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -130,7 +130,7 @@ public: } point = ConvertPointY(point); - auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)]; + auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)]; *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)]; @@ -145,12 +145,18 @@ public: } auto cocoaViewPoint = ToNSPoint([View translateLocalPoint:point]); - auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint]; + auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint]; *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint)); return S_OK; } + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) + { + [View setSwRenderedFrame: fb dispose: dispose]; + return S_OK; + } + protected: virtual NSWindowStyleMask GetStyle() { @@ -163,9 +169,14 @@ protected: } }; +NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil]; + @implementation AvnView { ComPtr _parent; + ComPtr _swRenderedFrame; + AvnFramebuffer _swRenderedFrameBuffer; + bool _queuedDisplayFromThread; NSTrackingArea* _area; bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver; NSEvent* _lastMouseDownEvent; @@ -209,18 +220,10 @@ protected: _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height}); } -- (void)drawRect:(NSRect)dirtyRect +- (void) drawFb: (AvnFramebuffer*) fb { - auto logicalSize = [self frame].size; - auto pixelSize = [self convertSizeToBacking:logicalSize]; - int w = pixelSize.width; - int h = pixelSize.height; - int stride = w * 4; - void*ptr = malloc(h * stride); - _parent->BaseEvents->SoftwareDraw(ptr, stride, w, h, AvnSize{logicalSize.width, logicalSize.height}); - auto colorSpace = CGColorSpaceCreateDeviceRGB(); - auto bctx = CGBitmapContextCreate(ptr, w, h, 8, stride, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast); + auto bctx = CGBitmapContextCreate(fb->Data, fb->Width, fb->Height, 8, fb->Stride, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast); auto image = CGBitmapContextCreateImage(bctx); CGContextRelease(bctx); CGColorSpaceRelease(colorSpace); @@ -230,13 +233,74 @@ protected: [ctx saveGraphicsState]; auto cgc = [ctx CGContext]; - CGContextDrawImage(cgc, CGRect{0,0, logicalSize.width, logicalSize.height}, image); + CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image); CGImageRelease(image); [ctx restoreGraphicsState]; + +} + +- (void)drawRect:(NSRect)dirtyRect +{ + _parent->BaseEvents->RunRenderPriorityJobs(); + @synchronized (self) { + if(_swRenderedFrame != NULL) + { + [self drawFb: &_swRenderedFrameBuffer]; + return; + } + } + + auto logicalSize = [self frame].size; + auto pixelSize = [self convertSizeToBacking:logicalSize]; + int w = pixelSize.width; + int h = pixelSize.height; + int stride = w * 4; + void*ptr = malloc(h * stride); + AvnFramebuffer fb = { + .Data = ptr, + .Stride = stride, + .Width = w, + .Height = h, + .PixelFormat = kAvnRgba8888, + .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96} + }; + _parent->BaseEvents->SoftwareDraw(&fb); + [self drawFb: &fb]; free(ptr); } +-(void) redrawSelf +{ + @autoreleasepool + { + @synchronized(self) + { + if(!_queuedDisplayFromThread) + return; + _queuedDisplayFromThread = false; + } + [self setNeedsDisplayInRect:[self frame]]; + [self display]; + + } +} + +-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose +{ + @autoreleasepool { + @synchronized (self) { + _swRenderedFrame = dispose; + _swRenderedFrameBuffer = *fb; + if(!_queuedDisplayFromThread) + { + _queuedDisplayFromThread = true; + [self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes]; + } + } + } +} + - (AvnPoint) translateLocalPoint:(AvnPoint)pt { pt.Y = [self bounds].size.height - pt.Y; @@ -253,6 +317,12 @@ protected: return result; } +- (void) viewDidChangeBackingProperties +{ + _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]); + [super viewDidChangeBackingProperties]; +} + - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type { auto localPoint = [self convertPoint:[event locationInWindow] toView:self]; diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs new file mode 100644 index 0000000000..395d9bb0d3 --- /dev/null +++ b/src/Avalonia.Native/DeferredFramebuffer.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.InteropServices; +using Avalonia.Native.Interop; +using Avalonia.Platform; +using SharpGen.Runtime; + +namespace Avalonia.Native +{ + public class DeferredFramebuffer : ILockedFramebuffer + { + private readonly Func, bool> _lockWindow; + public DeferredFramebuffer(Func, bool> lockWindow, + int width, int height, Vector dpi) + { + _lockWindow = lockWindow; + Address = Marshal.AllocHGlobal(width * height * 4); + Width = width; + Height = height; + RowBytes = width * 4; + Dpi = dpi; + Format = PixelFormat.Rgba8888; + } + + public IntPtr Address { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int RowBytes { get; set; } + public Vector Dpi { get; set; } + public PixelFormat Format { get; set; } + + + class Disposer : CallbackBase + { + private IntPtr _ptr; + + public Disposer(IntPtr ptr) + { + _ptr = ptr; + } + + protected override void Destroyed() + { + if(_ptr != IntPtr.Zero) + { + Marshal.FreeHGlobal(_ptr); + _ptr = IntPtr.Zero; + } + } + } + + public void Dispose() + { + if (Address == IntPtr.Zero) + return; + if (!_lockWindow(win => + { + var fb = new AvnFramebuffer + { + Data = Address, + Dpi = new AvnVector + { + X = Dpi.X, + Y = Dpi.Y + }, + Width = Width, + Height = Height, + PixelFormat = (AvnPixelFormat)Format, + Stride = RowBytes + }; + using (var d = new Disposer(Address)) + win.ThreadSafeSetSwRenderedFrame(ref fb, d); + })) + Marshal.FreeHGlobal(Address); + Address = IntPtr.Zero; + } + } +} diff --git a/src/Avalonia.Native/DeferredRendererProxy.cs b/src/Avalonia.Native/DeferredRendererProxy.cs new file mode 100644 index 0000000000..099275eaa6 --- /dev/null +++ b/src/Avalonia.Native/DeferredRendererProxy.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.VisualTree; + +namespace Avalonia.Native +{ + public class DeferredRendererProxy : IRenderer, IRenderLoopTask, IRenderLoop + { + object _lock = new object(); + void IRenderLoop.Add(IRenderLoopTask i) + { + AvaloniaLocator.Current.GetService().Add(this); + } + + void IRenderLoop.Remove(IRenderLoopTask i) + { + AvaloniaLocator.Current.GetService().Remove(this); + } + + + DeferredRenderer _renderer; + IRenderLoopTask _rendererTask; + public DeferredRendererProxy(IRenderRoot root) + { + _renderer = new DeferredRenderer(root, this); + _rendererTask = (IRenderLoopTask)_renderer; + } + + public bool DrawFps{ + get => _renderer.DrawFps; + set => _renderer.DrawFps = value; + } + public bool DrawDirtyRects + { + get => _renderer.DrawDirtyRects; + set => _renderer.DrawDirtyRects = value; + } + + public bool NeedsUpdate => _rendererTask.NeedsUpdate; + + public void AddDirty(IVisual visual) => _renderer.AddDirty(visual); + + public void Dispose() => _renderer.Dispose(); + + public IEnumerable HitTest(Point p, IVisual root, Func filter) + { + return _renderer.HitTest(p, root, filter); + } + + public void Paint(Rect rect) + { + if (NeedsUpdate) + Update(TimeSpan.FromMilliseconds(Environment.TickCount)); + Render(); + } + + public void Resized(Size size) => _renderer.Resized(size); + + public void Start() => _renderer.Start(); + + public void Stop() => _renderer.Stop(); + + public void Update(TimeSpan time) + { + _rendererTask.Update(time); + } + + public void Render() + { + lock(_lock) + { + _rendererTask.Render(); + } + } + } +} diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index e6d4af62e1..f40d8ad4e7 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -15,9 +15,11 @@ namespace Avalonia.Native { IInputRoot _inputRoot; IAvnWindowBase _native; - - private bool _deferredRendering = false; + private object _syncRoot = new object(); + private bool _deferredRendering = true; private readonly IMouseDevice _mouse; + private Size _savedLogicalSize; + private double _savedScaling; public WindowBaseImpl() { @@ -27,6 +29,8 @@ namespace Avalonia.Native protected void Init(IAvnWindowBase window) { _native = window; + _savedLogicalSize = ClientSize; + _savedScaling = Scaling; } public Size ClientSize @@ -42,7 +46,28 @@ namespace Avalonia.Native public IEnumerable Surfaces => new[] { this }; public ILockedFramebuffer Lock() { - return _framebuffer; + if(_deferredRendering) + { + var w = _savedLogicalSize.Width / _savedScaling; + var h = _savedLogicalSize.Height / _savedScaling; + var dpi = _savedScaling * 96; + return new DeferredFramebuffer(cb => + { + lock (_syncRoot) + { + if (_native == null) + return false; + cb(_native); + return true; + } + }, (int)w, (int)h, new Vector(dpi, dpi)); + } + + var fb = _framebuffer; + _framebuffer = null; + if (fb == null) + throw new InvalidOperationException("Lock call without corresponding Paint event"); + return fb; } public Action Paint { get; set; } @@ -80,29 +105,48 @@ namespace Avalonia.Native void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke(); - void IAvnWindowBaseEvents.SoftwareDraw(IntPtr ptr, int stride, int pixelWidth, int pixelHeight, AvnSize logicalSize) + void IAvnWindowBaseEvents.SoftwareDraw(ref AvnFramebuffer fb) { Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); _parent._framebuffer = new SavedFramebuffer { - Address = ptr, - RowBytes = stride, - Width = pixelWidth, - Height = pixelHeight, - Dpi = new Vector(pixelWidth / logicalSize.Width * 96, pixelHeight / logicalSize.Height * 96) + Address = fb.Data, + RowBytes = fb.Stride, + Width = fb.Width, + Height = fb.Height, + Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y) }; - _parent.Paint?.Invoke(new Rect(0, 0, logicalSize.Width, logicalSize.Height)); + _parent.Paint?.Invoke(new Rect(0, 0, fb.Width / (fb.Dpi.X / 96), fb.Height / (fb.Dpi.Y / 96))); } - void IAvnWindowBaseEvents.Resized(AvnSize size) => _parent.Resized?.Invoke(new Size(size.Width, size.Height)); + void IAvnWindowBaseEvents.Resized(AvnSize size) + { + var s = new Size(size.Width, size.Height); + _parent._savedLogicalSize = s; + _parent.Resized?.Invoke(s); + } public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) { _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta); } + + void IAvnWindowBaseEvents.ScalingChanged(double scaling) + { + _parent._savedScaling = scaling; + _parent.ScalingChanged?.Invoke(scaling); + } + + void IAvnWindowBaseEvents.RunRenderPriorityJobs() + { + if (_parent._deferredRendering) + // Hack to trigger Paint event on the renderer + _parent.Paint?.Invoke(new Rect()); + Dispatcher.UIThread.RunJobs(DispatcherPriority.Render); + } } @@ -134,8 +178,8 @@ namespace Avalonia.Native public IRenderer CreateRenderer(IRenderRoot root) { - //_deferredRendering = true; - //return new DeferredRenderer(root, AvaloniaLocator.Current.GetService()); + if(_deferredRendering) + return new DeferredRendererProxy(root); return new ImmediateRenderer(root); } @@ -149,7 +193,8 @@ namespace Avalonia.Native public void Invalidate(Rect rect) { - _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y }); + if (!_deferredRendering) + _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y }); } public void SetInputRoot(IInputRoot inputRoot) diff --git a/src/headers/avalonia-native.h b/src/headers/avalonia-native.h index 4775859395..d5d13a369d 100644 --- a/src/headers/avalonia-native.h +++ b/src/headers/avalonia-native.h @@ -30,6 +30,23 @@ struct AvnPoint double X, Y; }; +enum AvnPixelFormat +{ + kAvnRgb565, + kAvnRgba8888, + kAvnBgra8888 +}; + +struct AvnFramebuffer +{ + void* Data; + int Width; + int Height; + int Stride; + AvnVector Dpi; + AvnPixelFormat PixelFormat; +}; + enum AvnRawMouseEventType { LeaveWindow, @@ -81,6 +98,7 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown virtual void SetPosition (AvnPoint point) = 0; virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0; virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0; + virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0; }; AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase @@ -96,7 +114,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown { - virtual HRESULT SoftwareDraw(void* ptr, int stride, int pixelWidth, int pixelHeight, const AvnSize& logicalSize) = 0; + virtual HRESULT SoftwareDraw(AvnFramebuffer* fb) = 0; virtual void Closed() = 0; virtual void Activated() = 0; virtual void Deactivated() = 0; @@ -106,6 +124,8 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown AvnInputModifiers modifiers, AvnPoint point, AvnVector delta) = 0; + virtual void ScalingChanged(double scaling) = 0; + virtual void RunRenderPriorityJobs() = 0; }; From f5edeac7bb2bc780870480c049f8596e517f925f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 24 Sep 2018 21:08:17 +0100 Subject: [PATCH 5/6] Fixed scaling computation --- src/Avalonia.Native/WindowImplBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs index f40d8ad4e7..c6b0397274 100644 --- a/src/Avalonia.Native/WindowImplBase.cs +++ b/src/Avalonia.Native/WindowImplBase.cs @@ -48,8 +48,8 @@ namespace Avalonia.Native { if(_deferredRendering) { - var w = _savedLogicalSize.Width / _savedScaling; - var h = _savedLogicalSize.Height / _savedScaling; + var w = _savedLogicalSize.Width * _savedScaling; + var h = _savedLogicalSize.Height * _savedScaling; var dpi = _savedScaling * 96; return new DeferredFramebuffer(cb => { From b206f063bf4fd96ba2de57c27c8aeda9f784bdea Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 24 Sep 2018 21:44:29 +0100 Subject: [PATCH 6/6] Fixed window.mm compilation --- src/Avalonia.Native.OSX/window.mm | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Avalonia.Native.OSX/window.mm b/src/Avalonia.Native.OSX/window.mm index 608f828e0e..6104ac1f28 100644 --- a/src/Avalonia.Native.OSX/window.mm +++ b/src/Avalonia.Native.OSX/window.mm @@ -488,11 +488,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent return (AvnInputModifiers)rv; } - -- (void)viewDidChangeBackingProperties -{ - _parent->BaseEvents->ScalingChanged(); -} @end