feat(wayland): add idle inhibitor for wayland
This commit is contained in:
17
include/wayland.h
Normal file
17
include/wayland.h
Normal 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);
|
37
meson.build
37
meson.build
@@ -7,16 +7,51 @@ add_project_arguments([
|
|||||||
srcfiles = files(
|
srcfiles = files(
|
||||||
'src/main.c',
|
'src/main.c',
|
||||||
'src/led.c',
|
'src/led.c',
|
||||||
|
'src/wayland.c',
|
||||||
'src/acpi.c',
|
'src/acpi.c',
|
||||||
'src/utils.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')
|
cc = meson.get_compiler('c')
|
||||||
math_dep = cc.find_library('m', required: true)
|
math_dep = cc.find_library('m', required: true)
|
||||||
|
|
||||||
executable('eh', srcfiles,
|
executable('eh', srcfiles,
|
||||||
|
wl_proto_c + wl_proto_h,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
math_dep
|
math_dep,
|
||||||
|
wayland_client,
|
||||||
|
wayland_server
|
||||||
],
|
],
|
||||||
include_directories: [
|
include_directories: [
|
||||||
'lib/log.c/src',
|
'lib/log.c/src',
|
||||||
|
@@ -23,7 +23,7 @@ struct Data {
|
|||||||
|
|
||||||
/* global data accessed by all threads */
|
/* global data accessed by all threads */
|
||||||
static struct Data *data;
|
static struct Data *data;
|
||||||
pthread_t thread;
|
static pthread_t thread;
|
||||||
|
|
||||||
/* mutex to lock the thread */
|
/* mutex to lock the thread */
|
||||||
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
|
#include "wayland.h"
|
||||||
#include "acpi.h"
|
#include "acpi.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
@@ -86,8 +87,11 @@ on_battery_event(char *percent, bool plugged)
|
|||||||
} else {
|
} else {
|
||||||
led_set_rate(0);
|
led_set_rate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wayland_set_idle_lock(false);
|
||||||
} else {
|
} else {
|
||||||
led_set_rate(0);
|
led_set_rate(0);
|
||||||
|
wayland_set_idle_lock(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(percent);
|
free(percent);
|
||||||
@@ -109,6 +113,9 @@ main(int argc, char *argv[])
|
|||||||
/* create the led thread */
|
/* create the led thread */
|
||||||
led_create_thread((char *)ledfile);
|
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
|
/* 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
|
* inside the program reflects that that is outside the program
|
||||||
*/
|
*/
|
||||||
|
240
src/wayland.c
Normal file
240
src/wayland.c
Normal 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, ®listener, 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);
|
||||||
|
}
|
Reference in New Issue
Block a user