diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 42478ec12..e2c915cef 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -9,6 +9,12 @@ include_directories(${FFMPEG_INCLUDE_DIRS}) list(REMOVE_ITEM FFMPEG_LIBRARIES ${FFMPEG_AVCODEC_LIBRARIES}) +if(UNIX) + find_package(DBus QUIET) +else() + set(HAVE_DBUS "0") +endif() + find_package(ImageMagick QUIET COMPONENTS MagickCore) if(NOT ImageMagick_MagickCore_FOUND AND NOT FFMPEG_AVCODEC_FOUND) @@ -103,6 +109,15 @@ elseif(UNIX) util/pipe-posix.c util/platform-nix.c) + if(DBUS_FOUND) + set(libobs_PLATFORM_SOURCES ${libobs_PLATFORM_SOURCES} + util/platform-nix-dbus.c) + include_directories(${DBUS_INCLUDE_DIRS}) + set(libobs_PLATFORM_DEPS + ${libobs_PLATFORM_DEPS} + ${DBUS_LIBRARIES}) + endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") # use the sysinfo compatibility library on bsd find_package(Libsysinfo REQUIRED) diff --git a/libobs/obsconfig.h.in b/libobs/obsconfig.h.in index b49fd7571..7a7833cd5 100644 --- a/libobs/obsconfig.h.in +++ b/libobs/obsconfig.h.in @@ -7,3 +7,4 @@ #define OBS_PLUGIN_DESTINATION "@OBS_PLUGIN_DESTINATION@" #define OBS_RELATIVE_PREFIX "@OBS_RELATIVE_PREFIX@" #define OBS_UNIX_STRUCTURE @OBS_UNIX_STRUCTURE@ +#define HAVE_DBUS @HAVE_DBUS@ diff --git a/libobs/util/platform-cocoa.m b/libobs/util/platform-cocoa.m index 9d475b746..7d8b1054d 100644 --- a/libobs/util/platform-cocoa.m +++ b/libobs/util/platform-cocoa.m @@ -27,6 +27,8 @@ #include #include +#include + #import /* clock function selection taken from libc++ */ @@ -231,3 +233,60 @@ void os_end_high_performance(os_performance_token_t *token) } } +struct os_inhibit_info { + CFStringRef reason; + IOPMAssertionID sleep_id; + IOPMAssertionID user_id; + bool active; +}; + +os_inhibit_t *os_inhibit_sleep_create(const char *reason) +{ + struct os_inhibit_info *info = bzalloc(sizeof(*info)); + if (!reason) + info->reason = CFStringCreateWithCString(kCFAllocatorDefault, + reason, kCFStringEncodingUTF8); + else + info->reason = CFStringCreateCopy(kCFAllocatorDefault, + CFSTR("")); + + return info; +} + +bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active) +{ + IOReturn success; + + if (!info) + return false; + if (info->active == active) + return false; + + if (active) { + IOPMAssertionDeclareUserActivity(info->reason, + kIOPMUserActiveLocal, &info->user_id); + success = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, info->reason, + &info->sleep_id); + + if (success != kIOReturnSuccess) { + blog(LOG_WARNING, "Failed to disable sleep"); + return false; + } + } else { + IOPMAssertionRelease(info->sleep_id); + } + + info->active = active; + return true; +} + +void os_inhibit_sleep_destroy(os_inhibit_t *info) +{ + if (info) { + os_inhibit_sleep_set_active(info, false); + CFRelease(info->reason); + bfree(info); + } +} diff --git a/libobs/util/platform-nix-dbus.c b/libobs/util/platform-nix-dbus.c new file mode 100644 index 000000000..6bcebd50d --- /dev/null +++ b/libobs/util/platform-nix-dbus.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2015 Hugh Bailey + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "bmem.h" + +/* NOTE: This is basically just the VLC implementation from its d-bus power + * management inhibition code. Credit is theirs for this. */ + +enum service_type { + FREEDESKTOP_SS, /* freedesktop screensaver (KDE >= 4, GNOME >= 3.10) */ + FREEDESKTOP_PM, /* freedesktop power management (KDE, gnome <= 2.26) */ + MATE_SM, /* MATE (>= 1.0) session manager */ + GNOME_SM, /* GNOME 2.26 - 3.4 sessopm mamager */ +}; + +struct service_info { + const char *name; + const char *path; + const char *uninhibit; +}; + +static const struct service_info services[] = { + [FREEDESKTOP_SS] = { + .name = "org.freedesktop.ScreenSaver", + .path = "/ScreenSaver", + .uninhibit = "UnInhibit", + }, + [FREEDESKTOP_PM] = { + .name = "org.freedesktop.PowerManagement.Inhibit", + .path = "/org/freedesktop/PowerManagement", + .uninhibit = "UnInhibit", + }, + [MATE_SM] = { + .name = "org.mate.SessionManager", + .path = "/org/mate/SessionManager", + .uninhibit = "Uninhibit", + }, + [GNOME_SM] = { + .name = "org.gnome.SessionManager", + .path = "/org/gnome/SessionManager", + .uninhibit = "Uninhibit", + }, +}; + +static const size_t num_services = + (sizeof(services) / sizeof(struct service_info)); + +struct dbus_sleep_info { + const struct service_info *service; + DBusPendingCall *pending; + DBusConnection *c; + dbus_uint32_t id; + enum service_type type; +}; + +void dbus_sleep_info_destroy(struct dbus_sleep_info *info) +{ + if (info) { + if (info->pending) { + dbus_pending_call_cancel(info->pending); + dbus_pending_call_unref(info->pending); + } + + dbus_connection_close(info->c); + dbus_connection_unref(info->c); + bfree(info); + } +} + +struct dbus_sleep_info *dbus_sleep_info_create(void) +{ + struct dbus_sleep_info *info = bzalloc(sizeof(*info)); + DBusError err; + + dbus_error_init(&err); + + info->c = dbus_bus_get_private(DBUS_BUS_SESSION, &err); + if (!info->c) { + blog(LOG_ERROR, "Could not create dbus connection: %s", + err.message); + bfree(info); + return NULL; + } + + for (size_t i = 0; i < num_services; i++) { + const struct service_info *service = &services[i]; + + if (!service->name) + continue; + + if (dbus_bus_name_has_owner(info->c, service->name, NULL)) { + blog(LOG_DEBUG, "Found dbus service: %s", + service->name); + info->service = service; + info->type = (enum service_type)i; + return info; + } + } + + dbus_sleep_info_destroy(info); + return NULL; +} + +void dbus_inhibit_sleep(struct dbus_sleep_info *info, const char *reason, + bool active) +{ + DBusMessage *reply; + const char *method; + dbus_bool_t success; + + if (info->pending) { + + dbus_pending_call_block(info->pending); + reply = dbus_pending_call_steal_reply(info->pending); + dbus_pending_call_unref(info->pending); + info->pending = NULL; + + if (reply) { + success = dbus_message_get_args(reply, NULL, + DBUS_TYPE_UINT32, &info->id, + DBUS_TYPE_INVALID); + if (!success) + info->id = 0; + dbus_message_unref(reply); + } + } + + if (active == !!info->id) + return; + + method = active ? "Inhibit" : info->service->uninhibit; + + reply = dbus_message_new_method_call(info->service->name, + info->service->path, info->service->name, method); + if (reply == NULL) { + blog(LOG_ERROR, "dbus_message_new_method_call failed"); + return; + } + + if (active) { + const char *program = "libobs"; + dbus_uint32_t flags = 0xC; + dbus_uint32_t xid = 0; + + assert(info->id == 0); + + switch (info->type) { + case MATE_SM: + case GNOME_SM: + success = dbus_message_append_args(reply, + DBUS_TYPE_STRING, &program, + DBUS_TYPE_UINT32, &xid, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_INVALID); + break; + default: + success = dbus_message_append_args(reply, + DBUS_TYPE_STRING, &program, + DBUS_TYPE_STRING, &reason, + DBUS_TYPE_INVALID); + } + + if (success) { + success = dbus_connection_send_with_reply(info->c, + reply, &info->pending, -1); + if (!success) + info->pending = NULL; + } + } else { + assert(info->id != 0); + success = dbus_message_append_args(reply, + DBUS_TYPE_UINT32, &info->id, + DBUS_TYPE_INVALID); + if (success) + success = dbus_connection_send(info->c, reply, NULL); + if (!success) + info->id = 0; + } + + dbus_connection_flush(info->c); + dbus_message_unref(reply); +} diff --git a/libobs/util/platform-nix.c b/libobs/util/platform-nix.c index 91e4ae351..d2f8830a0 100644 --- a/libobs/util/platform-nix.c +++ b/libobs/util/platform-nix.c @@ -25,13 +25,19 @@ #include #include +#include "obsconfig.h" + #if !defined(__APPLE__) #include +#include +#include +#include #endif #include "darray.h" #include "dstr.h" #include "platform.h" +#include "threading.h" void *os_dlopen(const char *path) { @@ -422,3 +428,125 @@ int os_chdir(const char *path) { return chdir(path); } + +#if !defined(__APPLE__) + +#if HAVE_DBUS +struct dbus_sleep_info; + +extern struct dbus_sleep_info *dbus_sleep_info_create(void); +extern void dbus_inhibit_sleep(struct dbus_sleep_info *dbus, const char *sleep, + bool active); +extern void dbus_sleep_info_destroy(struct dbus_sleep_info *dbus); +#endif + +struct os_inhibit_info { +#if HAVE_DBUS + struct dbus_sleep_info *dbus; +#endif + pthread_t screensaver_thread; + os_event_t *stop_event; + char *reason; + posix_spawnattr_t attr; + bool active; +}; + +os_inhibit_t *os_inhibit_sleep_create(const char *reason) +{ + struct os_inhibit_info *info = bzalloc(sizeof(*info)); + sigset_t set; + +#if HAVE_DBUS + info->dbus = dbus_sleep_info_create(); +#endif + + os_event_init(&info->stop_event, OS_EVENT_TYPE_AUTO); + posix_spawnattr_init(&info->attr); + + sigemptyset(&set); + posix_spawnattr_setsigmask(&info->attr, &set); + sigaddset(&set, SIGPIPE); + posix_spawnattr_setsigdefault(&info->attr, &set); + posix_spawnattr_setflags(&info->attr, + POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK); + + info->reason = bstrdup(reason); + return info; +} + +extern char **environ; + +static void reset_screensaver(os_inhibit_t *info) +{ + char *argv[3] = {(char*)"xdg-screensaver", (char*)"reset", NULL}; + pid_t pid; + + int err = posix_spawnp(&pid, "xdg-screensaver", NULL, &info->attr, + argv, environ); + if (err == 0) { + int status; + while (waitpid(pid, &status, 0) == -1); + } else { + blog(LOG_WARNING, "Failed to create xdg-screensaver: %d", err); + } +} + +static void *screensaver_thread(void *param) +{ + os_inhibit_t *info = param; + + while (os_event_timedwait(info->stop_event, 30000) == ETIMEDOUT) + reset_screensaver(info); + + return NULL; +} + +bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active) +{ + int ret; + + if (!info) + return false; + if (info->active == active) + return false; + +#if HAVE_DBUS + if (info->dbus) + dbus_inhibit_sleep(info->dbus, info->reason, active); +#endif + + if (!info->stop_event) + return true; + + if (active) { + ret = pthread_create(&info->screensaver_thread, NULL, + &screensaver_thread, info); + if (ret < 0) { + blog(LOG_ERROR, "Failed to create screensaver " + "inhibitor thread"); + return false; + } + } else { + os_event_signal(info->stop_event); + pthread_join(info->screensaver_thread, NULL); + } + + info->active = active; + return true; +} + +void os_inhibit_sleep_destroy(os_inhibit_t *info) +{ + if (info) { + os_inhibit_sleep_set_active(info, false); +#if HAVE_DBUS + dbus_sleep_info_destroy(info->dbus); +#endif + os_event_destroy(info->stop_event); + posix_spawnattr_destroy(&info->attr); + bfree(info->reason); + bfree(info); + } +} + +#endif diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index ce1762d8e..0f4d9b82c 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -666,3 +666,49 @@ void get_win_ver(struct win_version_info *info) *info = ver; } + +struct os_inhibit_info { + BOOL was_active; + bool active; +}; + +os_inhibit_t *os_inhibit_sleep_create(const char *reason) +{ + UNUSED_PARAMETER(reason); + return bzalloc(sizeof(struct os_inhibit_info)); +} + +bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active) +{ + if (!info) + return false; + if (info->active == active) + return false; + + if (active) { + SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, + &info->was_active, 0); + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, false, + NULL, 0); + SetThreadExecutionState( + ES_CONTINUOUS | + ES_SYSTEM_REQUIRED | + ES_AWAYMODE_REQUIRED | + ES_DISPLAY_REQUIRED); + } else { + SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, info->was_active, + NULL, 0); + SetThreadExecutionState(ES_CONTINUOUS); + } + + info->active = active; + return true; +} + +void os_inhibit_sleep_destroy(os_inhibit_t *info) +{ + if (info) { + os_inhibit_sleep_set_active(info, false); + bfree(info); + } +} diff --git a/libobs/util/platform.h b/libobs/util/platform.h index 8d0013cda..af3d03620 100644 --- a/libobs/util/platform.h +++ b/libobs/util/platform.h @@ -145,6 +145,13 @@ EXPORT int os_mkdirs(const char *path); EXPORT int os_rename(const char *old_path, const char *new_path); EXPORT int os_copyfile(const char *file_in, const char *file_out); +struct os_inhibit_info; +typedef struct os_inhibit_info os_inhibit_t; + +EXPORT os_inhibit_t *os_inhibit_sleep_create(const char *reason); +EXPORT bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active); +EXPORT void os_inhibit_sleep_destroy(os_inhibit_t *info); + #ifdef _MSC_VER #define strtoll _strtoi64 #if _MSC_VER < 1900