λ

swayaway

a drop-in replacement for swayidle(1)

settings

wayland.c

4275 bytes raw

#include "swayaway.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-client.h>
#include "ext-idle-notify-v1-client-protocol.h"

struct seat_entry {
	struct seat_entry *next;
	struct wl_seat *proxy;
	char *name;
};

static struct seat_entry *seats;
static struct ext_idle_notification_v1 *tracking;

/* One tracking notification with timeout 0 is enough: the compositor sends
 * "idled" the moment the seat goes idle (a zero timeout means "as soon as
 * possible") and "resumed" when activity starts again, then restarts the
 * timer.  All real timeout bookkeeping happens in idle.c against
 * CLOCK_MONOTONIC, so one protocol object per timeout is not needed. */

static void handle_idled(void *data, struct ext_idle_notification_v1 *notif)
{
	(void)data;
	(void)notif;
	idle_on_idled();
}

static void handle_resumed(void *data, struct ext_idle_notification_v1 *notif)
{
	(void)data;
	(void)notif;
	idle_on_resumed();
}

static const struct ext_idle_notification_v1_listener tracking_listener = {
	.idled = handle_idled,
	.resumed = handle_resumed,
};

static void seat_handle_capabilities(void *data, struct wl_seat *seat,
                                     uint32_t capabilities)
{
	(void)data;
	(void)seat;
	(void)capabilities;
}

static void seat_handle_name(void *data, struct wl_seat *seat,
                             const char *name)
{
	struct seat_entry *entry = data;
	(void)seat;
	if (name) {
		entry->name = strdup(name);
	}
}

static const struct wl_seat_listener seat_listener = {
	.capabilities = seat_handle_capabilities,
	.name = seat_handle_name,
};

static void handle_global(void *data, struct wl_registry *registry,
                          uint32_t name, const char *interface, uint32_t version)
{
	(void)data;
	if (strcmp(interface, ext_idle_notifier_v1_interface.name) == 0) {
		state.notifier = wl_registry_bind(registry, name,
		                                  &ext_idle_notifier_v1_interface, 1);
		sway_log_debug("bound ext_idle_notifier_v1 (version %u)", version);
	} else if (strcmp(interface, wl_seat_interface.name) == 0) {
		struct seat_entry *entry = calloc(1, sizeof(*entry));
		if (!entry) {
			sway_log("out of memory binding seat");
			return;
		}
		entry->proxy = wl_registry_bind(registry, name, &wl_seat_interface, 2);
		wl_seat_add_listener(entry->proxy, &seat_listener, entry);
		entry->next = seats;
		seats = entry;
		sway_log_debug("bound wl_seat");
	}
}

static void handle_global_remove(void *data, struct wl_registry *registry,
                                 uint32_t name)
{
	(void)data;
	(void)registry;
	(void)name;
}

static const struct wl_registry_listener registry_listener = {
	.global = handle_global,
	.global_remove = handle_global_remove,
};

int wayland_init(void)
{
	state.display = wl_display_connect(NULL);
	if (state.display == NULL) {
		sway_log("unable to connect to the compositor. "
		         "Check or set WAYLAND_DISPLAY.");
		return -1;
	}

	struct wl_registry *registry = wl_display_get_registry(state.display);
	wl_registry_add_listener(registry, &registry_listener, NULL);
	// roundtrip twice: once for globals, once for the seat name
	wl_display_roundtrip(state.display);
	wl_display_roundtrip(state.display);

	if (state.notifier == NULL) {
		sway_log("compositor does not support ext-idle-notify-v1");
		return -1;
	}

	struct seat_entry *entry;
	for (entry = seats; entry; entry = entry->next) {
		if (state.seat_name == NULL ||
		        (entry->name && strcmp(entry->name, state.seat_name) == 0)) {
			state.seat = entry->proxy;
			break;
		}
	}

	if (state.seat == NULL) {
		if (state.seat_name) {
			sway_log("seat '%s' not found", state.seat_name);
		} else {
			sway_log("no seat found");
		}
		return -1;
	}
	sway_log_debug("using seat '%s'",
	               entry->name ? entry->name : "?");
	return 0;
}

void wayland_create_tracking_notification(void)
{
	if (state.notifier == NULL || state.seat == NULL || tracking != NULL) {
		return;
	}
	tracking = ext_idle_notifier_v1_get_idle_notification(
	               state.notifier, 0, state.seat);
	ext_idle_notification_v1_add_listener(tracking, &tracking_listener, NULL);
	sway_log_debug("created tracking idle notification (timeout 0)");
	wl_display_flush(state.display);
}