3
votes

I'm attempting to add the new OS X 10.9 (Mavericks) method beginActivityWithOptions method to the NSProcessInfo interface (TNSProcessInfo) in FireMonkey (Delphi XE2).

The function seems to work. It returns an object, however, it's not disabling App Nap for the application. I'm using the Energy tab of Activity Monitor to monitor the App Nap state.

I've added the following code to Macapi.Foundation.pas:

const
  NSActivityBackground = 255;
  NSActivityIdleSystemSleepDisabled = 1048576;
  NSActivityUserInitiated = NSActivityIdleSystemSleepDisabled or 16777215;
  NSActivityLatencyCritical = 1095216660480;

type
  NSActivityOptions = UInt64;

  NSProcessInfo = interface(NSObject)
    ['{B96935F6-3809-4A49-AD4F-CBBAB0F2C961}']
    ...
    // Added following
    function beginActivityWithOptions(options: NSActivityOptions; reason: NSString): NSObject; cdecl;
    ...
  end;

I'm calling it like this:

var
  obj: NSObject;
  reason: NSString;
  options: NSActivityOptions;
begin
  reason := NSSTR('...');
  options := NSActivityUserInitiated or NSActivityLatencyCritical;
  obj := TNSProcessInfo.Wrap(TNSProcessInfo.OCClass.processInfo).beginActivityWithOptions(options, reason);
end;

I've tried various combinations of the options flag, and it's not disabling App Nap. Any ideas? Do you see anything wrong with my implementation?

2

2 Answers

3
votes

Thanks! I've been working on this same App Nap related problem but was using the NSProcessInfo disableAutomaticTermination and enableAutomaticTermination with no success. I tried your idea of using beginActivityWithOptions and now it works fine. I believe your only mistake is that you need to create an instance of NSProcessInfo that is assigned to field in the class. The way you are doing it, when TNSProcessInfo.Wrap(TNSProcessInfo.OCClass.processInfo) goes out of scope the modification you made to the activity is lost.

In my test, I created a form with

TForm46 = class(TForm)
  procedure FormCreate(Sender: TObject);
private
  ProcessInfo: NSProcessInfo;
public
end;

then in the FormCreate

procedure TForm46.FormCreate(Sender: TObject);
begin
  ProcessInfo := TNSProcessInfo.Wrap(TNSProcessInfo.OCClass.processInfo);
  ProcessInfo.beginActivityWithOptions(NSActivityUserInitiated or NSActivityLatencyCritical, NSSTR('Good Reason'));
end;

Then I deployed it to my mac and ran it, and monitored it with Activity Monitor. It seems to work as expected with the app staying in the App Nap No state for hours at a time.

BTW the whole App Nap thing seems to get fooled by running the app through PAServer. I had to deploy and then run locally on the Mac.

0
votes

Starting on your post, id did this unit to avoid modification on "Macapi.Foundation.pas" i create "Macapi.Foundation.Ext.pas"

unit Macapi.Foundation.ext;

interface
uses  Macapi.ObjCRuntime, Macapi.ObjectiveC, Macapi.CocoaTypes,
      Macapi.CoreFoundation, Macapi.CoreServices,Macapi.Foundation;

type

  NSProcessInfoExt = interface(NSProcessInfo)
    ['{1458E7B6-B64E-430E-8DE4-A19C680C17D3}']
    // add process begin
    // Added following
    function beginActivityWithOptions(options: NSActivityOptions; reason: NSString): NSObject; cdecl;
  end;
  TNSProcessInfoExt = class(TOCGenericImport<NSProcessInfoClass, NSProcessInfoExt>)
  public
    class procedure disableAppNap(Areason : String = 'Your reason to disable appNap');
  end;


var
  ProcessInfo: NSProcessInfoExt;

implementation

{ TNSProcessInfoExt }

class procedure TNSProcessInfoExt.disableAppNap(Areason: String = 'Your reason to disable appNap');
const
  NSActivityBackground = 255;
  NSActivityIdleSystemSleepDisabled = 1048576;
  NSActivityUserInitiated = NSActivityIdleSystemSleepDisabled or 16777215;
  NSActivityLatencyCritical = 1095216660480;

type
  NSActivityOptions = UInt64;
var
  obj: NSObject;
  reason: NSString;
  options: NSActivityOptions;
begin
 reason := NSSTR(Areason);
 options := NSActivityUserInitiated or NSActivityLatencyCritical;
 obj := TNSProcessInfoExt.Wrap(TNSProcessInfo.OCClass.processInfo).beginActivityWithOptions(options, reason);



  ProcessInfo := TNSProcessInfoExt.Wrap(TNSProcessInfo.OCClass.processInfo);
  ProcessInfo.beginActivityWithOptions(NSActivityUserInitiated or NSActivityLatencyCritical,reason);


end;

end.

So when you need to disable the appNap you only need to add "Macapi.Foundation.Ext.pas" to uses of your form and call the procedure to disable app nap like:

  {$IFDEF MACOS}
    TNSProcessInfoExt.disableAppNap('Maintain web server active');
  {$ENDIF}

I did an article about this argument on : https://synaptica.info/2022/02/17/delphi-disable-appnap-on-macosx-application/