feat(wayland): add idle inhibitor for wayland

This commit is contained in:
2025-09-21 23:46:57 -04:00
parent 6e075bff4a
commit ee2df36041
5 changed files with 301 additions and 2 deletions

17
include/wayland.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <stdbool.h>
/**
* @brief create the wayland thread
*
* @return 0 on success
*/
int wayland_create_thread(void);
/**
* @brief set the idle lock
*
* @param lock change the idle lock state
*/
void wayland_set_idle_lock(int lock);

View File

@@ -7,16 +7,51 @@ add_project_arguments([
srcfiles = files(
'src/main.c',
'src/led.c',
'src/wayland.c',
'src/acpi.c',
'src/utils.c'
)
# wayland stuff
wscanner = dependency('wayland-scanner', native: true)
wscanner_prog = find_program(
wscanner.get_variable('wayland_scanner'), native: true)
wayland_protocols = dependency('wayland-protocols', version: '>=1.41',
default_options: ['tests=false'])
wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir')
wayland_client = dependency('wayland-client')
wayland_server = dependency('wayland-server')
wl_proto_c = []
wl_proto_h = []
wl_proto_xml = [
wayland_protocols_datadir / 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'
]
foreach prot : wl_proto_xml
wl_proto_h += custom_target(
prot.underscorify() + '-client-header',
input: prot,
output: '@BASENAME@.h',
command: [ wscanner_prog, 'client-header', '@INPUT@', '@OUTPUT@' ])
wl_proto_c += custom_target(
prot.underscorify() + '-private-code',
input: prot,
output: '@BASENAME@.c',
command: [ wscanner_prog, 'private-code', '@INPUT@', '@OUTPUT@' ])
endforeach
# build stuff
cc = meson.get_compiler('c')
math_dep = cc.find_library('m', required: true)
executable('eh', srcfiles,
wl_proto_c + wl_proto_h,
dependencies: [
math_dep
math_dep,
wayland_client,
wayland_server
],
include_directories: [
'lib/log.c/src',

View File

@@ -23,7 +23,7 @@ struct Data {
/* global data accessed by all threads */
static struct Data *data;
pthread_t thread;
static pthread_t thread;
/* mutex to lock the thread */
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;

View File

@@ -14,6 +14,7 @@
#include "log.h"
#include "led.h"
#include "wayland.h"
#include "acpi.h"
#include "utils.h"
@@ -86,8 +87,11 @@ on_battery_event(char *percent, bool plugged)
} else {
led_set_rate(0);
}
wayland_set_idle_lock(false);
} else {
led_set_rate(0);
wayland_set_idle_lock(true);
}
free(percent);
@@ -109,6 +113,9 @@ main(int argc, char *argv[])
/* create the led thread */
led_create_thread((char *)ledfile);
/* create the wayland thread */
wayland_create_thread();
/* run events that need to be run on start to ensure that the current state
* inside the program reflects that that is outside the program
*/

240
src/wayland.c Normal file
View File

@@ -0,0 +1,240 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-server.h>
#include "idle-inhibit-unstable-v1.h"
#include "wayland.h"
#include "log.h"
struct Data {
int idle_lock;
struct wl_compositor *compositor;
struct wl_display *display;
struct wl_registry *registry;
struct wl_surface *surface;
struct zwp_idle_inhibitor_v1 *inhibitor;
struct zwp_idle_inhibit_manager_v1 *inhibitmanager;
};
/* global data accessed by all threads */
static struct Data *data;
static pthread_t thread;
/* mutex to lock the thread */
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static void
reghandler(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
struct Data *d = (struct Data *)data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
d->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
} else if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) {
d->inhibitmanager = wl_registry_bind(registry, id, &zwp_idle_inhibit_manager_v1_interface, 1);
}
}
static const struct wl_registry_listener reglistener = {
.global = reghandler,
};
/**
* @brief helper function to set the idle lock
*
* @param value set the state of the idle lock
* @param d data
*/
static inline void
idle_lock_set(int lock, struct Data *d)
{
if (lock) {
d->inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(d->inhibitmanager, d->surface);
} else if (d->inhibitor) {
zwp_idle_inhibitor_v1_destroy(d->inhibitor);
d->inhibitor = NULL;
}
wl_display_roundtrip(d->display);
}
/**
* @brief main thread loop
*
* @param args args
*/
static void
*run(void *args)
{
int err;
struct Data *d = (struct Data *)args;
for (;;) {
/* wait for the condition to change */
while (true) {
if ((err = pthread_cond_wait(&cv, &mp)) != 0) {
log_fatal("pthread_cond_wait: %s", strerror(err));
pthread_exit(0);
} else {
break;
}
}
/* exit if we're told to */
if (d->idle_lock == -1) {
log_debug("received a null idle lock, exiting");
pthread_exit(0);
}
idle_lock_set(d->idle_lock, d);
}
pthread_exit(0);
}
/**
* @brief run before exiting the program
* this is for cleaning up all the messes we've made
*
* @param signal signal
*/
static void
pre_exit(int signal)
{
idle_lock_set(false, data);
wayland_set_idle_lock(-1);
pthread_cond_destroy(&cv);
pthread_mutex_destroy(&mp);
zwp_idle_inhibit_manager_v1_destroy(data->inhibitmanager);
wl_registry_destroy(data->registry);
wl_compositor_destroy(data->compositor);
wl_surface_destroy(data->surface);
/* TODO: this causes a segfault
wl_display_destroy(data->display);
*/
/* make sure to cleanup the allocated data */
free(data);
exit(0);
}
int
wayland_create_thread(void)
{
int err;
struct sigaction act;
static bool running;
/* make sure the wayland thread cannot be created twice */
if (running) {
log_warn("wayland thread has already been created");
return 1;
}
/* setup data */
data = calloc(1, sizeof(struct Data));
/* connect to wayland */
data->display = wl_display_connect(NULL);
if (!data->display) {
log_fatal("failed to connect to wayland display");
return -1;
}
data->registry = wl_display_get_registry(data->display);
if (wl_registry_add_listener(data->registry, &reglistener, data) < 0) {
log_fatal("failed to connect to the wayland registry");
return -1;
}
/* roundtrip so that the registry listener collects all the data */
wl_display_roundtrip(data->display);
if (!data->compositor) {
log_fatal("wayland needs a compositor to run");
return -1;
}
if (!data->inhibitmanager) {
log_fatal("compositor doesn't support the idle inhibit protocol");
return -1;
}
data->surface = wl_compositor_create_surface(data->compositor);
if (!data->surface) {
log_fatal("wayland couldn't create a surface");
return -1;
}
data->idle_lock = false;
data->inhibitor = NULL;
/* zero the array, and setup sig handler and exit handler */
memset(&act, 0, sizeof(act));
act.sa_handler = &pre_exit;
if (sigaction(SIGINT, &act, NULL) < 0) {
log_fatal("failed to create sigaction: %s, memory is gonna leak",
strerror(errno));
}
/* attempt to create the thread */
if ((err = pthread_create(&thread, NULL, run, data)) != 0) {
log_fatal("failed to create wayland thread: pthread_create: %s", strerror(err));
return -1;
}
/* detach the thread to make cleanup nice and easy */
if ((err = pthread_detach(thread)) != 0) {
log_fatal("failed to create wayland thread: pthread_detach: %s", strerror(err));
return -1;
}
log_info("started wayland thread");
running = true;
return 0;
}
void
wayland_set_idle_lock(int lock)
{
int err;
static bool locked = false;
/* there's no reason to set the lock if it's already the same */
if (lock == data->idle_lock) {
return;
}
/* lock the thread, update, and unlock it */
if (!locked) {
if ((err = pthread_mutex_lock(&mp)) != 0) {
log_fatal("failed to lock the mutex: %s", strerror(errno));
}
}
/* set the idle lock */
data->idle_lock = lock;
log_debug("%s wayland idle lock", lock ? "enabled" : "disabled");
pthread_cond_signal(&cv);
if (!locked) {
if ((err = pthread_mutex_unlock(&mp)) != 0) {
log_fatal("failed to lock the mutex: %s", strerror(errno));
}
}
/* make sure we don't lock the thread in an infinite loop */
locked = (lock != 0);
}