From 2273534a57821b5fe39b374e7610e518e624a0be Mon Sep 17 00:00:00 2001 From: Stan Wijckmans Date: Mon, 5 Sep 2022 22:15:39 +0200 Subject: [PATCH] Add API to prevent App Nap. --- .../project.pbxproj | 4 ++ .../src/OSX/PlatformBehaviorInhibition.mm | 39 +++++++++++++++++++ native/Avalonia.Native/src/OSX/common.h | 1 + native/Avalonia.Native/src/OSX/main.mm | 11 ++++++ .../Platform/IPlatformBehaviorInhibition.cs | 12 ++++++ .../PlatformInhibitionType.cs | 13 +++++++ src/Avalonia.Controls/TopLevel.cs | 25 ++++++++++++ src/Avalonia.Native/AvaloniaNativePlatform.cs | 21 ---------- .../PlatformBehaviorInhibition.cs | 20 ++++++++++ src/Avalonia.Native/avn.idl | 7 ++++ 10 files changed, 132 insertions(+), 21 deletions(-) create mode 100644 native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm create mode 100644 src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs create mode 100644 src/Avalonia.Controls/PlatformInhibitionType.cs create mode 100644 src/Avalonia.Native/PlatformBehaviorInhibition.cs diff --git a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj index 41d1534f8d..c49290314d 100644 --- a/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj +++ b/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 523484CA26EA688F00EA0C2C /* trayicon.mm in Sources */ = {isa = PBXBuildFile; fileRef = 523484C926EA688F00EA0C2C /* trayicon.mm */; }; 5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; }; 5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; }; + 855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */ = {isa = PBXBuildFile; fileRef = 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */; }; AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; }; AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; }; AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; }; @@ -95,6 +96,7 @@ 5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = ""; }; 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = ""; }; 5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = ""; }; + 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PlatformBehaviorInhibition.mm; sourceTree = ""; }; AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; }; AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -140,6 +142,7 @@ AB7A61E62147C814003C5833 = { isa = PBXGroup; children = ( + 855EDC9E28C6546F00807998 /* PlatformBehaviorInhibition.mm */, BC11A5BC2608D58F0017BAD0 /* automation.h */, BC11A5BD2608D58F0017BAD0 /* automation.mm */, 1A1852DB23E05814008F0DED /* deadlock.mm */, @@ -288,6 +291,7 @@ 1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */, BC11A5BF2608D58F0017BAD0 /* automation.mm in Sources */, 37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */, + 855EDC9F28C6546F00807998 /* PlatformBehaviorInhibition.mm in Sources */, 520624B322973F4100C4DCEF /* menu.mm in Sources */, 37A517B32159597E00FBA241 /* Screens.mm in Sources */, 1AFD334123E03C4F0042899B /* controlhost.mm in Sources */, diff --git a/native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm b/native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm new file mode 100644 index 0000000000..db054d82ef --- /dev/null +++ b/native/Avalonia.Native/src/OSX/PlatformBehaviorInhibition.mm @@ -0,0 +1,39 @@ +#include "common.h" + +namespace +{ + id s_inhibitAppSleepHandle{}; +} + +class PlatformBehaviorInhibition : public ComSingleObject +{ +public: + FORWARD_IUNKNOWN() + + virtual void SetInhibitAppSleep(bool inhibitAppSleep, char* reason) override + { + START_COM_CALL; + + @autoreleasepool + { + if (inhibitAppSleep && s_inhibitAppSleepHandle == nullptr) + { + NSActivityOptions options = NSActivityUserInitiatedAllowingIdleSystemSleep; + s_inhibitAppSleepHandle = [[NSProcessInfo processInfo] beginActivityWithOptions:options reason:[NSString stringWithUTF8String: reason]]; + } + + if (!inhibitAppSleep) + { + s_inhibitAppSleepHandle = nullptr; + } + } + } +}; + +extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition() +{ + @autoreleasepool + { + return new PlatformBehaviorInhibition(); + } +} diff --git a/native/Avalonia.Native/src/OSX/common.h b/native/Avalonia.Native/src/OSX/common.h index 972927b99d..4353737dc8 100644 --- a/native/Avalonia.Native/src/OSX/common.h +++ b/native/Avalonia.Native/src/OSX/common.h @@ -26,6 +26,7 @@ extern IAvnTrayIcon* CreateTrayIcon(); extern IAvnMenuItem* CreateAppMenuItem(); extern IAvnMenuItem* CreateAppMenuItemSeparator(); extern IAvnApplicationCommands* CreateApplicationCommands(); +extern IAvnPlatformBehaviorInhibition* CreatePlatformBehaviorInhibition(); extern IAvnNativeControlHost* CreateNativeControlHost(NSView* parent); extern IAvnPlatformSettings* CreatePlatformSettings(); extern void SetAppMenu(IAvnMenu *menu); diff --git a/native/Avalonia.Native/src/OSX/main.mm b/native/Avalonia.Native/src/OSX/main.mm index 99063e600e..4bfda4b531 100644 --- a/native/Avalonia.Native/src/OSX/main.mm +++ b/native/Avalonia.Native/src/OSX/main.mm @@ -408,6 +408,17 @@ public: return S_OK; } } + + virtual HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv) override + { + START_COM_CALL; + + @autoreleasepool + { + *ppv = ::CreatePlatformBehaviorInhibition(); + return S_OK; + } + } }; extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative() diff --git a/src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs b/src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs new file mode 100644 index 0000000000..227e65c08d --- /dev/null +++ b/src/Avalonia.Base/Platform/IPlatformBehaviorInhibition.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Avalonia.Platform +{ + /// + /// Allows to inhibit platform specific behavior. + /// + public interface IPlatformBehaviorInhibition + { + Task SetInhibitAppSleep(bool inhibitAppSleep, string reason); + } +} diff --git a/src/Avalonia.Controls/PlatformInhibitionType.cs b/src/Avalonia.Controls/PlatformInhibitionType.cs new file mode 100644 index 0000000000..03e3270e0b --- /dev/null +++ b/src/Avalonia.Controls/PlatformInhibitionType.cs @@ -0,0 +1,13 @@ +namespace Avalonia.Controls +{ + /// + /// A platform specific behavior that can be inhibited. + /// + public enum PlatformInhibitionType + { + /// + /// When inhibited, prevents the app from being put to sleep or being given a lower priority when not in focus. + /// + AppSleep + } +} diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index f956fb8724..ed11dec1d0 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -20,6 +20,7 @@ using Avalonia.Styling; using Avalonia.Utilities; using Avalonia.Input.Platform; using System.Linq; +using System.Threading.Tasks; namespace Avalonia.Controls { @@ -570,6 +571,30 @@ namespace Avalonia.Controls /// The event args. protected virtual void OnClosed(EventArgs e) => Closed?.Invoke(this, e); + /// + /// Requests a to be inhibited. + /// The behavior remains inhibited until the return value is disposed. + /// The available set of s depends on the platform. + /// If a behavior is inhibited on a platform where this type is not supported the request will have no effect. + /// + protected async Task RequestPlatformInhibition(PlatformInhibitionType type, string reason) + { + var platformBehaviorInhibition = PlatformImpl?.TryGetFeature(); + if (platformBehaviorInhibition == null) + { + return Disposable.Create(() => { }); + } + + switch (type) + { + case PlatformInhibitionType.AppSleep: + await platformBehaviorInhibition.SetInhibitAppSleep(true, reason); + return Disposable.Create(() => platformBehaviorInhibition.SetInhibitAppSleep(false, reason).Wait()); + default: + return Disposable.Create(() => { }); + } + } + /// /// Tries to get a service from an , logging a /// warning if not found. diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs index 09feb0c768..67b55bd512 100644 --- a/src/Avalonia.Native/AvaloniaNativePlatform.cs +++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs @@ -158,25 +158,4 @@ namespace Avalonia.Native throw new NotImplementedException(); } } - - public class AvaloniaNativeMacOptions - { - private readonly IAvnMacOptions _opts; - private bool _showInDock; - internal AvaloniaNativeMacOptions(IAvnMacOptions opts) - { - _opts = opts; - ShowInDock = true; - } - - public bool ShowInDock - { - get => _showInDock; - set - { - _showInDock = value; - _opts.SetShowInDock(value ? 1 : 0); - } - } - } } diff --git a/src/Avalonia.Native/PlatformBehaviorInhibition.cs b/src/Avalonia.Native/PlatformBehaviorInhibition.cs new file mode 100644 index 0000000000..dfbfe8a5e5 --- /dev/null +++ b/src/Avalonia.Native/PlatformBehaviorInhibition.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Avalonia.Native.Interop; +using Avalonia.Platform; + +namespace Avalonia.Native +{ + internal class PlatformBehaviorInhibition : IPlatformBehaviorInhibition + { + readonly IAvnPlatformBehaviorInhibition _native; + + internal PlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition native) + => _native = native; + + public Task SetInhibitAppSleep(bool inhibitAppSleep, string reason) + { + _native.SetInhibitAppSleep(inhibitAppSleep ? 1 : 0, reason); + return Task.CompletedTask; + } + } +} diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl index a062fdc61d..db78873672 100644 --- a/src/Avalonia.Native/avn.idl +++ b/src/Avalonia.Native/avn.idl @@ -503,6 +503,7 @@ interface IAvaloniaNativeFactory : IUnknown HRESULT CreateTrayIcon(IAvnTrayIcon** ppv); HRESULT CreateApplicationCommands(IAvnApplicationCommands** ppv); HRESULT CreatePlatformSettings(IAvnPlatformSettings** ppv); + HRESULT CreatePlatformBehaviorInhibition(IAvnPlatformBehaviorInhibition** ppv); } [uuid(233e094f-9b9f-44a3-9a6e-6948bbdd9fb1)] @@ -924,3 +925,9 @@ interface IAvnPlatformSettings : IUnknown uint GetAccentColor(); void RegisterColorsChange(IAvnActionCallback* callback); } + +[uuid(12edf00d-5803-4d3f-9947-b4840e5e9372)] +interface IAvnPlatformBehaviorInhibition : IUnknown +{ + void SetInhibitAppSleep(bool inhibitAppSleep, char* reason); +}