diff --git a/include/dbus-cinnamon-screensaver.h b/include/dbus-cinnamon-screensaver.h new file mode 100644 index 0000000..b70b9c9 --- /dev/null +++ b/include/dbus-cinnamon-screensaver.h @@ -0,0 +1,8 @@ +#ifndef DBUS_CINNAMON_SCREENSAVER_H +#define DBUS_CINNAMON_SCREENSAVER_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_cinnamon_screensaver_create(void); + +#endif diff --git a/include/dbus-common.h b/include/dbus-common.h new file mode 100644 index 0000000..8269f50 --- /dev/null +++ b/include/dbus-common.h @@ -0,0 +1,40 @@ +#ifndef DBUS_COMMON_H +#define DBUS_COMMON_H + +#include "inhibit-interface.h" +#include +#include + +typedef enum { + DBUS_MODE_NONE, + DBUS_MODE_IMPLEMENT, // we own the D-Bus name + DBUS_MODE_MONITOR // we monitor via BecomeMonitor +} DBusMode; + +typedef struct { + sd_bus *bus; + DBusMode mode; + char *bus_name; + char *object_path; + char *interface_name; + sd_bus_slot *slot; + InhibitInterface *parent; +} DBusService; + +int dbus_service_init(DBusService *svc, const char *bus_name, + const char *object_path, const char *interface_name); +void dbus_service_cleanup(DBusService *svc); + +int dbus_service_detect_mode(DBusService *svc); +int dbus_service_claim_name(DBusService *svc); +int dbus_service_become_monitor(DBusService *svc, const char **match_rules, + int num_rules); + +int dbus_service_poll(DBusService *svc); + +int dbus_parse_inhibit_request(sd_bus_message *m, char **app_name, + char **reason); +int dbus_send_signal(DBusService *svc, const char *signal_name, + const char *signature, ...); + +#endif diff --git a/include/dbus-freedesktop-powermanager.h b/include/dbus-freedesktop-powermanager.h new file mode 100644 index 0000000..5119e49 --- /dev/null +++ b/include/dbus-freedesktop-powermanager.h @@ -0,0 +1,8 @@ +#ifndef DBUS_POWERMANAGER_H +#define DBUS_POWERMANAGER_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_freedesktop_powermanager_create(void); + +#endif diff --git a/include/dbus-freedesktop-screensaver.h b/include/dbus-freedesktop-screensaver.h new file mode 100644 index 0000000..8b3a9a8 --- /dev/null +++ b/include/dbus-freedesktop-screensaver.h @@ -0,0 +1,9 @@ +#ifndef DBUS_SCREENSAVER_H +#define DBUS_SCREENSAVER_H + +#include "dbus-common.h" +#include "inhibit-interface.h" + +InhibitInterface *dbus_freedesktop_screensaver_create(void); + +#endif diff --git a/include/dbus-gnome-screensaver.h b/include/dbus-gnome-screensaver.h new file mode 100644 index 0000000..0afdc62 --- /dev/null +++ b/include/dbus-gnome-screensaver.h @@ -0,0 +1,8 @@ +#ifndef DBUS_GNOME_SCREENSAVER_H +#define DBUS_GNOME_SCREENSAVER_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_gnome_screensaver_create(void); + +#endif diff --git a/include/dbus-gnome-session.h b/include/dbus-gnome-session.h new file mode 100644 index 0000000..a73455f --- /dev/null +++ b/include/dbus-gnome-session.h @@ -0,0 +1,8 @@ +#ifndef DBUS_GNOME_SESSION_H +#define DBUS_GNOME_SESSION_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_gnome_session_create(void); + +#endif diff --git a/include/dbus-mate-screensaver.h b/include/dbus-mate-screensaver.h new file mode 100644 index 0000000..1fd95ac --- /dev/null +++ b/include/dbus-mate-screensaver.h @@ -0,0 +1,8 @@ +#ifndef DBUS_MATE_SCREENSAVER_H +#define DBUS_MATE_SCREENSAVER_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_mate_screensaver_create(void); + +#endif diff --git a/include/dbus-systemd-login1.h b/include/dbus-systemd-login1.h new file mode 100644 index 0000000..28df75c --- /dev/null +++ b/include/dbus-systemd-login1.h @@ -0,0 +1,8 @@ +#ifndef DBUS_SYSTEMD_LOGIN1_H +#define DBUS_SYSTEMD_LOGIN1_H + +#include "inhibit-interface.h" + +InhibitInterface *dbus_systemd_login1_create(void); + +#endif diff --git a/include/inhibit-interface.h b/include/inhibit-interface.h new file mode 100644 index 0000000..2ae49ef --- /dev/null +++ b/include/inhibit-interface.h @@ -0,0 +1,103 @@ +#ifndef INHIBIT_INTERFACE_H +#define INHIBIT_INTERFACE_H + +#include +#include +#include + +#define MAX_INTERFACES 16 +#define MAX_INHIBITS_PER_INTERFACE 64 +#define INHIBIT_ID_LEN 64 + +typedef enum { + INHIBIT_TYPE_SCREENSAVER = 1 << 0, + INHIBIT_TYPE_SUSPEND = 1 << 1, + INHIBIT_TYPE_IDLE = 1 << 2, + INHIBIT_TYPE_ALL = + INHIBIT_TYPE_SCREENSAVER | INHIBIT_TYPE_SUSPEND | INHIBIT_TYPE_IDLE +} InhibitType; + +typedef enum { + INHIBIT_FLAG_NONE = 0, + INHIBIT_FLAG_FORWARDED = 1 << 0, + INHIBIT_FLAG_LOOP_DETECTED = 1 << 1 +} InhibitFlags; + +typedef struct { + char id[INHIBIT_ID_LEN]; + InhibitType type; + char app_name[256]; + char reason[1024]; + InhibitFlags flags; + uint64_t cookie; + void *interface_data; +} InhibitEntry; + +typedef struct { + InhibitEntry entry; + int interface_idx; + char forwarded_to[MAX_INTERFACES][INHIBIT_ID_LEN]; + int num_forwarded; +} TrackedInhibit; + +typedef struct InhibitInterface InhibitInterface; + +typedef void (*InhibitEventCallback)(InhibitInterface *source, + const InhibitEntry *entry, int is_inhibit); + +struct InhibitInterface { + const char *name; + int enabled; + + int (*init)(InhibitInterface *iface); + void (*cleanup)(InhibitInterface *iface); + + int (*inhibit)(InhibitInterface *iface, InhibitType type, + const char *app_name, const char *reason, + InhibitEntry *out_entry); + + int (*uninhibit)(InhibitInterface *iface, const InhibitEntry *entry); + + int (*poll)(InhibitInterface *iface); + + void (*set_callback)(InhibitInterface *iface, InhibitEventCallback cb); + + void *private_data; +}; + +typedef struct { + InhibitInterface *interfaces[MAX_INTERFACES]; + int num_interfaces; + TrackedInhibit inhibits[MAX_INTERFACES * MAX_INHIBITS_PER_INTERFACE]; + int num_inhibits; + InhibitEventCallback event_callback; +} InhibitManager; + +int inhibit_manager_init(InhibitManager *mgr); +void inhibit_manager_cleanup(InhibitManager *mgr); +int inhibit_manager_register(InhibitManager *mgr, InhibitInterface *iface); +void inhibit_manager_set_callback(InhibitManager *mgr, InhibitEventCallback cb); + +int inhibit_manager_forward_inhibit(InhibitManager *mgr, + InhibitInterface *source, + const InhibitEntry *entry); + +int inhibit_manager_forward_uninhibit(InhibitManager *mgr, + InhibitInterface *source, + const InhibitEntry *entry); + +TrackedInhibit *inhibit_manager_find(InhibitManager *mgr, + InhibitInterface *source, const char *id); + +int inhibit_manager_add_tracked(InhibitManager *mgr, InhibitInterface *source, + const InhibitEntry *entry); + +void inhibit_manager_remove_tracked(InhibitManager *mgr, + InhibitInterface *source, const char *id); + +void inhibit_manager_poll_all(InhibitManager *mgr); + +const char *inhibit_type_to_string(InhibitType type); +InhibitType inhibit_type_from_string(const char *str); + +#endif diff --git a/include/kernel-wakelock.h b/include/kernel-wakelock.h new file mode 100644 index 0000000..f845f22 --- /dev/null +++ b/include/kernel-wakelock.h @@ -0,0 +1,8 @@ +#ifndef KERNEL_WAKELOCK_H +#define KERNEL_WAKELOCK_H + +#include "inhibit-interface.h" + +InhibitInterface *kernel_wakelock_create(void); + +#endif diff --git a/include/shell-command.h b/include/shell-command.h new file mode 100644 index 0000000..2388963 --- /dev/null +++ b/include/shell-command.h @@ -0,0 +1,18 @@ +#ifndef SHELL_COMMAND_H +#define SHELL_COMMAND_H + +#include "inhibit-interface.h" + +typedef struct { + char *screensaver_inhibit; + char *screensaver_uninhibit; + char *suspend_inhibit; + char *suspend_uninhibit; + char *any_inhibit; + char *any_uninhibit; +} ShellCommandConfig; + +InhibitInterface *shell_command_create(const ShellCommandConfig *config); +void shell_command_config_free(ShellCommandConfig *config); + +#endif diff --git a/include/xidlehook.h b/include/xidlehook.h new file mode 100644 index 0000000..b3b63a1 --- /dev/null +++ b/include/xidlehook.h @@ -0,0 +1,8 @@ +#ifndef XIDLEHOOK_H +#define XIDLEHOOK_H + +#include "inhibit-interface.h" + +InhibitInterface *xidlehook_create(const char *socket_path); + +#endif diff --git a/src/dbus-cinnamon-screensaver.c b/src/dbus-cinnamon-screensaver.c new file mode 100644 index 0000000..e3bee33 --- /dev/null +++ b/src/dbus-cinnamon-screensaver.c @@ -0,0 +1,192 @@ +#include "dbus-cinnamon-screensaver.h" +#include "dbus-common.h" +#include +#include +#include +#include + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; +} CinnamonSSData; + +static int cinnamon_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + CinnamonSSData* data = userdata; + + char *app_name = NULL, *reason = NULL; + if (dbus_parse_inhibit_request(m, &app_name, &reason) < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "cinnamon_%u", cookie); + strncpy(entry.app_name, app_name ? app_name : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + free(app_name); + free(reason); + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int cinnamon_method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + CinnamonSSData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "cinnamon_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int cinnamon_method_simulate_activity(sd_bus_message* m, void* userdata, + sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, ""); +} + +static int cinnamon_method_get_active(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 1); +} + +static const sd_bus_vtable cinnamon_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "ss", "u", cinnamon_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnInhibit", "u", "", cinnamon_method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SimulateUserActivity", "", "", cinnamon_method_simulate_activity, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetActive", "", "b", cinnamon_method_get_active, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END}; + +static int cinnamon_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + CinnamonSSData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.cinnamon.ScreenSaver", "/org/cinnamon/ScreenSaver", + "org.cinnamon.ScreenSaver"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for cinnamon-screensaver: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, cinnamon_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add cinnamon vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.cinnamon.ScreenSaver'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void cinnamon_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + CinnamonSSData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int cinnamon_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int cinnamon_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int cinnamon_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + CinnamonSSData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void cinnamon_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + CinnamonSSData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_cinnamon_screensaver_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + CinnamonSSData* data = calloc(1, sizeof(CinnamonSSData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "cinnamon-screensaver"; + iface->init = cinnamon_init; + iface->cleanup = cinnamon_cleanup; + iface->inhibit = cinnamon_inhibit; + iface->uninhibit = cinnamon_uninhibit; + iface->poll = cinnamon_poll; + iface->set_callback = cinnamon_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/dbus-freedesktop-powermanager.c b/src/dbus-freedesktop-powermanager.c new file mode 100644 index 0000000..a8e8b2c --- /dev/null +++ b/src/dbus-freedesktop-powermanager.c @@ -0,0 +1,192 @@ +#include "dbus-freedesktop-powermanager.h" +#include "dbus-common.h" +#include +#include +#include +#include + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; + int has_inhibit; +} FDPMData; + +static int pm_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + FDPMData* data = userdata; + + char *app_name = NULL, *reason = NULL; + if (dbus_parse_inhibit_request(m, &app_name, &reason) < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitEntry entry = {.type = INHIBIT_TYPE_SUSPEND, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "fdpm_%u", cookie); + strncpy(entry.app_name, app_name ? app_name : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + int had_inhibit = data->has_inhibit; + data->has_inhibit = 1; + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + if (!had_inhibit && data->service.bus) { + dbus_send_signal(&data->service, "HasInhibitChanged", "b", 1); + } + + free(app_name); + free(reason); + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int pm_method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + FDPMData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_SUSPEND, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "fdpm_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int pm_method_has_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + FDPMData* data = userdata; + return sd_bus_reply_method_return(m, "b", data->has_inhibit); +} + +static const sd_bus_vtable powermanager_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "ss", "u", pm_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnInhibit", "u", "", pm_method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("HasInhibit", "", "b", pm_method_has_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_SIGNAL("HasInhibitChanged", "b", 0), + SD_BUS_VTABLE_END}; + +static int fdpm_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + FDPMData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.freedesktop.PowerManagement", + "/org/freedesktop/PowerManagement", "org.freedesktop.PowerManagement"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for powermanager: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, powermanager_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add powermanager vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.freedesktop.PowerManagement'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void fdpm_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + FDPMData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int fdpm_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int fdpm_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int fdpm_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + FDPMData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void fdpm_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + FDPMData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_freedesktop_powermanager_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + FDPMData* data = calloc(1, sizeof(FDPMData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "freedesktop-powermanager"; + iface->init = fdpm_init; + iface->cleanup = fdpm_cleanup; + iface->inhibit = fdpm_inhibit; + iface->uninhibit = fdpm_uninhibit; + iface->poll = fdpm_poll; + iface->set_callback = fdpm_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/dbus-freedesktop-screensaver.c b/src/dbus-freedesktop-screensaver.c new file mode 100644 index 0000000..9fe329f --- /dev/null +++ b/src/dbus-freedesktop-screensaver.c @@ -0,0 +1,198 @@ +#include "dbus-freedesktop-screensaver.h" +#include +#include +#include +#include + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; +} FDSSData; + +static int method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + FDSSData* data = userdata; + + char *app_name = NULL, *reason = NULL; + if (dbus_parse_inhibit_request(m, &app_name, &reason) < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "fdss_%u", cookie); + strncpy(entry.app_name, app_name ? app_name : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + free(app_name); + free(reason); + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + FDSSData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "fdss_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int method_simulate_user_activity(sd_bus_message* m, void* userdata, + sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, ""); +} + +static int method_get_active(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 1); +} + +static int method_get_session_idle(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 0); +} + +static const sd_bus_vtable screensaver_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "ss", "u", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnInhibit", "u", "", method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SimulateUserActivity", "", "", method_simulate_user_activity, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetActive", "", "b", method_get_active, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSessionIdle", "", "b", method_get_session_idle, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END}; + +static int fdss_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + FDSSData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.freedesktop.ScreenSaver", + "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for freedesktop-screensaver: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, screensaver_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.freedesktop.ScreenSaver'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void fdss_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + FDSSData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int fdss_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int fdss_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int fdss_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + FDSSData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void fdss_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + FDSSData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_freedesktop_screensaver_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + FDSSData* data = calloc(1, sizeof(FDSSData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "freedesktop-screensaver"; + iface->init = fdss_init; + iface->cleanup = fdss_cleanup; + iface->inhibit = fdss_inhibit; + iface->uninhibit = fdss_uninhibit; + iface->poll = fdss_poll; + iface->set_callback = fdss_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/dbus-gnome-screensaver.c b/src/dbus-gnome-screensaver.c new file mode 100644 index 0000000..dd08c88 --- /dev/null +++ b/src/dbus-gnome-screensaver.c @@ -0,0 +1,201 @@ +#include "dbus-gnome-screensaver.h" +#include "dbus-common.h" +#include +#include +#include +#include + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; +} GnomeSSData; + +static int gnome_ss_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + GnomeSSData* data = userdata; + + char *app_name = NULL, *reason = NULL; + if (dbus_parse_inhibit_request(m, &app_name, &reason) < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "gnome_ss_%u", cookie); + strncpy(entry.app_name, app_name ? app_name : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + free(app_name); + free(reason); + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int gnome_ss_method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + GnomeSSData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "gnome_ss_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int gnome_ss_method_simulate_activity(sd_bus_message* m, void* userdata, + sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, ""); +} + +static int gnome_ss_method_get_active(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 1); +} + +static int gnome_ss_method_get_session_idle(sd_bus_message* m, void* userdata, + sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 0); +} + +static const sd_bus_vtable gnome_ss_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "ss", "u", gnome_ss_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnInhibit", "u", "", gnome_ss_method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SimulateUserActivity", "", "", gnome_ss_method_simulate_activity, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetActive", "", "b", gnome_ss_method_get_active, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetSessionIdle", "", "b", gnome_ss_method_get_session_idle, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END}; + +static int gnome_ss_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + GnomeSSData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", + "org.gnome.ScreenSaver"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for gnome-screensaver: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, gnome_ss_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add gnome-screensaver vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.gnome.ScreenSaver'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void gnome_ss_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + GnomeSSData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int gnome_ss_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int gnome_ss_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int gnome_ss_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + GnomeSSData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void gnome_ss_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + GnomeSSData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_gnome_screensaver_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + GnomeSSData* data = calloc(1, sizeof(GnomeSSData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "gnome-screensaver"; + iface->init = gnome_ss_init; + iface->cleanup = gnome_ss_cleanup; + iface->inhibit = gnome_ss_inhibit; + iface->uninhibit = gnome_ss_uninhibit; + iface->poll = gnome_ss_poll; + iface->set_callback = gnome_ss_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/dbus-gnome-session.c b/src/dbus-gnome-session.c new file mode 100644 index 0000000..3871e73 --- /dev/null +++ b/src/dbus-gnome-session.c @@ -0,0 +1,195 @@ +#include "dbus-gnome-session.h" +#include "dbus-common.h" +#include +#include +#include +#include + +#define GNOME_INHIBIT_IDLE 0x01 +#define GNOME_INHIBIT_SUSPEND 0x04 +#define GNOME_INHIBIT_LOGOUT 0x02 +#define GNOME_INHIBIT_SWITCH_USERS 0x08 + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; +} GnomeSessionData; + +static int gnome_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + GnomeSessionData* data = userdata; + + char * app_id = NULL, *reason = NULL; + uint32_t flags = 0, toplevel_xid = 0; + + int r = sd_bus_message_read(m, "susu", &app_id, &toplevel_xid, &reason, &flags); + if (r < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitType type = INHIBIT_TYPE_IDLE; + if (flags & GNOME_INHIBIT_SUSPEND) { + type = INHIBIT_TYPE_SUSPEND; + } else if (flags & GNOME_INHIBIT_IDLE) { + type = INHIBIT_TYPE_IDLE; + } + + InhibitEntry entry = {.type = type, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "gnome_sess_%u", cookie); + strncpy(entry.app_name, app_id ? app_id : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int gnome_method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + GnomeSessionData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_IDLE, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "gnome_sess_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int gnome_method_is_inhibited(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 0); +} + +static const sd_bus_vtable gnome_session_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "susu", "u", gnome_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Uninhibit", "u", "", gnome_method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("IsInhibited", "u", "b", gnome_method_is_inhibited, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END}; + +static int gnome_session_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + GnomeSessionData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.gnome.SessionManager", "/org/gnome/SessionManager", + "org.gnome.SessionManager"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for gnome-session: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, gnome_session_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add gnome-session vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.gnome.SessionManager'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void gnome_session_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + GnomeSessionData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int gnome_session_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int gnome_session_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int gnome_session_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + GnomeSessionData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void gnome_session_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + GnomeSessionData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_gnome_session_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + GnomeSessionData* data = calloc(1, sizeof(GnomeSessionData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "gnome-session"; + iface->init = gnome_session_init; + iface->cleanup = gnome_session_cleanup; + iface->inhibit = gnome_session_inhibit; + iface->uninhibit = gnome_session_uninhibit; + iface->poll = gnome_session_poll; + iface->set_callback = gnome_session_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/dbus-mate-screensaver.c b/src/dbus-mate-screensaver.c new file mode 100644 index 0000000..4a6d76e --- /dev/null +++ b/src/dbus-mate-screensaver.c @@ -0,0 +1,192 @@ +#include "dbus-mate-screensaver.h" +#include "dbus-common.h" +#include +#include +#include +#include + +typedef struct { + DBusService service; + InhibitEventCallback callback; + uint32_t next_cookie; +} MateSSData; + +static int mate_method_inhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + MateSSData* data = userdata; + + char *app_name = NULL, *reason = NULL; + if (dbus_parse_inhibit_request(m, &app_name, &reason) < 0) { + return sd_bus_reply_method_return(m, "u", 0); + } + + uint32_t cookie = data->next_cookie++; + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "mate_%u", cookie); + strncpy(entry.app_name, app_name ? app_name : "unknown", sizeof(entry.app_name) - 1); + strncpy(entry.reason, reason ? reason : "", sizeof(entry.reason) - 1); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 1); + } + + free(app_name); + free(reason); + + return sd_bus_reply_method_return(m, "u", cookie); +} + +static int mate_method_uninhibit(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) ret_error; + MateSSData* data = userdata; + + uint32_t cookie = 0; + int r = sd_bus_message_read(m, "u", &cookie); + if (r < 0) { + return sd_bus_reply_method_return(m, ""); + } + + InhibitEntry entry = {.type = INHIBIT_TYPE_SCREENSAVER, .cookie = cookie}; + snprintf(entry.id, sizeof(entry.id), "mate_%u", cookie); + + if (data->callback) { + data->callback((InhibitInterface*) data->service.parent, &entry, 0); + } + + return sd_bus_reply_method_return(m, ""); +} + +static int mate_method_simulate_activity(sd_bus_message* m, void* userdata, + sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, ""); +} + +static int mate_method_get_active(sd_bus_message* m, void* userdata, sd_bus_error* ret_error) { + (void) userdata; + (void) ret_error; + return sd_bus_reply_method_return(m, "b", 1); +} + +static const sd_bus_vtable mate_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Inhibit", "ss", "u", mate_method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("UnInhibit", "u", "", mate_method_uninhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SimulateUserActivity", "", "", mate_method_simulate_activity, + SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetActive", "", "b", mate_method_get_active, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END}; + +static int mate_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + MateSSData* data = iface->private_data; + + int r = dbus_service_init(&data->service, "org.mate.ScreenSaver", "/org/mate/ScreenSaver", + "org.mate.ScreenSaver"); + if (r < 0) + return -1; + + r = sd_bus_open(&data->service.bus); + if (r < 0) { + fprintf(stderr, "Failed to open bus for mate-screensaver: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + + data->service.parent = iface; + + r = dbus_service_detect_mode(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + if (data->service.mode == DBUS_MODE_IMPLEMENT) { + r = dbus_service_claim_name(&data->service); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + + r = sd_bus_add_object_vtable(data->service.bus, &data->service.slot, data->service.object_path, + data->service.interface_name, mate_vtable, data); + if (r < 0) { + fprintf(stderr, "Failed to add mate vtable: %s\n", strerror(-r)); + dbus_service_cleanup(&data->service); + return -1; + } + } else { + const char* rules[] = {"interface='org.mate.ScreenSaver'"}; + r = dbus_service_become_monitor(&data->service, rules, 1); + if (r < 0) { + dbus_service_cleanup(&data->service); + return -1; + } + } + + return 0; +} + +static void mate_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + MateSSData* data = iface->private_data; + dbus_service_cleanup(&data->service); +} + +static int mate_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) iface; + (void) type; + (void) app_name; + (void) reason; + (void) out_entry; + return 0; +} + +static int mate_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + (void) entry; + return 0; +} + +static int mate_poll(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + MateSSData* data = iface->private_data; + return dbus_service_poll(&data->service); +} + +static void mate_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + if (!iface || !iface->private_data) + return; + MateSSData* data = iface->private_data; + data->callback = cb; +} + +InhibitInterface* dbus_mate_screensaver_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + MateSSData* data = calloc(1, sizeof(MateSSData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "mate-screensaver"; + iface->init = mate_init; + iface->cleanup = mate_cleanup; + iface->inhibit = mate_inhibit; + iface->uninhibit = mate_uninhibit; + iface->poll = mate_poll; + iface->set_callback = mate_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/kernel-wakelock.c b/src/kernel-wakelock.c new file mode 100644 index 0000000..e4ba7d0 --- /dev/null +++ b/src/kernel-wakelock.c @@ -0,0 +1,128 @@ +#include "kernel-wakelock.h" +#include +#include +#include +#include +#include +#include +#include + +#define WAKE_LOCK_PATH "/sys/power/wake_lock" +#define WAKE_UNLOCK_PATH "/sys/power/wake_unlock" + +typedef struct { + uint32_t next_id; +} KernelWakelockData; + +static int kernel_wakelock_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + int fd = open(WAKE_LOCK_PATH, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Kernel wakelock not available (no %s): %s\n", WAKE_LOCK_PATH, strerror(errno)); + return -1; + } + close(fd); + + return 0; +} + +static void kernel_wakelock_cleanup(InhibitInterface* iface) { (void) iface; } + +static int kernel_wakelock_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) type; + (void) app_name; + (void) reason; + + if (!iface || !iface->private_data || !out_entry) + return -1; + + KernelWakelockData* data = iface->private_data; + + int fd = open(WAKE_LOCK_PATH, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open wake_lock: %s\n", strerror(errno)); + return -1; + } + + uint32_t id = data->next_id++; + char lock_name[64]; + snprintf(lock_name, sizeof(lock_name), "sin_%u", id); + + ssize_t written = write(fd, lock_name, strlen(lock_name)); + close(fd); + + if (written < 0) { + fprintf(stderr, "Failed to write wake_lock: %s\n", strerror(errno)); + return -1; + } + + snprintf(out_entry->id, sizeof(out_entry->id), "kernel_%u", id); + out_entry->cookie = id; + out_entry->type = INHIBIT_TYPE_SUSPEND; + strncpy(out_entry->app_name, app_name ? app_name : "unknown", sizeof(out_entry->app_name) - 1); + strncpy(out_entry->reason, reason ? reason : "", sizeof(out_entry->reason) - 1); + + return 0; +} + +static int kernel_wakelock_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) iface; + + if (!entry) + return -1; + + int fd = open(WAKE_UNLOCK_PATH, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open wake_unlock: %s\n", strerror(errno)); + return -1; + } + + char lock_name[64]; + snprintf(lock_name, sizeof(lock_name), "sin_%" PRIu64, entry->cookie); + + ssize_t written = write(fd, lock_name, strlen(lock_name)); + close(fd); + + if (written < 0) { + fprintf(stderr, "Failed to write wake_unlock: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int kernel_wakelock_poll(InhibitInterface* iface) { + (void) iface; + return 0; +} + +static void kernel_wakelock_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + (void) iface; + (void) cb; +} + +InhibitInterface* kernel_wakelock_create(void) { + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + KernelWakelockData* data = calloc(1, sizeof(KernelWakelockData)); + if (!data) { + free(iface); + return NULL; + } + + iface->name = "kernel-wakelock"; + iface->init = kernel_wakelock_init; + iface->cleanup = kernel_wakelock_cleanup; + iface->inhibit = kernel_wakelock_inhibit; + iface->uninhibit = kernel_wakelock_uninhibit; + iface->poll = kernel_wakelock_poll; + iface->set_callback = kernel_wakelock_set_callback; + iface->private_data = data; + + return iface; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7bf29d2 --- /dev/null +++ b/src/main.c @@ -0,0 +1,637 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbus-cinnamon-screensaver.h" +#include "dbus-freedesktop-powermanager.h" +#include "dbus-freedesktop-screensaver.h" +#include "dbus-gnome-screensaver.h" +#include "dbus-gnome-session.h" +#include "dbus-mate-screensaver.h" +#include "dbus-systemd-login1.h" +#include "inhibit-interface.h" +#include "kernel-wakelock.h" +#include "shell-command.h" +#include "xidlehook.h" + +#define VERSION "2.0.0" + +#define EXIT_OK 0 +#define EXIT_ARGS 1 +#define EXIT_INIT 2 +#define EXIT_RUNTIME 3 + +#define MAX_PATTERNS 128 +#define MAX_PATTERN_LEN 4096U +#define MIN_POLL_INTERVAL 0.1 +#define MAX_POLL_INTERVAL 3600.0 +#define DEFAULT_POLL_INTERVAL 2.0 +#define CMDLINE_INITIAL_SIZE 4096U +#define CMDLINE_MAX_SIZE (1024U * 1024U) + +static volatile sig_atomic_t running = 1; +static volatile sig_atomic_t cleanup_requested = 0; +static int quiet = 0; +static int daemonize = 0; + +static InhibitManager g_manager; +static ShellCommandConfig g_shell_config; + +static void handle_sig(int sig) { + (void) sig; + running = 0; + cleanup_requested = 1; +} + +static void on_inhibit_event(InhibitInterface* source, const InhibitEntry* entry, int is_inhibit) { + if (!quiet) { + fprintf(stderr, "[%s] %s: %s (%s) - %s\n", source ? source->name : "process-watcher", + is_inhibit ? "INHIBIT" : "RELEASE", entry->app_name, + inhibit_type_to_string(entry->type), entry->reason); + } + + if (is_inhibit) { + inhibit_manager_forward_inhibit(&g_manager, source, entry); + } else { + inhibit_manager_forward_uninhibit(&g_manager, source, entry); + } +} + +static char* read_cmdline(pid_t pid) { + char path[64]; + int n = snprintf(path, sizeof(path), "/proc/%d/cmdline", (int) pid); + if (n < 0 || (size_t) n >= sizeof(path)) { + return NULL; + } + + int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) + return NULL; + + size_t cap = CMDLINE_INITIAL_SIZE; + char* buf = malloc(cap); + if (!buf) { + close(fd); + return NULL; + } + + size_t len = 0; + for (;;) { + if (len >= CMDLINE_MAX_SIZE) { + free(buf); + close(fd); + return NULL; + } + + ssize_t r = read(fd, buf + len, cap - len); + if (r < 0) { + if (errno == EINTR) + continue; + free(buf); + close(fd); + return NULL; + } + if (r == 0) + break; + + len += (size_t) r; + if (len >= cap) { + size_t new_cap = cap * 2U; + if (new_cap > CMDLINE_MAX_SIZE) + new_cap = CMDLINE_MAX_SIZE; + if (new_cap <= cap) { + break; + } + char* nb = realloc(buf, new_cap); + if (!nb) { + free(buf); + close(fd); + return NULL; + } + buf = nb; + cap = new_cap; + } + } + close(fd); + + if (len == 0) { + free(buf); + return NULL; + } + + for (size_t j = 0; j < len; ++j) { + if (buf[j] == '\0') + buf[j] = ' '; + } + + if (len >= cap) { + char* nb = realloc(buf, len + 1); + if (!nb) { + free(buf); + return NULL; + } + buf = nb; + } + buf[len] = '\0'; + return buf; +} + +static int any_process_running(char** patterns, int npats) { + if (!patterns || npats <= 0) + return 0; + + DIR* d = opendir("/proc"); + if (!d) { + fprintf(stderr, "opendir /proc failed: %s\n", strerror(errno)); + return 0; + } + + struct dirent* ent; + int found = 0; + + while ((ent = readdir(d)) != NULL && !found) { + const char* name = ent->d_name; + + if (!isdigit((unsigned char) name[0])) + continue; + + int all_digits = 1; + for (const char* p = name; *p; ++p) { + if (!isdigit((unsigned char) *p)) { + all_digits = 0; + break; + } + } + if (!all_digits) + continue; + + char* endptr; + long pid_long = strtol(name, &endptr, 10); + if (*endptr != '\0' || pid_long <= 0 || pid_long > INT_MAX) + continue; + + pid_t pid = (pid_t) pid_long; + char* cmd = read_cmdline(pid); + if (!cmd) + continue; + + for (int i = 0; i < npats; ++i) { + if (strstr(cmd, patterns[i]) != NULL) { + found = 1; + break; + } + } + free(cmd); + } + closedir(d); + return found; +} + +static void usage(FILE* f, const char* prog) { + fprintf(f, + "Usage: %s [OPTIONS] -n [-n ...]\n" + "\n" + "Unified inhibitor daemon - bridges multiple inhibit interfaces\n" + "\n" + "Options:\n" + " -n, --name PATTERN Process cmdline substring to watch " + "(repeatable)\n" + " --what WHAT What to inhibit: idle, sleep, etc. " + "(default: idle)\n" + " --poll SECONDS Poll interval (%.1f-%.1f, default: %.1f)\n" + " -d, --daemon Run as daemon (fork to background)\n" + " -q, --quiet Suppress informational output\n" + " --ia, --inhibit-action CMD Command to run on any inhibit\n" + " --uia, --uninhibit-action CMD Command to run on any uninhibit\n" + " --sia, --screensaver-inhibit-action CMD Command for " + "screensaver inhibit\n" + " --suia, --screensaver-uninhibit-action CMD Command for " + "screensaver uninhibit\n" + " -h, --help Show this help\n" + " -v, --version Show version\n", + prog, MIN_POLL_INTERVAL, MAX_POLL_INTERVAL, DEFAULT_POLL_INTERVAL); +} + +static int validate_string_param(const char* param, const char* name, size_t max_len) { + if (!param || param[0] == '\0') { + fprintf(stderr, "Error: %s cannot be empty\n", name); + return 0; + } + size_t len = strlen(param); + if (len > max_len) { + fprintf(stderr, "Error: %s too long (max %zu characters)\n", name, max_len); + return 0; + } + return 1; +} + +static void* safe_realloc(void* ptr, size_t new_size) { + void* new_ptr = realloc(ptr, new_size); + if (!new_ptr && new_size > 0) { + return NULL; + } + return new_ptr; +} + +static int do_daemonize(void) { + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "fork failed: %s\n", strerror(errno)); + return -1; + } + + if (pid > 0) { + exit(EXIT_OK); + } + + if (setsid() < 0) { + fprintf(stderr, "setsid failed: %s\n", strerror(errno)); + return -1; + } + + signal(SIGHUP, SIG_IGN); + + pid = fork(); + if (pid < 0) { + fprintf(stderr, "second fork failed: %s\n", strerror(errno)); + return -1; + } + + if (pid > 0) { + exit(EXIT_OK); + } + + if (chdir("/") < 0) { + fprintf(stderr, "chdir failed: %s\n", strerror(errno)); + return -1; + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + int fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) { + close(fd); + } + } + + return 0; +} + +int main(int argc, char** argv) { + double poll_interval = DEFAULT_POLL_INTERVAL; + + char** patterns = NULL; + int npats = 0; + int exit_code = EXIT_OK; + int have_patterns = 0; + + memset(&g_shell_config, 0, sizeof(g_shell_config)); + + for (int i = 1; i < argc; ++i) { + if ((strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--name") == 0)) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: %s requires an argument\n", argv[i]); + usage(stderr, argv[0]); + free(patterns); + return EXIT_ARGS; + } + i++; + + if (npats >= MAX_PATTERNS) { + fprintf(stderr, "Error: Too many patterns (max %d)\n", MAX_PATTERNS); + free(patterns); + return EXIT_ARGS; + } + + if (!validate_string_param(argv[i], "pattern", MAX_PATTERN_LEN)) { + free(patterns); + return EXIT_ARGS; + } + + char** new_patterns = safe_realloc(patterns, (size_t) (npats + 1) * sizeof(char*)); + if (!new_patterns) { + fprintf(stderr, "Error: Memory allocation failed\n"); + return EXIT_INIT; + } + patterns = new_patterns; + patterns[npats++] = argv[i]; + have_patterns = 1; + + } else if (strncmp(argv[i], "--poll=", 7U) == 0) { + char* endptr; + errno = 0; + poll_interval = strtod(argv[i] + 7, &endptr); + + if (errno != 0 || *endptr != '\0' || endptr == argv[i] + 7) { + fprintf(stderr, "Error: Invalid --poll value: %s\n", argv[i] + 7); + free(patterns); + return EXIT_ARGS; + } + + if (poll_interval < MIN_POLL_INTERVAL || poll_interval > MAX_POLL_INTERVAL) { + fprintf(stderr, "Error: --poll must be between %.1f and %.1f seconds\n", MIN_POLL_INTERVAL, + MAX_POLL_INTERVAL); + free(patterns); + return EXIT_ARGS; + } + + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + usage(stdout, argv[0]); + free(patterns); + return EXIT_OK; + + } else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) { + printf("sin %s (unified inhibitor daemon)\n", VERSION); + free(patterns); + return EXIT_OK; + + } else if (strcmp(argv[i], "--quiet") == 0 || strcmp(argv[i], "-q") == 0) { + quiet = 1; + + } else if (strcmp(argv[i], "--daemon") == 0 || strcmp(argv[i], "-d") == 0) { + daemonize = 1; + + } else if (strcmp(argv[i], "--ia") == 0 || strcmp(argv[i], "--inhibit-action") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: %s requires an argument\n", argv[i]); + free(patterns); + return EXIT_ARGS; + } + i++; + g_shell_config.any_inhibit = argv[i]; + + } else if (strcmp(argv[i], "--uia") == 0 || strcmp(argv[i], "--uninhibit-action") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: %s requires an argument\n", argv[i]); + free(patterns); + return EXIT_ARGS; + } + i++; + g_shell_config.any_uninhibit = argv[i]; + + } else if (strcmp(argv[i], "--sia") == 0 || + strcmp(argv[i], "--screensaver-inhibit-action") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: %s requires an argument\n", argv[i]); + free(patterns); + return EXIT_ARGS; + } + i++; + g_shell_config.screensaver_inhibit = argv[i]; + + } else if (strcmp(argv[i], "--suia") == 0 || + strcmp(argv[i], "--screensaver-uninhibit-action") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "Error: %s requires an argument\n", argv[i]); + free(patterns); + return EXIT_ARGS; + } + i++; + g_shell_config.screensaver_uninhibit = argv[i]; + + } else if (argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] != '\0') { + if (npats >= MAX_PATTERNS) { + fprintf(stderr, "Error: Too many patterns (max %d)\n", MAX_PATTERNS); + free(patterns); + return EXIT_ARGS; + } + + if (!validate_string_param(argv[i] + 2, "pattern", MAX_PATTERN_LEN)) { + free(patterns); + return EXIT_ARGS; + } + + char** new_patterns = safe_realloc(patterns, (size_t) (npats + 1) * sizeof(char*)); + if (!new_patterns) { + fprintf(stderr, "Error: Memory allocation failed\n"); + return EXIT_INIT; + } + patterns = new_patterns; + patterns[npats++] = argv[i] + 2; + have_patterns = 1; + + } else { + fprintf(stderr, "Error: Unknown argument: %s\n", argv[i]); + usage(stderr, argv[0]); + free(patterns); + return EXIT_ARGS; + } + } + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handle_sig; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(SIGINT, &sa, NULL) < 0) { + fprintf(stderr, "Warning: Failed to set SIGINT handler: %s\n", strerror(errno)); + } + if (sigaction(SIGTERM, &sa, NULL) < 0) { + fprintf(stderr, "Warning: Failed to set SIGTERM handler: %s\n", strerror(errno)); + } + + if (daemonize) { + if (do_daemonize() < 0) { + free(patterns); + return EXIT_INIT; + } + } + + if (inhibit_manager_init(&g_manager) < 0) { + fprintf(stderr, "Failed to initialize inhibit manager\n"); + free(patterns); + return EXIT_INIT; + } + + inhibit_manager_set_callback(&g_manager, on_inhibit_event); + + InhibitInterface* iface; + + iface = dbus_freedesktop_screensaver_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_freedesktop_powermanager_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_gnome_session_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_gnome_screensaver_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_cinnamon_screensaver_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_mate_screensaver_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = dbus_systemd_login1_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = xidlehook_create("/tmp/xidlehook.sock"); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + iface = kernel_wakelock_create(); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + + if (g_shell_config.any_inhibit || g_shell_config.any_uninhibit || + g_shell_config.screensaver_inhibit || g_shell_config.screensaver_uninhibit) { + iface = shell_command_create(&g_shell_config); + if (iface) { + if (inhibit_manager_register(&g_manager, iface) == 0) { + if (!quiet) + fprintf(stderr, "Registered: %s\n", iface->name); + } else { + free(iface->private_data); + free(iface); + } + } + } + + if (!quiet) { + fprintf(stderr, "Unified inhibitor daemon started (%d interfaces)\n", g_manager.num_interfaces); + if (have_patterns) { + fprintf(stderr, "Watching %d process pattern(s)\n", npats); + } + } + + int process_inhibited = 0; + + while (running) { + inhibit_manager_poll_all(&g_manager); + + if (have_patterns) { + int found = any_process_running(patterns, npats); + + if (found && !process_inhibited) { + if (!quiet) + fprintf(stderr, "Target process found - creating inhibit\n"); + + InhibitEntry entry = {.type = INHIBIT_TYPE_IDLE, .cookie = 0}; + snprintf(entry.id, sizeof(entry.id), "process_watcher"); + strncpy(entry.app_name, "sin-process-watcher", sizeof(entry.app_name) - 1); + strncpy(entry.reason, "Process matching pattern is running", sizeof(entry.reason) - 1); + + on_inhibit_event(NULL, &entry, 1); + process_inhibited = 1; + } else if (!found && process_inhibited) { + if (!quiet) + fprintf(stderr, "Target process exited - releasing inhibit\n"); + + InhibitEntry entry = {.type = INHIBIT_TYPE_IDLE, .cookie = 0}; + snprintf(entry.id, sizeof(entry.id), "process_watcher"); + + on_inhibit_event(NULL, &entry, 0); + process_inhibited = 0; + } + } + + struct timespec ts; + ts.tv_sec = (time_t) poll_interval; + ts.tv_nsec = (long) ((poll_interval - (time_t) poll_interval) * 1e9); + + while (nanosleep(&ts, &ts) < 0) { + if (errno != EINTR || !running) + break; + } + } + + if (cleanup_requested && !quiet) { + fprintf(stderr, "Shutting down gracefully...\n"); + } + + inhibit_manager_cleanup(&g_manager); + free(patterns); + + return exit_code; +} diff --git a/src/shell-command.c b/src/shell-command.c new file mode 100644 index 0000000..2cd9361 --- /dev/null +++ b/src/shell-command.c @@ -0,0 +1,179 @@ +#include "shell-command.h" +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + ShellCommandConfig config; + uint32_t next_id; +} ShellCommandData; + +static int execute_command(const char* cmd) { + if (!cmd || !cmd[0]) + return 0; + + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "Failed to fork: %s\n", strerror(errno)); + return -1; + } + + if (pid == 0) { + execl("/bin/sh", "sh", "-c", cmd, NULL); + _exit(127); + } + + int status; + if (waitpid(pid, &status, 0) < 0) { + fprintf(stderr, "Failed to wait for child: %s\n", strerror(errno)); + return -1; + } + + return WIFEXITED(status) ? WEXITSTATUS(status) : -1; +} + +static int shell_cmd_init(InhibitInterface* iface) { + (void) iface; + return 0; +} + +static void shell_cmd_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + ShellCommandData* data = iface->private_data; + + free(data->config.screensaver_inhibit); + free(data->config.screensaver_uninhibit); + free(data->config.suspend_inhibit); + free(data->config.suspend_uninhibit); + free(data->config.any_inhibit); + free(data->config.any_uninhibit); +} + +static int shell_cmd_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) app_name; + (void) reason; + + if (!iface || !iface->private_data || !out_entry) + return -1; + + ShellCommandData* data = iface->private_data; + const char* cmd = NULL; + + if (type & INHIBIT_TYPE_SCREENSAVER && data->config.screensaver_inhibit) { + cmd = data->config.screensaver_inhibit; + } else if (type & INHIBIT_TYPE_SUSPEND && data->config.suspend_inhibit) { + cmd = data->config.suspend_inhibit; + } else if (data->config.any_inhibit) { + cmd = data->config.any_inhibit; + } + + if (cmd) { + execute_command(cmd); + } + + uint32_t id = data->next_id++; + snprintf(out_entry->id, sizeof(out_entry->id), "shell_%u", id); + out_entry->cookie = id; + out_entry->type = type; + strncpy(out_entry->app_name, app_name ? app_name : "unknown", sizeof(out_entry->app_name) - 1); + strncpy(out_entry->reason, reason ? reason : "", sizeof(out_entry->reason) - 1); + + return 0; +} + +static int shell_cmd_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) entry; + + if (!iface || !iface->private_data) + return -1; + + ShellCommandData* data = iface->private_data; + const char* cmd = NULL; + + if (entry->type & INHIBIT_TYPE_SCREENSAVER && data->config.screensaver_uninhibit) { + cmd = data->config.screensaver_uninhibit; + } else if (entry->type & INHIBIT_TYPE_SUSPEND && data->config.suspend_uninhibit) { + cmd = data->config.suspend_uninhibit; + } else if (data->config.any_uninhibit) { + cmd = data->config.any_uninhibit; + } + + if (cmd) { + execute_command(cmd); + } + + return 0; +} + +static int shell_cmd_poll(InhibitInterface* iface) { + (void) iface; + return 0; +} + +static void shell_cmd_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + (void) iface; + (void) cb; +} + +InhibitInterface* shell_command_create(const ShellCommandConfig* config) { + if (!config) + return NULL; + + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + ShellCommandData* data = calloc(1, sizeof(ShellCommandData)); + if (!data) { + free(iface); + return NULL; + } + + if (config->screensaver_inhibit) { + data->config.screensaver_inhibit = strdup(config->screensaver_inhibit); + } + if (config->screensaver_uninhibit) { + data->config.screensaver_uninhibit = strdup(config->screensaver_uninhibit); + } + if (config->suspend_inhibit) { + data->config.suspend_inhibit = strdup(config->suspend_inhibit); + } + if (config->suspend_uninhibit) { + data->config.suspend_uninhibit = strdup(config->suspend_uninhibit); + } + if (config->any_inhibit) { + data->config.any_inhibit = strdup(config->any_inhibit); + } + if (config->any_uninhibit) { + data->config.any_uninhibit = strdup(config->any_uninhibit); + } + + iface->name = "shell-command"; + iface->init = shell_cmd_init; + iface->cleanup = shell_cmd_cleanup; + iface->inhibit = shell_cmd_inhibit; + iface->uninhibit = shell_cmd_uninhibit; + iface->poll = shell_cmd_poll; + iface->set_callback = shell_cmd_set_callback; + iface->private_data = data; + + return iface; +} + +void shell_command_config_free(ShellCommandConfig* config) { + if (!config) + return; + free(config->screensaver_inhibit); + free(config->screensaver_uninhibit); + free(config->suspend_inhibit); + free(config->suspend_uninhibit); + free(config->any_inhibit); + free(config->any_uninhibit); + memset(config, 0, sizeof(*config)); +} diff --git a/src/xidlehook.c b/src/xidlehook.c new file mode 100644 index 0000000..33398f9 --- /dev/null +++ b/src/xidlehook.c @@ -0,0 +1,146 @@ +#include "xidlehook.h" +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + char* socket_path; + int sock_fd; + uint32_t next_id; +} XidlehookData; + +static int xidlehook_init(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return -1; + + XidlehookData* data = iface->private_data; + + data->sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (data->sock_fd < 0) { + fprintf(stderr, "Failed to create xidlehook socket: %s\n", strerror(errno)); + return -1; + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, data->socket_path, sizeof(addr.sun_path) - 1); + + if (connect(data->sock_fd, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + fprintf(stderr, "Failed to connect to xidlehook socket at %s: %s\n", data->socket_path, + strerror(errno)); + close(data->sock_fd); + data->sock_fd = -1; + return -1; + } + + return 0; +} + +static void xidlehook_cleanup(InhibitInterface* iface) { + if (!iface || !iface->private_data) + return; + XidlehookData* data = iface->private_data; + if (data->sock_fd >= 0) { + close(data->sock_fd); + data->sock_fd = -1; + } +} + +static int xidlehook_inhibit(InhibitInterface* iface, InhibitType type, const char* app_name, + const char* reason, InhibitEntry* out_entry) { + (void) type; + (void) app_name; + (void) reason; + + if (!iface || !iface->private_data || !out_entry) + return -1; + + XidlehookData* data = iface->private_data; + + if (data->sock_fd < 0) + return -1; + + const char* cmd = "disable\n"; + if (write(data->sock_fd, cmd, strlen(cmd)) < 0) { + fprintf(stderr, "Failed to send disable to xidlehook: %s\n", strerror(errno)); + return -1; + } + + uint32_t id = data->next_id++; + snprintf(out_entry->id, sizeof(out_entry->id), "xidlehook_%u", id); + out_entry->cookie = id; + out_entry->type = INHIBIT_TYPE_SCREENSAVER; + strncpy(out_entry->app_name, app_name ? app_name : "unknown", sizeof(out_entry->app_name) - 1); + strncpy(out_entry->reason, reason ? reason : "", sizeof(out_entry->reason) - 1); + + return 0; +} + +static int xidlehook_uninhibit(InhibitInterface* iface, const InhibitEntry* entry) { + (void) entry; + + if (!iface || !iface->private_data) + return -1; + + XidlehookData* data = iface->private_data; + + if (data->sock_fd < 0) + return -1; + + const char* cmd = "enable\n"; + if (write(data->sock_fd, cmd, strlen(cmd)) < 0) { + fprintf(stderr, "Failed to send enable to xidlehook: %s\n", strerror(errno)); + return -1; + } + + return 0; +} + +static int xidlehook_poll(InhibitInterface* iface) { + (void) iface; + return 0; +} + +static void xidlehook_set_callback(InhibitInterface* iface, InhibitEventCallback cb) { + (void) iface; + (void) cb; +} + +InhibitInterface* xidlehook_create(const char* socket_path) { + if (!socket_path) + return NULL; + + InhibitInterface* iface = calloc(1, sizeof(InhibitInterface)); + if (!iface) + return NULL; + + XidlehookData* data = calloc(1, sizeof(XidlehookData)); + if (!data) { + free(iface); + return NULL; + } + + data->socket_path = strdup(socket_path); + if (!data->socket_path) { + free(data); + free(iface); + return NULL; + } + data->sock_fd = -1; + + iface->name = "xidlehook"; + iface->init = xidlehook_init; + iface->cleanup = xidlehook_cleanup; + iface->inhibit = xidlehook_inhibit; + iface->uninhibit = xidlehook_uninhibit; + iface->poll = xidlehook_poll; + iface->set_callback = xidlehook_set_callback; + iface->private_data = data; + + return iface; +}