diff options
author | lamp | 2023-03-05 21:43:24 +0000 |
---|---|---|
committer | lamp | 2023-03-05 21:43:24 +0000 |
commit | 42044f09272acdcc30a280e072effe429739c32a (patch) | |
tree | 737f34d33e42c6ca6da9f9068a70e176de1736e9 |
initmain
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | LICENSE | 13 | ||||
-rw-r--r-- | README.md | 15 | ||||
-rw-r--r-- | main.c | 299 | ||||
-rw-r--r-- | meson.build | 36 | ||||
-rw-r--r-- | protocol/meson.build | 46 | ||||
-rw-r--r-- | shell.nix | 5 |
7 files changed, 416 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e050ba0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build +/.cache
\ No newline at end of file @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..12095d1 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# wlr-idle-proxy + +A simple org.freedesktop.ScreenSaver implementation for Wayland compositors that support idle-inhibit-unstable-v1. +This allows software that inhibits screen locking over DBus to transparently inhibit idling on these compositors. + +## build + +``` +meson build +ninja -C build +``` + +## usage + +Just run the output binary in `build`. @@ -0,0 +1,299 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <dbus/dbus.h> + +#include "dbus/dbus-protocol.h" +#include "dbus/dbus-shared.h" +#include "idle-inhibit-unstable-v1-client-protocol.h" +#include "wayland-client-core.h" +#include "wayland-util.h" +#include <wayland-client.h> + +const char *introspection_data = + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection " + "1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">" + "<node name=\"/org/freedesktop/ScreenSaver\">" + " <interface name=\"org.freedesktop.ScreenSaver\">" + " <tp:docstring>" + " The Idle Inhibition Service manages the inhibition requests." + " </tp:docstring>" + " <method name=\"Inhibit\">" + " <tp:docstring>Inhibit idleness for the caller " + "application.</tp:docstring>" + " <arg name=\"application_name\" type=\"s\" direction=\"in\">" + " <tp:docstring>A unique identifier for the application, usually a " + "reverse domain (such as 'org.freedesktop.example').</tp:docstring>" + " </arg>" + " <arg name=\"reason_for_inhibit\" type=\"s\" direction=\"in\">" + " <tp:docstring>A human-readable and possibly translated string " + "explaining the reason why idleness is inhibited (such as 'Playing a " + "movie').</tp:docstring>" + " </arg>" + " <arg name=\"cookie\" type=\"u\" direction=\"out\">" + " <tp:docstring>A cookie uniquely representing the inhibition " + "request, to be passed to UnInhibit when done.</tp:docstring>" + " </arg>" + " </method>" + " <method name=\"UnInhibit\">" + " <tp:docstring>Disable inhibit idleness for the caller " + "application.</tp:docstring>" + " <arg name=\"cookie\" type=\"u\" direction=\"in\">" + " <tp:docstring>A cookie representing the inhibition request, as " + "returned by the 'Inhibit' function.</tp:docstring>" + " </arg>" + " </method>" + " </interface>" + "</node>"; + +struct Inhibitor { + struct wl_list link; + char *application_name; + char *reason_for_inhibit; + uint32_t cookie; +}; + +static struct wl_list inhibitor_list; + +static struct wl_surface *surface = NULL; +struct wl_display *display = NULL; +static struct wl_compositor *compositor = NULL; +static struct wl_seat *seat = NULL; +static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL; +static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version) { + if (strcmp(interface, "wl_compositor") == 0) { + compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == + 0) { + idle_inhibit_manager = wl_registry_bind( + registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + seat = wl_registry_bind(registry, name, &wl_seat_interface, version); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // who cares +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +static DBusHandlerResult handle_message(DBusConnection *conn, + DBusMessage *message, void *data) { + const char *interface_name = dbus_message_get_interface(message); + const char *member_name = dbus_message_get_member(message); + if (!strncmp(interface_name, "org.freedesktop.DBus.Introspectable", + strlen("org.freedesktop.DBus.Introspectable"))) { + if (!strncmp(member_name, "Introspect", strlen("Introspect"))) { + DBusMessage *reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_data, + DBUS_TYPE_INVALID); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + } else if (!strncmp(interface_name, "org.freedesktop.ScreenSaver", + strlen("org.freedesktop.ScreenSaver"))) { + if (!strncmp(member_name, "Inhibit", strlen("Inhibit"))) { + DBusMessage *reply = dbus_message_new_method_return(message); + DBusError err; + char *application_name; + char *reason_for_inhibit; + dbus_error_init(&err); + struct Inhibitor *new_inhibitor = malloc(sizeof(struct Inhibitor)); + if (!new_inhibitor) { + fprintf(stderr, "Memory allocation failed!"); + exit(EXIT_FAILURE); + } + dbus_message_get_args(message, &err, DBUS_TYPE_STRING, &application_name, + DBUS_TYPE_STRING, &reason_for_inhibit, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&err)) { + reply = dbus_message_new_error(message, "wrong_arguments", + "Illegal arguments"); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + new_inhibitor->application_name = malloc(strlen(application_name) + 1); + if (!new_inhibitor->application_name) { + fprintf(stderr, "Memory allocation failed!"); + exit(EXIT_FAILURE); + } + strcpy(new_inhibitor->application_name, application_name); + new_inhibitor->reason_for_inhibit = + malloc(strlen(reason_for_inhibit) + 1); + if (!new_inhibitor->reason_for_inhibit) { + fprintf(stderr, "Memory allocation failed!"); + exit(EXIT_FAILURE); + } + strcpy(new_inhibitor->reason_for_inhibit, reason_for_inhibit); + uint32_t cookie = rand(); + bool cookie_in_list = true; + while (cookie_in_list) { + cookie_in_list = false; + struct Inhibitor *elm; + wl_list_for_each(elm, &inhibitor_list, link) { + if (elm->cookie == cookie) { + cookie_in_list = true; + cookie = rand(); + break; + } + } + } + new_inhibitor->cookie = cookie; + wl_list_insert(inhibitor_list.prev, &new_inhibitor->link); + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_UINT32, &cookie, + DBUS_TYPE_INVALID); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + printf("Added inhibitor: %s, %s, %d\n", new_inhibitor->application_name, + new_inhibitor->reason_for_inhibit, new_inhibitor->cookie); + if (!wl_list_empty(&inhibitor_list) && idle_inhibitor == NULL) { + printf("Began inhibiting over wayland...\n"); + idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( + idle_inhibit_manager, surface); + if (wl_display_roundtrip(display) == -1) { + fprintf(stderr, "Failed to commit wayland events!"); + exit(EXIT_FAILURE); + } + } + return DBUS_HANDLER_RESULT_HANDLED; + } else if (!strncmp(member_name, "UnInhibit", strlen("UnInhibit"))) { + DBusMessage *reply = dbus_message_new_method_return(message); + DBusError err; + uint32_t cookie; + struct Inhibitor *to_remove = NULL; + struct Inhibitor *elm; + dbus_error_init(&err); + dbus_message_get_args(message, &err, DBUS_TYPE_UINT32, &cookie, + DBUS_TYPE_INVALID); + if (dbus_error_is_set(&err)) { + reply = dbus_message_new_error(message, "wrong_arguments", + "Illegal arguments"); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + wl_list_for_each(elm, &inhibitor_list, link) { + if (elm->cookie == cookie) { + to_remove = elm; + break; + } + } + if (!to_remove) { + printf("Recieved illegal cookie!"); + reply = dbus_message_new_error(message, "wrong_arguments", + "Illegal cookie"); + dbus_connection_send(conn, reply, NULL); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + } + printf("Removed inhibitor: %s, %s, %d\n", to_remove->application_name, + to_remove->reason_for_inhibit, to_remove->cookie); + wl_list_remove(&to_remove->link); + free(to_remove->application_name); + free(to_remove->reason_for_inhibit); + free(to_remove); + if (wl_list_empty(&inhibitor_list) && idle_inhibitor != NULL) { + printf("Stopped inhibiting over wayland...\n"); + zwp_idle_inhibitor_v1_destroy(idle_inhibitor); + idle_inhibitor = NULL; + if (wl_display_roundtrip(display) == -1) { + fprintf(stderr, "Failed to commit wayland events!"); + exit(EXIT_FAILURE); + } + } + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int main(int argc, char **argv) { + srand(time(NULL)); + display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "Failed to create display\n"); + return EXIT_FAILURE; + } + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_roundtrip(display); + + if (compositor == NULL) { + fprintf(stderr, "wl-compositor not available\n"); + return EXIT_FAILURE; + } + if (idle_inhibit_manager == NULL) { + fprintf(stderr, "idle-inhibit not available\n"); + return EXIT_FAILURE; + } + + surface = wl_compositor_create_surface(compositor); + + wl_surface_commit(surface); + wl_display_roundtrip(display); + + DBusConnection *conn; + DBusError err; + DBusObjectPathVTable vtable; + int ret; + + dbus_error_init(&err); + conn = dbus_bus_get(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "DBus connection error: %s\n", err.message); + dbus_error_free(&err); + } + if (conn == NULL) { + fprintf(stderr, "DBus connection failed.\n"); + exit(EXIT_FAILURE); + } + + ret = dbus_bus_request_name(conn, "org.freedesktop.ScreenSaver", + DBUS_NAME_FLAG_REPLACE_EXISTING, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "DBus name error: %s\n", err.message); + dbus_error_free(&err); + } + if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + fprintf(stderr, "DBus: Failed to become primary owner of " + "org.freedesktop.ScreenSaver\n"); + exit(EXIT_FAILURE); + } + + vtable.message_function = handle_message; + vtable.unregister_function = NULL; + + dbus_connection_try_register_object_path(conn, "/org/freedesktop/ScreenSaver", + &vtable, NULL, &err); + if (dbus_error_is_set(&err)) { + fprintf(stderr, "DBus object error: %s\n", err.message); + dbus_error_free(&err); + exit(EXIT_FAILURE); + } + + wl_list_init(&inhibitor_list); + + // TODO: wl_display_dispatch + while (true) { + dbus_connection_read_write_dispatch(conn, 1000); + } + + return EXIT_SUCCESS; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..21efd00 --- /dev/null +++ b/meson.build @@ -0,0 +1,36 @@ +project( + 'wlr-idle-proxy', + 'c', + version: '1.6', + license: 'WTFPL', + meson_version: '>=0.50.0', + default_options: [ + 'c_std=c11', + ], +) + +cc = meson.get_compiler('c') + +dbus = dependency('dbus-1') +wayland_client = dependency('wayland-client') +wayland_protos = dependency('wayland-protocols', version: '>=1.21') + +subdir('protocol') + +src_files = [ + 'main.c', +] + +executable( + 'wlr-idle-proxy', + files(src_files), + dependencies: [ + client_protos, + dbus, + wayland_client, + ], + install: true, +) + +conf_data = configuration_data() +conf_data.set('bindir', join_paths(get_option('prefix'), get_option('bindir'))) diff --git a/protocol/meson.build b/protocol/meson.build new file mode 100644 index 0000000..9ac6c07 --- /dev/null +++ b/protocol/meson.build @@ -0,0 +1,46 @@ +wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') + +wayland_scanner = find_program('wayland-scanner', native: true) + +# should check wayland_scanner's version, but it is hard to get +if wayland_client.version().version_compare('>=1.14.91') + code_type = 'private-code' +else + code_type = 'code' +endif + +wayland_scanner_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: [code_type, '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_client = generator( + wayland_scanner, + output: '@BASENAME@-client-protocol.h', + arguments: ['client-header', '@INPUT@', '@OUTPUT@'], +) + +client_protocols = [ + [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], +] + +client_protos_src = [] +client_protos_headers = [] + +foreach p : client_protocols + xml = join_paths(p) + client_protos_src += wayland_scanner_code.process(xml) + client_protos_headers += wayland_scanner_client.process(xml) +endforeach + +lib_client_protos = static_library( + 'client_protos', + client_protos_src + client_protos_headers, + dependencies: [wayland_client] +) # for the include directory + +client_protos = declare_dependency( + link_with: lib_client_protos, + sources: client_protos_headers, +) diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..0be7253 --- /dev/null +++ b/shell.nix @@ -0,0 +1,5 @@ +{ pkgs ? import <nixpkgs> {} }: + pkgs.mkShell { + # nativeBuildInputs is usually what you want -- tools you need to run + nativeBuildInputs = with pkgs.buildPackages; [ meson ninja pkg-config wayland wayland-protocols wlroots dbus ]; +} |