Wayland: Add support for idle.

When the user doesn't have a seat, or their compositor doesn't support the idle
protocol, we'll assume that they are not idle.

Also, rename wl_output to something that doesn't exist yet.
This commit is contained in:
fwsmit 2020-11-25 22:33:04 +01:00
parent 74c5d97181
commit 859c18a2e9
6 changed files with 127 additions and 28 deletions

View File

@ -141,6 +141,8 @@ wayland-protocols: src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
wayland-scanner private-code ${DATA_DIR_WAYLAND_PROTOCOLS}/unstable/xdg-output/xdg-output-unstable-v1.xml src/wayland/protocols/xdg-output-unstable-v1.h wayland-scanner private-code ${DATA_DIR_WAYLAND_PROTOCOLS}/unstable/xdg-output/xdg-output-unstable-v1.xml src/wayland/protocols/xdg-output-unstable-v1.h
wayland-scanner client-header src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1-client-header.h wayland-scanner client-header src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1-client-header.h
wayland-scanner private-code src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1.h wayland-scanner private-code src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1.h
wayland-scanner client-header src/wayland/protocols/idle.xml src/wayland/protocols/idle-client-header.h
wayland-scanner private-code src/wayland/protocols/idle.xml src/wayland/protocols/idle.h
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run .PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run
clean: clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run clean-wayland-protocols clean: clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run clean-wayland-protocols

View File

@ -264,7 +264,7 @@ Set to 0 to disable.
A client can mark a notification as transient to bypass this setting and timeout A client can mark a notification as transient to bypass this setting and timeout
anyway. Use a rule with 'set_transient = no' to disable this behavior. anyway. Use a rule with 'set_transient = no' to disable this behavior.
Note: this doesn't work on wayland yet. Note: this doesn't work on xwayland yet.
=item B<font> (default: "Monospace 8") =item B<font> (default: "Monospace 8")

View File

@ -5,11 +5,16 @@
#include "x11/screen.h" #include "x11/screen.h"
#include "wayland/wl.h" #include "wayland/wl.h"
const bool is_running_wayland(void){ const bool is_running_wayland(void) {
char* wayland_display = getenv("WAYLAND_DISPLAY"); char* wayland_display = getenv("WAYLAND_DISPLAY");
return !(wayland_display == NULL); return !(wayland_display == NULL);
} }
const bool is_running_xwayland(void) {
// FIXME
return false;
}
const struct output output_x11 = { const struct output output_x11 = {
x_setup, x_setup,
x_free, x_free,

View File

@ -144,8 +144,8 @@ static bool queues_notification_is_finished(struct notification *n, struct dunst
bool is_idle = status.fullscreen ? false : status.idle; bool is_idle = status.fullscreen ? false : status.idle;
/* don't timeout when user is idle */ /* don't timeout when user is idle */
/* NOTE: Idle is not working on wayland */ /* NOTE: Idle is not working on xwayland */
if (is_idle && !n->transient && !is_running_wayland()) { if (is_idle && !n->transient && !is_running_xwayland()) {
n->start = time_monotonic_now(); n->start = time_monotonic_now();
return false; return false;
} }

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="idle">
<copyright><![CDATA[
Copyright (C) 2015 Martin Gräßlin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]></copyright>
<interface name="org_kde_kwin_idle" version="1">
<description summary="User idle time manager">
This interface allows to monitor user idle time on a given seat. The interface
allows to register timers which trigger after no user activity was registered
on the seat for a given interval. It notifies when user activity resumes.
This is useful for applications wanting to perform actions when the user is not
interacting with the system, e.g. chat applications setting the user as away, power
management features to dim screen, etc..
</description>
<request name="get_idle_timeout">
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
</request>
</interface>
<interface name="org_kde_kwin_idle_timeout" version="1">
<request name="release" type="destructor">
<description summary="release the timeout object"/>
</request>
<request name="simulate_user_activity">
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
</request>
<event name="idle">
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
</event>
<event name="resumed">
<description summary="Triggered on the first user activity after an idle event"/>
</event>
</interface>
</protocol>

View File

@ -20,8 +20,11 @@
#include "protocols/xdg-shell.h" #include "protocols/xdg-shell.h"
#include "protocols/wlr-layer-shell-unstable-v1-client-header.h" #include "protocols/wlr-layer-shell-unstable-v1-client-header.h"
#include "protocols/wlr-layer-shell-unstable-v1.h" #include "protocols/wlr-layer-shell-unstable-v1.h"
#include "protocols/idle-client-header.h"
#include "protocols/idle.h"
#include "pool-buffer.h" #include "pool-buffer.h"
#include "../log.h" #include "../log.h"
#include "../settings.h" #include "../settings.h"
#include "../queues.h" #include "../queues.h"
@ -59,8 +62,11 @@ struct wl_ctx {
struct zwlr_layer_surface_v1 *layer_surface; struct zwlr_layer_surface_v1 *layer_surface;
struct wl_output *layer_surface_output; struct wl_output *layer_surface_output;
struct wl_callback *frame_callback; struct wl_callback *frame_callback;
struct org_kde_kwin_idle *idle_handler;
bool configured; bool configured;
bool dirty; bool dirty;
bool is_idle;
bool has_seat;
struct { struct {
struct wl_pointer *wl_pointer; struct wl_pointer *wl_pointer;
@ -74,7 +80,7 @@ struct wl_ctx {
struct pool_buffer *current_buffer; struct pool_buffer *current_buffer;
}; };
struct wl_output { struct dunst_output {
uint32_t global_name; uint32_t global_name;
char *name; char *name;
struct wl_output *wl_output; struct wl_output *wl_output;
@ -82,6 +88,7 @@ struct wl_output {
struct wl_list link; struct wl_list link;
uint32_t scale; uint32_t scale;
uint32_t subpixel; // TODO do something with it
}; };
@ -95,7 +102,7 @@ static void noop() {
void set_dirty(); void set_dirty();
static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output,
const char *name) { const char *name) {
struct wl_output *output = data; struct dunst_output *output = data;
output->name = g_strdup(name); output->name = g_strdup(name);
} }
@ -107,7 +114,7 @@ static const struct zxdg_output_v1_listener xdg_output_listener = {
.description = noop, .description = noop,
}; };
static void get_xdg_output(struct wl_output *output) { static void get_xdg_output(struct dunst_output *output) {
if (ctx.xdg_output_manager == NULL || if (ctx.xdg_output_manager == NULL ||
output->xdg_output != NULL) { output->xdg_output != NULL) {
return; return;
@ -124,13 +131,13 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t subpixel, const char *make, const char *model, int32_t subpixel, const char *make, const char *model,
int32_t transform) { int32_t transform) {
//TODO //TODO
/* struct wl_output *output = data; */ struct dunst_output *output = data;
/* output->subpixel = subpixel; */ output->subpixel = subpixel;
} }
static void output_handle_scale(void *data, struct wl_output *wl_output, static void output_handle_scale(void *data, struct wl_output *wl_output,
int32_t factor) { int32_t factor) {
struct wl_output *output = data; struct dunst_output *output = data;
output->scale = factor; output->scale = factor;
} }
@ -142,27 +149,30 @@ static const struct wl_output_listener output_listener = {
}; };
static void create_output( struct wl_output *wl_output, uint32_t global_name) { static void create_output( struct wl_output *wl_output, uint32_t global_name) {
struct wl_output *output = g_malloc0(sizeof(struct wl_output)); struct dunst_output *output = g_malloc0(sizeof(struct dunst_output));
if (output == NULL) { if (output == NULL) {
fprintf(stderr, "allocation failed\n"); fprintf(stderr, "allocation failed\n");
return; return;
} }
static int number = 0;
LOG_I("New output %i - id %i", global_name, number);
output->global_name = global_name; output->global_name = global_name;
output->wl_output = wl_output; output->wl_output = wl_output;
// TODO: Fix this // TODO: Fix this
//output->scale = 1; output->scale = 1;
//wl_list_insert(&state->outputs, &output->link); wl_list_insert(&ctx.outputs, &output->link);
wl_output_set_user_data(wl_output, output); wl_output_set_user_data(wl_output, output);
wl_output_add_listener(wl_output, &output_listener, output); wl_output_add_listener(wl_output, &output_listener, output);
get_xdg_output(output); get_xdg_output(output);
number++;
} }
static void destroy_output(struct wl_output *output) { static void destroy_output(struct dunst_output *output) {
if (ctx.surface_output == output) { if (ctx.surface_output == output->wl_output) {
ctx.surface_output = NULL; ctx.surface_output = NULL;
} }
if (ctx.layer_surface_output == output) { if (ctx.layer_surface_output == output->wl_output) {
ctx.layer_surface_output = NULL; ctx.layer_surface_output = NULL;
} }
wl_list_remove(&output->link); wl_list_remove(&output->link);
@ -336,6 +346,26 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
}; };
static void idle_start (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout){
ctx.is_idle = true;
LOG_I("User went idle");
}
static void idle_stop (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
ctx.is_idle = false;
LOG_I("User isn't idle anymore");
}
static const struct org_kde_kwin_idle_timeout_listener idle_timeout_listener = {
.idle = idle_start,
.resumed = idle_stop,
};
static void add_seat_to_idle_handler(struct wl_seat *seat){
uint32_t timeout_ms = settings.idle_threshold/1000;
struct org_kde_kwin_idle_timeout *idle_timeout = org_kde_kwin_idle_get_idle_timeout(ctx.idle_handler, ctx.seat, timeout_ms);
org_kde_kwin_idle_timeout_add_listener(idle_timeout, &idle_timeout_listener, 0);
}
static void handle_global(void *data, struct wl_registry *registry, static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) { uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_compositor_interface.name) == 0) { if (strcmp(interface, wl_compositor_interface.name) == 0) {
@ -350,6 +380,8 @@ static void handle_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, wl_seat_interface.name) == 0) { } else if (strcmp(interface, wl_seat_interface.name) == 0) {
ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3); ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat); wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat);
add_seat_to_idle_handler(ctx.seat);
ctx.has_seat = true;
} else if (strcmp(interface, wl_output_interface.name) == 0) { } else if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output = struct wl_output *output =
wl_registry_bind(registry, name, &wl_output_interface, 3); wl_registry_bind(registry, name, &wl_output_interface, 3);
@ -359,12 +391,15 @@ static void handle_global(void *data, struct wl_registry *registry,
ctx.xdg_output_manager = wl_registry_bind(registry, name, ctx.xdg_output_manager = wl_registry_bind(registry, name,
&zxdg_output_manager_v1_interface, &zxdg_output_manager_v1_interface,
ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); ZXDG_OUTPUT_V1_NAME_SINCE_VERSION);
} else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
} }
} }
static void handle_global_remove(void *data, struct wl_registry *registry, static void handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) { uint32_t name) {
struct wl_output *output, *tmp; struct dunst_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &ctx.outputs, link) { wl_list_for_each_safe(output, tmp, &ctx.outputs, link) {
if (output->global_name == name) { if (output->global_name == name) {
destroy_output(output); destroy_output(output);
@ -407,7 +442,7 @@ bool init_wayland() {
} }
if (ctx.xdg_output_manager != NULL) { if (ctx.xdg_output_manager != NULL) {
struct wl_output *output; struct dunst_output *output;
wl_list_for_each(output, &ctx.outputs, link) { wl_list_for_each(output, &ctx.outputs, link) {
get_xdg_output(output); get_xdg_output(output);
} }
@ -432,7 +467,7 @@ void finish_wayland() {
finish_buffer(&ctx.buffers[0]); finish_buffer(&ctx.buffers[0]);
finish_buffer(&ctx.buffers[1]); finish_buffer(&ctx.buffers[1]);
struct wl_output *output, *output_tmp; struct dunst_output *output, *output_tmp;
wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) { wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) {
destroy_output(output); destroy_output(output);
} }
@ -449,9 +484,9 @@ void finish_wayland() {
wl_display_disconnect(ctx.display); wl_display_disconnect(ctx.display);
} }
static struct wl_output *get_configured_output() { static struct dunst_output *get_configured_output() {
struct wl_output *output; struct dunst_output *output;
// FIXME // FIXME Make sure the returned output corresponds to the monitor number configured in the dunstrc
wl_list_for_each(output, &ctx.outputs, link) { wl_list_for_each(output, &ctx.outputs, link) {
return output; return output;
} }
@ -465,12 +500,12 @@ static void schedule_frame_and_commit();
static void send_frame() { static void send_frame() {
int scale = 1; int scale = 1;
struct wl_output *output = get_configured_output(); struct dunst_output *output = get_configured_output();
int height = ctx.cur_dim.h; int height = ctx.cur_dim.h;
// There are two cases where we want to tear down the surface: zero // There are two cases where we want to tear down the surface: zero
// notifications (height = 0) or moving between outputs. // notifications (height = 0) or moving between outputs.
if (height == 0 || ctx.layer_surface_output != output) { if (height == 0 || ctx.layer_surface_output != output->wl_output) {
if (ctx.layer_surface != NULL) { if (ctx.layer_surface != NULL) {
zwlr_layer_surface_v1_destroy(ctx.layer_surface); zwlr_layer_surface_v1_destroy(ctx.layer_surface);
ctx.layer_surface = NULL; ctx.layer_surface = NULL;
@ -498,10 +533,11 @@ static void send_frame() {
if (ctx.layer_surface == NULL) { if (ctx.layer_surface == NULL) {
struct wl_output *wl_output = NULL; struct wl_output *wl_output = NULL;
if (output != NULL) { if (output != NULL) {
LOG_I("Output is not null");
wl_output = output->wl_output; wl_output = output->wl_output;
} } else
ctx.layer_surface_output = output; LOG_I("output is null");
ctx.layer_surface_output = output->wl_output;
ctx.surface = wl_compositor_create_surface(ctx.compositor); ctx.surface = wl_compositor_create_surface(ctx.compositor);
wl_surface_add_listener(ctx.surface, &surface_listener, NULL); wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
@ -707,7 +743,14 @@ const struct screen_info* wl_get_active_screen(void) {
} }
bool wl_is_idle(void) { bool wl_is_idle(void) {
return false; LOG_I("Idle status queried: %i", ctx.is_idle);
// When the user doesn't have a seat, or their compositor doesn't support the idle
// protocol, we'll assume that they are not idle.
if (settings.idle_threshold == 0 || ctx.has_seat == false || ctx.idle_handler == NULL) {
return false;
} else {
return ctx.is_idle;
}
} }
bool wl_have_fullscreen_window(void) { bool wl_have_fullscreen_window(void) {
return false; return false;