Skip to content

Commit

Permalink
Support osx idle on screensaver, screenlock
Browse files Browse the repository at this point in the history
Add fah-screen-agent
Support idle/not idle notifications
Add tools.scons
  • Loading branch information
kbernhagen committed Aug 12, 2024
1 parent e1233c4 commit 71e208e
Show file tree
Hide file tree
Showing 11 changed files with 589 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*.app

# Build byproducts
/bin
/build
/package.txt
/package-description.txt
Expand Down
17 changes: 13 additions & 4 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,15 @@ if env['PLATFORM'] == 'win32' or int(env.get('cross_mingw', 0)):
duplicate = 0)
Default(hide_console)

tools = SConscript('src/tools.scons', variant_dir = 'build', duplicate = 0)
Default(tools)
Depends(tools, client)
# Clean
Clean(client, ['build', 'config.log'])
Clean(client, ['bin', 'build', 'config.log'])

# Dist
docs = ['README.md', 'CHANGELOG.md', 'LICENSE']
distfiles = docs + [client, 'images/fahlogo.png']
distfiles = docs + client + tools + ['images/fahlogo.png']
if env['PLATFORM'] == 'posix':
distfiles.append('install/lin/fah-client.service')
if hide_console is not None: distfiles.append(hide_console)
Expand Down Expand Up @@ -116,26 +119,32 @@ if 'package' in COMMAND_LINE_TARGETS:
home = '.', # abs path or relative to PWD
pkg_scripts = 'build/install/osx/scripts', # relative to home
root = './build/pkg/root', # abs path or relative to PWD
sign_tools = ['usr/local/bin/fah-client'], # relative to root
sign_tools = ['usr/local/bin/*'], # relative to root
must_close_apps = [
'org.foldingathome.fahviewer',
'org.foldingathome.fahcontrol',
'edu.stanford.folding.fahviewer',
'edu.stanford.folding.fahcontrol',
],
pkg_files = [
[str(client[0]), 'usr/local/bin/', 0o755],
['build/install/osx/fahclient.url',
'Applications/Folding@home/Folding@home.url', 0o644],
['build/install/osx/uninstall.url',
'Applications/Folding@home/uninstall.url', 0o644],
['build/install/osx/fah-screen-agent.plist',
'Library/LaunchAgents/' +
'org.foldingathome.fah-screen-agent.plist', 0o644],
['build/install/osx/launchd.plist',
'Library/LaunchDaemons/' +
'org.foldingathome.fahclient.plist', 0o644]
],
pre_sign_callback = seticon,
)]

pkg_files = pkg_components[0]['pkg_files']
for tool in client + tools:
pkg_files += [[str(tool), 'usr/local/bin/', 0o755]]

# min pkg target macos 10.13
pkg_target = env.get('osx_min_ver', '10.13')
ver = tuple([int(x) for x in pkg_target.split('.')])
Expand Down
25 changes: 25 additions & 0 deletions install/osx/fah-screen-agent.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<dict>
<key>SuccessfulExit</key>
<false/>
</dict>
<key>Label</key>
<string>org.foldingathome.fah-screen-agent</string>
<key>LimitLoadToSessionType</key>
<string>Aqua</string>
<key>LowPriorityIO</key>
<true/>
<key>Program</key>
<string>/usr/local/bin/fah-screen-agent</string>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>Umask</key>
<integer>18</integer>
</dict>
</plist>
14 changes: 13 additions & 1 deletion install/osx/scripts/postinstall
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ chmod -R u+rwX,go-w "$RUN_DIR"
chown -R nobody:nobody "$RUN_DIR"

PLIST=/Library/LaunchDaemons/org.foldingathome.fahclient.plist
AGENT_PLIST="/Library/LaunchAgents/org.foldingathome.fah-screen-agent.plist"

"$SCRIPTS"/organize-credits.sh &

# Start service
chmod 0644 "$PLIST"
chmod 0644 "$PLIST" "$AGENT_PLIST"
launchctl load -w "$PLIST"

# start, in case RunAtLoad is false
launchctl start org.foldingathome.fahclient || true

# restart any running agents in other sessions
# note that this does not reload their launchd jobs
killall -HUP fah-screen-agent || true

# start agent in user's gui session
conuser=$(/usr/bin/stat -f "%Su" /dev/console) || conuser="root"
conuid=$(/usr/bin/id -u "$conuser") || conuid=0
if [[ $conuid != 0 ]]; then
launchctl bootstrap gui/$conuid "$AGENT_PLIST" || true
fi
10 changes: 10 additions & 0 deletions install/osx/scripts/preinstall
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SCRIPTS="$(dirname "$0")"
OLD_LAUNCHD="/Library/LaunchDaemons/FAHClient.plist"
OLD_LAUNCHD2="/Library/LaunchDaemons/edu.stanford.folding.fahclient.plist"
NEW_LAUNCHD="/Library/LaunchDaemons/org.foldingathome.fahclient.plist"
AGENT_PLIST="/Library/LaunchAgents/org.foldingathome.fah-screen-agent.plist"

if [ -f "$NEW_LAUNCHD" ]; then
launchctl unload -w "$NEW_LAUNCHD" || true
Expand All @@ -23,6 +24,15 @@ if [ -f "$OLD_LAUNCHD" ]; then
rm -f "$OLD_LAUNCHD" || true
fi

if [ -f "$AGENT_PLIST" ]; then
# stop agent running in console user's session and unload job
conuser=$(/usr/bin/stat -f "%Su" /dev/console) || conuser="root"
conuid=$(/usr/bin/id -u "$conuser") || conuid=0
if [[ $conuid != 0 ]]; then
launchctl bootout gui/$conuid "$AGENT_PLIST" || true
fi
fi

# Assuming upgrade, remove old stuff
F1="/Applications/FAHClient.url"
F2="/Applications/Folding@home/Web Control.url"
Expand Down
73 changes: 70 additions & 3 deletions src/fah/client/osx/OSXOSImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
#include <pwd.h>
#include <unistd.h>

#include <fah/screen-agent/defines.h>

using namespace FAH::Client;
using namespace cb;
using namespace std;
Expand All @@ -60,6 +62,9 @@ enum {


namespace {
CFStringRef kScreenIdle = CFSTR(SCREEN_IDLE_NOTIFICATION);
CFStringRef kScreenNotIdle = CFSTR(SCREEN_NOT_IDLE_NOTIFICATION);

#pragma mark c callbacks
void consoleUserCB(SCDynamicStoreRef s, CFArrayRef keys, void *info) {
OSXOSImpl::instance().consoleUserChanged(s, keys, info);
Expand All @@ -85,6 +90,20 @@ namespace {
<< CFStringGetCStringPtr(name, kCFStringEncodingUTF8));
OSXOSImpl::instance().requestExit();
}


void screenIdleCB(CFNotificationCenterRef center, void *observer,
CFNotificationName name, const void *object,
CFDictionaryRef info) {
OSXOSImpl::instance().noteScreenIdle();
}


void screenNotIdleCB(CFNotificationCenterRef center, void *observer,
CFNotificationName name, const void *object,
CFDictionaryRef info) {
OSXOSImpl::instance().noteScreenNotIdle();
}
}


Expand Down Expand Up @@ -187,9 +206,10 @@ void OSXOSImpl::finishInit() {


void OSXOSImpl::updateSystemIdle() {
bool shouldBeIdle = displayPower == kDisplayPowerOff || loginwindowIsActive ||
screensaverIsActive || screenIsLocked;

bool shouldBeIdle;
if (gotScreenNotIdleRecently()) shouldBeIdle = false;
else shouldBeIdle = displayPower == kDisplayPowerOff || loginwindowIsActive ||
gotScreenIdleRecently();
if (shouldBeIdle == systemIsIdle) return;
systemIsIdle = shouldBeIdle;

Expand Down Expand Up @@ -415,10 +435,57 @@ bool OSXOSImpl::registerForConsoleUserNotifications() {
}


void OSXOSImpl::noteScreenIdle() {
screenIdleExpiry =
dispatch_time(DISPATCH_TIME_NOW, SCREEN_NOTIFICATION_EXPIRES * NSEC_PER_SEC);
bool wasIdle = screenIdle;
screenIdle = true;
dispatch_after(screenIdleExpiry + NSEC_PER_SEC, dispatch_get_main_queue(), ^{
updateSystemIdle();
});
if (!wasIdle) delayedUpdateSystemIdle(5);
}


void OSXOSImpl::noteScreenNotIdle() {
screenNotIdleExpiry =
dispatch_time(DISPATCH_TIME_NOW, SCREEN_NOTIFICATION_EXPIRES * NSEC_PER_SEC);
screenNotIdle = true;
dispatch_after(screenNotIdleExpiry + NSEC_PER_SEC,dispatch_get_main_queue(),^{
updateSystemIdle();
});
updateSystemIdle();
}


bool OSXOSImpl::gotScreenIdleRecently() {
if (!screenIdle) return false;
if (dispatch_time(DISPATCH_TIME_NOW, 0) < screenIdleExpiry) return true;
screenIdle = false;
return false;
}


bool OSXOSImpl::gotScreenNotIdleRecently() {
if (!screenNotIdle) return false;
if (dispatch_time(DISPATCH_TIME_NOW, 0) < screenNotIdleExpiry) return true;
screenNotIdle = false;
return false;
}


bool OSXOSImpl::registerForDarwinNotifications() {
CFNotificationCenterRef nc = CFNotificationCenterGetDarwinNotifyCenter();
if (!nc) return false;

CFNotificationCenterAddObserver(
nc, (void *)this, &screenIdleCB, kScreenIdle, 0,
CFNotificationSuspensionBehaviorCoalesce);

CFNotificationCenterAddObserver(
nc, (void *)this, &screenNotIdleCB, kScreenNotIdle, 0,
CFNotificationSuspensionBehaviorCoalesce);

string user = "nobody";
struct passwd *pwent = getpwuid(getuid());
if (pwent && pwent->pw_name) user = pwent->pw_name;
Expand Down
12 changes: 10 additions & 2 deletions src/fah/client/osx/OSXOSImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ namespace FAH {
static OSXOSImpl *singleton;

std::atomic<bool> systemIsIdle = false;
bool screensaverIsActive = false;
bool screenIsLocked = false;
bool loginwindowIsActive = false;

io_service_t displayWrangler = 0;
Expand All @@ -62,6 +60,12 @@ namespace FAH {

int displayPower = 0;

// these are notification received bools; both are needed
bool screenIdle = false;
bool screenNotIdle = false;
dispatch_time_t screenIdleExpiry = 0;
dispatch_time_t screenNotIdleExpiry = 0;

public:
OSXOSImpl(App &app);
~OSXOSImpl();
Expand All @@ -82,12 +86,16 @@ namespace FAH {
void displayPowerChanged
(void *context, io_service_t service, natural_t mtype, void *marg);
void finishInit();
void noteScreenIdle();
void noteScreenNotIdle();

protected:
void initialize();
void addHeartbeatTimerToRunLoop(CFRunLoopRef loop);
void deregisterForConsoleUserNotifications();
bool registerForConsoleUserNotifications();
bool gotScreenIdleRecently();
bool gotScreenNotIdleRecently();
bool registerForDarwinNotifications();
void deregisterForDisplayPowerNotifications();
bool registerForDisplayPowerNotifications();
Expand Down
39 changes: 39 additions & 0 deletions src/fah/screen-agent/defines.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/******************************************************************************\
This file is part of the Folding@home Client.
The fah-client runs Folding@home protein folding simulations.
Copyright (c) 2001-2023, foldingathome.org
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
For information regarding this software email:
Joseph Coffland
joseph@cauldrondevelopment.com
\******************************************************************************/

#pragma once

// Defines shared with fah-client

#define SCREEN_IDLE_NOTIFICATION "org.foldingathome.screen.idle"
#define SCREEN_NOT_IDLE_NOTIFICATION "org.foldingathome.screen.notidle"

#define SCREEN_NOTIFICATION_INTERVAL 60
#define SCREEN_NOTIFICATION_LEEWAY 2
#define SCREEN_NOTIFICATION_EXPIRES (\
SCREEN_NOTIFICATION_INTERVAL + 2 * SCREEN_NOTIFICATION_LEEWAY + 1)
Loading

0 comments on commit 71e208e

Please sign in to comment.