1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <wayland-server-core.h>
5 #include <wlr/types/wlr_idle.h>
6 #include <wlr/util/log.h>
7 #include "idle-protocol.h"
8 #include "util/signal.h"
9 
10 static const struct org_kde_kwin_idle_timeout_interface idle_timeout_impl;
11 
idle_timeout_from_resource(struct wl_resource * resource)12 static struct wlr_idle_timeout *idle_timeout_from_resource(
13 		struct wl_resource *resource) {
14 	assert(wl_resource_instance_of(resource,
15 		&org_kde_kwin_idle_timeout_interface, &idle_timeout_impl));
16 	return wl_resource_get_user_data(resource);
17 }
18 
idle_notify(void * data)19 static int idle_notify(void *data) {
20 	struct wlr_idle_timeout *timer = data;
21 	if (timer->idle_state) {
22 		return 0;
23 	}
24 	timer->idle_state = true;
25 	wlr_signal_emit_safe(&timer->events.idle, timer);
26 
27 	if (timer->resource) {
28 		org_kde_kwin_idle_timeout_send_idle(timer->resource);
29 	}
30 	return 1;
31 }
32 
handle_activity(struct wlr_idle_timeout * timer)33 static void handle_activity(struct wlr_idle_timeout *timer) {
34 	if (!timer->enabled) {
35 		return;
36 	}
37 
38 	// in case the previous state was sleeping send a resume event and switch state
39 	if (timer->idle_state) {
40 		timer->idle_state = false;
41 		wlr_signal_emit_safe(&timer->events.resume, timer);
42 
43 		if (timer->resource) {
44 			org_kde_kwin_idle_timeout_send_resumed(timer->resource);
45 		}
46 	}
47 
48 	// rearm the timer
49 	wl_event_source_timer_update(timer->idle_source, timer->timeout);
50 	if (timer->timeout == 0) {
51 		idle_notify(timer);
52 	}
53 }
54 
handle_timer_resource_destroy(struct wl_resource * timer_resource)55 static void handle_timer_resource_destroy(struct wl_resource *timer_resource) {
56 	struct wlr_idle_timeout *timer = idle_timeout_from_resource(timer_resource);
57 	if (timer != NULL) {
58 		wlr_idle_timeout_destroy(timer);
59 	}
60 }
61 
handle_seat_destroy(struct wl_listener * listener,void * data)62 static void handle_seat_destroy(struct wl_listener *listener, void *data) {
63 	struct wlr_idle_timeout *timer = wl_container_of(listener, timer, seat_destroy);
64 	if (timer != NULL) {
65 		wlr_idle_timeout_destroy(timer);
66 	}
67 }
68 
release_idle_timeout(struct wl_client * client,struct wl_resource * resource)69 static void release_idle_timeout(struct wl_client *client,
70 		struct wl_resource *resource){
71 	handle_timer_resource_destroy(resource);
72 }
73 
simulate_activity(struct wl_client * client,struct wl_resource * resource)74 static void simulate_activity(struct wl_client *client,
75 		struct wl_resource *resource){
76 	struct wlr_idle_timeout *timer = idle_timeout_from_resource(resource);
77 	handle_activity(timer);
78 }
79 
80 static const struct org_kde_kwin_idle_timeout_interface idle_timeout_impl = {
81 	.release = release_idle_timeout,
82 	.simulate_user_activity = simulate_activity,
83 };
84 
85 static const struct org_kde_kwin_idle_interface idle_impl;
86 
idle_from_resource(struct wl_resource * resource)87 static struct wlr_idle *idle_from_resource(
88 		struct wl_resource *resource) {
89 	assert(wl_resource_instance_of(resource, &org_kde_kwin_idle_interface,
90 		&idle_impl));
91 	return wl_resource_get_user_data(resource);
92 }
93 
handle_input_notification(struct wl_listener * listener,void * data)94 static void handle_input_notification(struct wl_listener *listener, void *data) {
95 	struct wlr_idle_timeout *timer =
96 		wl_container_of(listener, timer, input_listener);
97 	struct wlr_seat *seat = data;
98 	if (timer->seat == seat) {
99 		handle_activity(timer);
100 	}
101 }
102 
create_timer(struct wlr_idle * idle,struct wlr_seat * seat,uint32_t timeout,struct wl_resource * resource)103 static struct wlr_idle_timeout *create_timer(struct wlr_idle *idle,
104 		struct wlr_seat *seat, uint32_t timeout, struct wl_resource *resource) {
105 	struct wlr_idle_timeout *timer =
106 		calloc(1, sizeof(struct wlr_idle_timeout));
107 	if (!timer) {
108 		return NULL;
109 	}
110 
111 	timer->seat = seat;
112 	timer->timeout = timeout;
113 	timer->idle_state = false;
114 	timer->enabled = idle->enabled;
115 
116 	wl_list_insert(&idle->idle_timers, &timer->link);
117 	wl_signal_init(&timer->events.idle);
118 	wl_signal_init(&timer->events.resume);
119 	wl_signal_init(&timer->events.destroy);
120 
121 	timer->seat_destroy.notify = handle_seat_destroy;
122 	wl_signal_add(&timer->seat->events.destroy, &timer->seat_destroy);
123 
124 	timer->input_listener.notify = handle_input_notification;
125 	wl_signal_add(&idle->events.activity_notify, &timer->input_listener);
126 	// create the timer
127 	timer->idle_source =
128 		wl_event_loop_add_timer(idle->event_loop, idle_notify, timer);
129 	if (timer->idle_source == NULL) {
130 		wl_list_remove(&timer->link);
131 		wl_list_remove(&timer->input_listener.link);
132 		wl_list_remove(&timer->seat_destroy.link);
133 		free(timer);
134 		return NULL;
135 	}
136 
137 	if (resource) {
138 		timer->resource = resource;
139 		wl_resource_set_user_data(resource, timer);
140 	}
141 
142 	if (timer->enabled) {
143 		// arm the timer
144 		wl_event_source_timer_update(timer->idle_source, timer->timeout);
145 		if (timer->timeout == 0) {
146 			idle_notify(timer);
147 		}
148 	}
149 
150 	return timer;
151 }
152 
create_idle_timer(struct wl_client * client,struct wl_resource * idle_resource,uint32_t id,struct wl_resource * seat_resource,uint32_t timeout)153 static void create_idle_timer(struct wl_client *client,
154 		struct wl_resource *idle_resource, uint32_t id,
155 		struct wl_resource *seat_resource, uint32_t timeout) {
156 	struct wlr_idle *idle = idle_from_resource(idle_resource);
157 	struct wlr_seat_client *client_seat =
158 		wlr_seat_client_from_resource(seat_resource);
159 
160 	struct wl_resource *resource = wl_resource_create(client,
161 		&org_kde_kwin_idle_timeout_interface,
162 		wl_resource_get_version(idle_resource), id);
163 	if (resource == NULL) {
164 		wl_resource_post_no_memory(idle_resource);
165 		return;
166 	}
167 	wl_resource_set_implementation(resource, &idle_timeout_impl,
168 		NULL, handle_timer_resource_destroy);
169 
170 	if (!create_timer(idle, client_seat->seat, timeout, resource)) {
171 		wl_resource_post_no_memory(resource);
172 	}
173 }
174 
175 static const struct org_kde_kwin_idle_interface idle_impl = {
176 	.get_idle_timeout = create_idle_timer,
177 };
178 
wlr_idle_set_enabled(struct wlr_idle * idle,struct wlr_seat * seat,bool enabled)179 void wlr_idle_set_enabled(struct wlr_idle *idle, struct wlr_seat *seat,
180 		bool enabled) {
181 	if (idle->enabled == enabled) {
182 		return;
183 	}
184 	wlr_log(WLR_DEBUG, "%s idle timers for %s",
185 		enabled ? "Enabling" : "Disabling",
186 		seat ? seat->name : "all seats");
187 	idle->enabled = enabled;
188 	struct wlr_idle_timeout *timer;
189 	wl_list_for_each(timer, &idle->idle_timers, link) {
190 		if (seat != NULL && timer->seat != seat) {
191 			continue;
192 		}
193 		int timeout = enabled ? timer->timeout : 0;
194 		wl_event_source_timer_update(timer->idle_source, timeout);
195 		timer->enabled = enabled;
196 	}
197 }
198 
idle_bind(struct wl_client * wl_client,void * data,uint32_t version,uint32_t id)199 static void idle_bind(struct wl_client *wl_client, void *data,
200 		uint32_t version, uint32_t id) {
201 	struct wlr_idle *idle = data;
202 	assert(wl_client && idle);
203 
204 	struct wl_resource *wl_resource = wl_resource_create(wl_client,
205 		&org_kde_kwin_idle_interface, version, id);
206 	if (wl_resource == NULL) {
207 		wl_client_post_no_memory(wl_client);
208 		return;
209 	}
210 	wl_resource_set_implementation(wl_resource, &idle_impl, idle, NULL);
211 }
212 
handle_display_destroy(struct wl_listener * listener,void * data)213 static void handle_display_destroy(struct wl_listener *listener, void *data) {
214 	struct wlr_idle *idle = wl_container_of(listener, idle, display_destroy);
215 	wlr_signal_emit_safe(&idle->events.destroy, idle);
216 	wl_list_remove(&idle->display_destroy.link);
217 	wl_global_destroy(idle->global);
218 	free(idle);
219 }
220 
wlr_idle_create(struct wl_display * display)221 struct wlr_idle *wlr_idle_create(struct wl_display *display) {
222 	struct wlr_idle *idle = calloc(1, sizeof(struct wlr_idle));
223 	if (!idle) {
224 		return NULL;
225 	}
226 	wl_list_init(&idle->idle_timers);
227 	wl_signal_init(&idle->events.activity_notify);
228 	wl_signal_init(&idle->events.destroy);
229 	idle->enabled = true;
230 
231 	idle->event_loop = wl_display_get_event_loop(display);
232 	if (idle->event_loop == NULL) {
233 		free(idle);
234 		return NULL;
235 	}
236 
237 	idle->display_destroy.notify = handle_display_destroy;
238 	wl_display_add_destroy_listener(display, &idle->display_destroy);
239 
240 	idle->global = wl_global_create(display, &org_kde_kwin_idle_interface,
241 		1, idle, idle_bind);
242 	if (idle->global == NULL) {
243 		wl_list_remove(&idle->display_destroy.link);
244 		free(idle);
245 		return NULL;
246 	}
247 	wlr_log(WLR_DEBUG, "idle manager created");
248 	return idle;
249 }
250 
wlr_idle_notify_activity(struct wlr_idle * idle,struct wlr_seat * seat)251 void wlr_idle_notify_activity(struct wlr_idle *idle, struct wlr_seat *seat) {
252 	wlr_signal_emit_safe(&idle->events.activity_notify, seat);
253 }
254 
wlr_idle_timeout_create(struct wlr_idle * idle,struct wlr_seat * seat,uint32_t timeout)255 struct wlr_idle_timeout *wlr_idle_timeout_create(struct wlr_idle *idle,
256 		struct wlr_seat *seat, uint32_t timeout) {
257 	return create_timer(idle, seat, timeout, NULL);
258 }
259 
wlr_idle_timeout_destroy(struct wlr_idle_timeout * timer)260 void wlr_idle_timeout_destroy(struct wlr_idle_timeout *timer) {
261 	wlr_signal_emit_safe(&timer->events.destroy, NULL);
262 
263 	wl_list_remove(&timer->input_listener.link);
264 	wl_list_remove(&timer->seat_destroy.link);
265 	wl_event_source_remove(timer->idle_source);
266 	wl_list_remove(&timer->link);
267 
268 	if (timer->resource) {
269 		wl_resource_set_user_data(timer->resource, NULL);
270 	}
271 
272 	free(timer);
273 }
274