diff --git a/.gitignore b/.gitignore index 7512d31..49b1211 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ *.app # Build byproducts +/bin /build /package.txt /package-description.txt diff --git a/SConstruct b/SConstruct index 15f4381..13ec6c0 100644 --- a/SConstruct +++ b/SConstruct @@ -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) @@ -116,7 +119,7 @@ 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', @@ -124,11 +127,13 @@ if 'package' in COMMAND_LINE_TARGETS: '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] @@ -136,6 +141,10 @@ if 'package' in COMMAND_LINE_TARGETS: 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('.')]) diff --git a/install/osx/fah-screen-agent.plist b/install/osx/fah-screen-agent.plist new file mode 100644 index 0000000..3ed8ac2 --- /dev/null +++ b/install/osx/fah-screen-agent.plist @@ -0,0 +1,25 @@ + + + + + KeepAlive + + SuccessfulExit + + + Label + org.foldingathome.fah-screen-agent + LimitLoadToSessionType + Aqua + LowPriorityIO + + Program + /usr/local/bin/fah-screen-agent + RunAtLoad + + StandardOutPath + /dev/null + Umask + 18 + + diff --git a/install/osx/scripts/postinstall b/install/osx/scripts/postinstall index 357e565..6d44711 100755 --- a/install/osx/scripts/postinstall +++ b/install/osx/scripts/postinstall @@ -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 diff --git a/install/osx/scripts/preinstall b/install/osx/scripts/preinstall index 61aa4f3..babca8c 100755 --- a/install/osx/scripts/preinstall +++ b/install/osx/scripts/preinstall @@ -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 @@ -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" diff --git a/src/fah/client/osx/OSXOSImpl.cpp b/src/fah/client/osx/OSXOSImpl.cpp index 9631f0b..57a1bc6 100644 --- a/src/fah/client/osx/OSXOSImpl.cpp +++ b/src/fah/client/osx/OSXOSImpl.cpp @@ -46,6 +46,8 @@ #include #include +#include + using namespace FAH::Client; using namespace cb; using namespace std; @@ -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); @@ -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(); + } } @@ -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; @@ -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; diff --git a/src/fah/client/osx/OSXOSImpl.h b/src/fah/client/osx/OSXOSImpl.h index 95ea013..f6da907 100644 --- a/src/fah/client/osx/OSXOSImpl.h +++ b/src/fah/client/osx/OSXOSImpl.h @@ -47,8 +47,6 @@ namespace FAH { static OSXOSImpl *singleton; std::atomic systemIsIdle = false; - bool screensaverIsActive = false; - bool screenIsLocked = false; bool loginwindowIsActive = false; io_service_t displayWrangler = 0; @@ -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(); @@ -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(); diff --git a/src/fah/screen-agent/defines.h b/src/fah/screen-agent/defines.h new file mode 100644 index 0000000..0ec2de0 --- /dev/null +++ b/src/fah/screen-agent/defines.h @@ -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) diff --git a/src/fah/screen-agent/screen-agent.c b/src/fah/screen-agent/screen-agent.c new file mode 100644 index 0000000..485d5d3 --- /dev/null +++ b/src/fah/screen-agent/screen-agent.c @@ -0,0 +1,362 @@ +/******************************************************************************\ + + 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 + +\******************************************************************************/ + +// Observe screensaver start/stop, screen lock/unlock notifications. +// Periodically post darwin notifications for screen idle/notidle. +// These notifications are observed by fah-client, and expire +// Agent will exit non-zero on SIGHUP, possibly to be restarted by launchd. + +#ifndef __APPLE__ +#error "This can only be compiled for macOS" +#endif + +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +#define AGENT_LABEL "org.foldingathome.fah-screen-agent" + +#define STRINGIFY(x) XSTRINGIFY(x) +#define XSTRINGIFY(x) #x +#ifndef VERSION +#define VERSION 0.0.0 +#endif +static const char *version = STRINGIFY(VERSION); + +struct _DTimer { + dispatch_source_t ds; + dispatch_time_t start; + uint64_t interval; + uint64_t leeway; +}; +typedef struct _DTimer DTimer; + +static DTimer timer; + +static dispatch_source_t sig_ds[NSIG] = {0}; + +static int quit_sig = 0; +static bool restart = false; +static CFAbsoluteTime start_time = 0; +static os_log_t logger = OS_LOG_DEFAULT; + +static bool screenIsLocked = false; +static bool screensaverIsActive = false; +static bool screenIdle = false; + +static CFNotificationCenterRef nc; +static const char *observer = AGENT_LABEL; + +const char *kScreenIdle = SCREEN_IDLE_NOTIFICATION; +const char *kScreenNotIdle = SCREEN_NOT_IDLE_NOTIFICATION; + + +#if DEBUG +static void print_time() { + time_t now = time(0); + struct tm sTm; + char buff[24]; + gmtime_r(&now, &sTm); + strftime(buff, sizeof(buff), "%F %TZ", &sTm); + printf("%s ", buff); +} + +#define LOGDEBUG(format, ...) do { \ + if (isatty(STDOUT_FILENO)) { \ + print_time(); \ + printf(format, ## __VA_ARGS__); \ + printf("\n"); \ + } else os_log_debug(logger, format, ## __VA_ARGS__); \ + } while(0) + +#else +#define LOGDEBUG(format, ...) + +#endif + + +static void notify() { + LOGDEBUG("%s %s", __FUNCTION__, screenIdle ? kScreenIdle : kScreenNotIdle); + notify_post(screenIdle ? kScreenIdle : kScreenNotIdle); +} + + +static void create_timer() { + // periodically repeat notifications, which expire in client + dispatch_queue_t q = dispatch_get_main_queue(); + timer.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, q); + timer.interval = SCREEN_NOTIFICATION_INTERVAL * NSEC_PER_SEC; + timer.leeway = SCREEN_NOTIFICATION_LEEWAY * NSEC_PER_SEC; + timer.start = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 4); + assert(timer.ds); + dispatch_source_set_timer(timer.ds,timer.start,timer.interval,timer.leeway); + dispatch_source_set_event_handler(timer.ds, ^{notify();}); + dispatch_resume(timer.ds); +} + + +static void reset_timer() { + timer.start = dispatch_time(DISPATCH_TIME_NOW, timer.interval); + dispatch_source_set_timer(timer.ds,timer.start,timer.interval,timer.leeway); +} + + +static void update() { + LOGDEBUG("%s", __FUNCTION__); + bool shouldBeIdle = screenIsLocked || screensaverIsActive; + if (screenIdle == shouldBeIdle) return; + screenIdle = shouldBeIdle; + reset_timer(); + notify(); +} + + +void screenDidLock(CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo) { + LOGDEBUG("%s", __FUNCTION__); + screenIsLocked = true; + update(); +} + + +void screenDidUnlock(CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo) { + LOGDEBUG("%s", __FUNCTION__); + screenIsLocked = false; + update(); +} + + +void screensaverDidStart(CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo) { + LOGDEBUG("%s", __FUNCTION__); + screensaverIsActive = true; + update(); +} + + +void screensaverDidStop(CFNotificationCenterRef center, void *observer, + CFStringRef name, const void *object, CFDictionaryRef userInfo) { + LOGDEBUG("%s", __FUNCTION__); + screensaverIsActive = false; + update(); +} + + +struct note_item { + CFStringRef in; + CFNotificationCallback cb; +}; + + +static const struct note_item notes[] = { + {CFSTR("com.apple.screensaver.didstart"), screensaverDidStart}, + {CFSTR("com.apple.screensaver.didstop"), screensaverDidStop}, + {CFSTR("com.apple.screenIsLocked"), screenDidLock}, + {CFSTR("com.apple.screenIsUnlocked"), screenDidUnlock}, + {NULL, NULL} +}; + + +static void register_for_launch_events() { + // register for xpc launchd.plist LaunchEvents + // this MUST be done if launchd might have LaunchEvents for us + // we never un-register + dispatch_queue_t q = dispatch_get_main_queue(); + xpc_set_event_stream_handler("com.apple.iokit.matching", q, + ^(xpc_object_t event) { + // DO NOTHING but consume event +#if DEBUG + const char *ename = xpc_dictionary_get_string(event, XPC_EVENT_KEY_NAME); + LOGDEBUG("LaunchEvents IO: %s", ename); +#endif + }); + + xpc_set_event_stream_handler("com.apple.notifyd.matching", q, + ^(xpc_object_t event) { + // DO NOTHING but consume event +#if DEBUG + const char *nname = xpc_dictionary_get_string(event, "Notification"); + LOGDEBUG("LaunchEvents notification: %s", nname); +#endif + }); +} + + +static void got_signal(int sig) { + // dispatch callback, NOT a signal handler + LOGDEBUG("%s %s", __FUNCTION__, sys_signame[sig]); + switch (sig) { + case SIGTERM: + quit_sig = sig; + // fallthrough + case SIGINT: + CFRunLoopStop(CFRunLoopGetMain()); + break; + case SIGHUP: + restart = true; + CFRunLoopStop(CFRunLoopGetMain()); + break; + default: break; + } +} + + +static void dummy_signal_handler(int sig) {} + + +static void install_signal_handlers() { + sigset_t sigs; + sigemptyset(&sigs); + + sigaddset(&sigs, SIGTERM); + sigaddset(&sigs, SIGINT); + sigaddset(&sigs, SIGHUP); + + struct sigaction action = {0}; + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); + + dispatch_queue_t q = dispatch_get_main_queue(); + // dummy handler instead of SIG_IGN so syscalls can still be interrupted + action.sa_handler = dummy_signal_handler; + for(int s = 1; s < NSIG; s++) { + if (sigismember(&sigs, s)) { + sigaction(s, &action, NULL); + sig_ds[s] = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, s, 0, q); + assert(sig_ds[s]); + dispatch_source_set_event_handler(sig_ds[s], ^{got_signal(s);}); + dispatch_resume(sig_ds[s]); + } + } +} + + +static void release_signal_handlers() { + for(int s = 1; s < NSIG; s++) + if (sig_ds[s]) dispatch_release(sig_ds[s]); +} + + +static void usage(int argc, const char * argv[]) { + printf("usage: %s [{start|stop}]\n", argv[0]); + printf(" start convenience for /bin/launchctl start %s\n", AGENT_LABEL); + printf(" stop convenience for /bin/launchctl stop %s\n", AGENT_LABEL); + printf(" -h,--help\n"); + printf(" --version\n"); +} + + +static void parse_args(int argc, const char * argv[]) { + if (getppid() != 1 && 1 < argc) { + const char *prog = "/bin/launchctl"; + if (strcmp("start", argv[1]) == 0 || strcmp("stop", argv[1]) == 0) { + printf("%s %s %s\n", prog, argv[1], AGENT_LABEL); + execl(prog, prog, argv[1], AGENT_LABEL); + } + if (strcmp("--help", argv[1]) == 0 || strcmp("-h", argv[1]) == 0) { + usage(argc, argv); + } else if (strcmp("--version", argv[1]) == 0) { + printf("%s\n", version); + } else { + printf("error: unknown argument: %s\n", argv[1]); + usage(argc, argv); + exit(EX_USAGE); + } + exit(0); + } +} + + +static void init(int argc, const char * argv[]) { + start_time = CFAbsoluteTimeGetCurrent(); +#if DEBUG + logger = os_log_create(AGENT_LABEL, "main"); +#endif + nc = CFNotificationCenterGetDistributedCenter(); + + if (geteuid() == 0) { + fprintf(stderr, "error: %s cannot be run as root\n", argv[0]); + exit(1); + } + // FIXME: refuse to run if not GUI session + + parse_args(argc, argv); + + install_signal_handlers(); + register_for_launch_events(); + + // TODO: maybe get initial values from CGSessionCopyCurrentDictionary + // https://stackoverflow.com/a/11511419 + // this should always be correct for a fresh gui login + screenIsLocked = false; + screensaverIsActive = false; + screenIdle = false; + + // set up notifications + for (int i = 0; notes[i].in; i++) + CFNotificationCenterAddObserver(nc, observer, notes[i].cb, + notes[i].in, NULL, CFNotificationSuspensionBehaviorCoalesce); + + create_timer(); +} + + +int main(int argc, const char * argv[]) { + init(argc, argv); + CFRunLoopRun(); + + // any critical cleanup would go here + + // exit immediately if think launchd signaled us + if (quit_sig) exit(0); + + // exit non-zero if want to restart (assume appropriate KeepAlive) + if (restart) exit(EX_TEMPFAIL); + +#if DEBUG + // non-critical cleanup + CFNotificationCenterRemoveEveryObserver(nc, observer); + dispatch_source_cancel(timer.ds); + dispatch_release(timer.ds); + release_signal_handlers(); +#endif + + // run 10+ sec or launchd may think we crashed and relaunch us + if (getppid() == 1) { + CFTimeInterval run_time = CFAbsoluteTimeGetCurrent() - start_time; + if (run_time < 10.0) sleep(10 - (int)run_time); + } + + return 0; +} diff --git a/src/osx-screen-agent.scons b/src/osx-screen-agent.scons new file mode 100644 index 0000000..cccb795 --- /dev/null +++ b/src/osx-screen-agent.scons @@ -0,0 +1,27 @@ +Import('*') + +env = env.Clone() +env.Replace(LIBS = []) +env.Replace(LIBPATH = []) +env.Replace(CPPPATH = []) +env.Replace(FRAMEWORKS = ['CoreFoundation']) + +try: + env['LINKFLAGS'].remove('-stdlib=libc++') +except: pass + +if not (env.GetOption('clean') or env.get('debug')): + try: + env.AppendUnique(CCFLAGS = ['-Os']) + env['CCFLAGS'].remove('-O3') + env['CCFLAGS'].remove('-funroll-loops') + except: pass + +version = env.get('PACKAGE_VERSION') +if version: env.CBDefine('VERSION=' + version) + +src = Glob('fah/screen-agent/*.c') + +tool = env.Program('#/bin/fah-screen-agent', src) + +Return('tool') diff --git a/src/tools.scons b/src/tools.scons new file mode 100644 index 0000000..91f94f8 --- /dev/null +++ b/src/tools.scons @@ -0,0 +1,19 @@ +Import('*') + +tools_scons = Glob('all-*.scons') + +if env['PLATFORM'] == 'win32' or int(env.get('cross_mingw', 0)): + tools_scons += Glob('win-*.scons') +elif env['PLATFORM'] == 'darwin': + tools_scons += Glob('osx-*.scons') +else: + tools_scons += Glob('lin-*.scons') + +Export('env') + +tools = [] +for tscons in tools_scons: + tool = SConscript(tscons, variant_dir = '#/build', duplicate = 0) + if tool: tools += tool + +Return('tools')