Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-xdp-input"
5 : :
6 : : #include "config.h"
7 : :
8 : : #ifdef __linux__
9 : : # include <linux/input-event-codes.h>
10 : : #else
11 : : # define BTN_LEFT 0x110
12 : : # define BTN_RIGHT 0x111
13 : : # define BTN_MIDDLE 0x112
14 : : #endif /* __linux */
15 : :
16 : : #include <glib-object.h>
17 : : #include <gdk/gdk.h>
18 : : #ifdef GDK_WINDOWING_X11
19 : : # include <gdk/x11/gdkx.h>
20 : : #endif /* GDK_WINDOWING_X11 */
21 : : #include <libportal/portal.h>
22 : : #include <valent.h>
23 : :
24 : : #include "valent-xdp-input.h"
25 : : #include "valent-xdp-utils.h"
26 : :
27 : :
28 : : struct _ValentXdpInput
29 : : {
30 : : ValentInputAdapter parent_instance;
31 : :
32 : : GCancellable *cancellable;
33 : : GSettings *settings;
34 : :
35 : : XdpSession *session;
36 : : int64_t session_expiry;
37 : : guint session_expiry_id;
38 : : gboolean session_starting;
39 : : gboolean started;
40 : : };
41 : :
42 : 0 : G_DEFINE_FINAL_TYPE (ValentXdpInput, valent_xdp_input, VALENT_TYPE_INPUT_ADAPTER)
43 : :
44 : :
45 : : /*
46 : : * Portal Callbacks
47 : : */
48 : : static void
49 : 0 : session_update (ValentXdpInput *self)
50 : : {
51 : 0 : g_autoptr (GDateTime) now = NULL;
52 : 0 : unsigned int timeout = 0;
53 : :
54 : 0 : now = g_date_time_new_now_local ();
55 : 0 : timeout = g_settings_get_uint (self->settings, "xdp-session-timeout");
56 : 0 : self->session_expiry = g_date_time_to_unix (now) + timeout;
57 : 0 : }
58 : :
59 : : static void
60 : 0 : on_session_closed (XdpSession *session,
61 : : gpointer user_data)
62 : : {
63 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (user_data);
64 : :
65 : : /* Mark the session as inactive */
66 : 0 : self->started = FALSE;
67 : 0 : g_clear_handle_id (&self->session_expiry_id, g_source_remove);
68 : 0 : g_clear_object (&self->session);
69 : 0 : }
70 : :
71 : : static gboolean
72 : 0 : on_session_expired (gpointer user_data)
73 : : {
74 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (user_data);
75 : 0 : g_autoptr (GDateTime) now = NULL;
76 : 0 : int remainder;
77 : :
78 : : /* If the session has been used recently, schedule a new expiry */
79 : 0 : now = g_date_time_new_now_local ();
80 : 0 : remainder = self->session_expiry - g_date_time_to_unix (now);
81 : :
82 : 0 : if (remainder > 0)
83 : : {
84 : 0 : self->session_expiry_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
85 : : remainder,
86 : : on_session_expired,
87 : : g_object_ref (self),
88 : : g_object_unref);
89 : :
90 : 0 : return G_SOURCE_REMOVE;
91 : : }
92 : :
93 : : /* Otherwise if there's an active session, close it */
94 : 0 : if (self->session != NULL)
95 : 0 : xdp_session_close (self->session);
96 : :
97 : : // Reset the GSource Id
98 : 0 : self->session_expiry_id = 0;
99 : :
100 : 0 : return G_SOURCE_REMOVE;
101 : : }
102 : :
103 : : static void
104 : 0 : on_session_started (XdpSession *session,
105 : : GAsyncResult *res,
106 : : gpointer user_data)
107 : : {
108 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (user_data);
109 : 0 : g_autoptr (GError) error = NULL;
110 : 0 : unsigned int timeout;
111 : :
112 : 0 : if (!xdp_session_start_finish (session, res, &error))
113 : : {
114 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
115 : 0 : g_clear_object (&self->session);
116 : 0 : self->session_starting = FALSE;
117 : :
118 : 0 : return;
119 : : }
120 : :
121 : 0 : if (!(xdp_session_get_devices (session) & XDP_DEVICE_POINTER) ||
122 : 0 : !(xdp_session_get_devices (session) & XDP_DEVICE_KEYBOARD))
123 : : {
124 : 0 : g_warning ("%s(): failed to get input device", G_STRFUNC);
125 : 0 : g_clear_object (&self->session);
126 : 0 : self->session_starting = FALSE;
127 : :
128 : 0 : return;
129 : : }
130 : :
131 : : /* Set a timeout */
132 : 0 : timeout = g_settings_get_uint (self->settings, "xdp-session-timeout");
133 : :
134 : 0 : if (timeout > 0)
135 : : {
136 : 0 : self->session_expiry_id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
137 : : timeout,
138 : : on_session_expired,
139 : : g_object_ref (self),
140 : : g_object_unref);
141 : 0 : session_update (self);
142 : : }
143 : :
144 : 0 : self->session_starting = FALSE;
145 : 0 : self->started = TRUE;
146 : : }
147 : :
148 : : static void
149 : 0 : on_session_created (XdpPortal *portal,
150 : : GAsyncResult *result,
151 : : gpointer user_data)
152 : : {
153 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (user_data);
154 : 0 : g_autoptr (XdpParent) parent = NULL;
155 : 0 : g_autoptr (GError) error = NULL;
156 : :
157 : 0 : self->session = xdp_portal_create_remote_desktop_session_finish (portal,
158 : : result,
159 : : &error);
160 : :
161 : 0 : if (self->session == NULL)
162 : : {
163 : 0 : g_warning ("%s(): %s", G_STRFUNC, error->message);
164 : 0 : self->session_starting = FALSE;
165 : :
166 : 0 : return;
167 : : }
168 : :
169 : 0 : g_signal_connect_object (self->session,
170 : : "closed",
171 : : G_CALLBACK (on_session_closed),
172 : : self, 0);
173 : :
174 : 0 : parent = valent_xdp_get_parent ();
175 : 0 : xdp_session_start (self->session,
176 : : parent,
177 : : self->cancellable,
178 : : (GAsyncReadyCallback)on_session_started,
179 : : self);
180 : : }
181 : :
182 : : static gboolean
183 : 0 : ensure_session (ValentXdpInput *self)
184 : : {
185 : 0 : if G_LIKELY (self->started)
186 : : {
187 : 0 : session_update (self);
188 : 0 : return TRUE;
189 : : }
190 : :
191 : 0 : if (self->session_starting)
192 : : return FALSE;
193 : :
194 : : /* Try to acquire a new session */
195 : 0 : self->session_starting = TRUE;
196 : 0 : xdp_portal_create_remote_desktop_session (valent_xdp_get_default (),
197 : : (XDP_DEVICE_KEYBOARD |
198 : : XDP_DEVICE_POINTER),
199 : : XDP_OUTPUT_MONITOR,
200 : : XDP_REMOTE_DESKTOP_FLAG_NONE,
201 : : XDP_CURSOR_MODE_HIDDEN,
202 : : self->cancellable,
203 : : (GAsyncReadyCallback)on_session_created,
204 : : self);
205 : :
206 : 0 : return FALSE;
207 : : }
208 : :
209 : :
210 : : /*
211 : : * ValentInputAdapter
212 : : */
213 : : static void
214 : 0 : valent_xdp_input_keyboard_keysym (ValentInputAdapter *adapter,
215 : : uint32_t keysym,
216 : : gboolean state)
217 : : {
218 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (adapter);
219 : :
220 : 0 : g_assert (VALENT_IS_INPUT_ADAPTER (adapter));
221 : 0 : g_assert (VALENT_IS_XDP_INPUT (self));
222 : :
223 : 0 : if G_UNLIKELY (!ensure_session (self))
224 : : return;
225 : :
226 : : // TODO: XDP_KEY_PRESSED/XDP_KEY_RELEASED
227 : :
228 : 0 : xdp_session_keyboard_key (self->session, TRUE, keysym, state);
229 : : }
230 : :
231 : : static unsigned int
232 : 0 : translate_to_evdev_button (unsigned int button)
233 : : {
234 : 0 : switch (button)
235 : : {
236 : : case VALENT_POINTER_PRIMARY:
237 : : return BTN_LEFT;
238 : :
239 : : case VALENT_POINTER_MIDDLE:
240 : : return BTN_MIDDLE;
241 : :
242 : : case VALENT_POINTER_SECONDARY:
243 : : return BTN_RIGHT;
244 : :
245 : 0 : default:
246 : : /* Any other buttons go after the legacy scroll buttons (4-7). */
247 : 0 : return button + (BTN_LEFT - 1) - 4;
248 : : }
249 : : }
250 : :
251 : : static void
252 : 0 : valent_xdp_input_pointer_axis (ValentInputAdapter *adapter,
253 : : double dx,
254 : : double dy)
255 : : {
256 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (adapter);
257 : :
258 : 0 : g_assert (VALENT_IS_INPUT_ADAPTER (adapter));
259 : 0 : g_assert (VALENT_IS_XDP_INPUT (self));
260 : 0 : g_assert (!G_APPROX_VALUE (dx, 0.0, 0.01) || !G_APPROX_VALUE (dy, 0.0, 0.01));
261 : :
262 : 0 : if G_UNLIKELY (!ensure_session (self))
263 : : return;
264 : :
265 : : /* On X11 we use discrete axis steps (eg. mouse wheel) because the absolute
266 : : * axis change doesn't seem to work
267 : : */
268 : : #ifdef GDK_WINDOWING_X11
269 : 0 : if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
270 : : {
271 : 0 : g_debug ("[%s] X11: using discrete axis step", G_STRFUNC);
272 : :
273 : 0 : if (dy < 0.0)
274 : 0 : xdp_session_pointer_axis_discrete (self->session,
275 : : XDP_AXIS_VERTICAL_SCROLL,
276 : : 1);
277 : 0 : else if (dy > 0.0)
278 : 0 : xdp_session_pointer_axis_discrete (self->session,
279 : : XDP_AXIS_VERTICAL_SCROLL,
280 : : -1);
281 : :
282 : 0 : return;
283 : : }
284 : : #endif /* GDK_WINDOWING_X11 */
285 : :
286 : 0 : xdp_session_pointer_axis (self->session, FALSE, dx, dy);
287 : 0 : xdp_session_pointer_axis (self->session, TRUE, 0.0, 0.0);
288 : : }
289 : :
290 : : static void
291 : 0 : valent_xdp_input_pointer_button (ValentInputAdapter *adapter,
292 : : unsigned int button,
293 : : gboolean pressed)
294 : : {
295 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (adapter);
296 : :
297 : 0 : g_assert (VALENT_IS_INPUT_ADAPTER (adapter));
298 : 0 : g_assert (VALENT_IS_XDP_INPUT (self));
299 : :
300 : 0 : if G_UNLIKELY (!ensure_session (self))
301 : : return;
302 : :
303 : : /* Translate the button to EVDEV constant */
304 : 0 : button = translate_to_evdev_button (button);
305 : 0 : xdp_session_pointer_button (self->session, button, pressed);
306 : : }
307 : :
308 : : static void
309 : 0 : valent_xdp_input_pointer_motion (ValentInputAdapter *adapter,
310 : : double dx,
311 : : double dy)
312 : : {
313 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (adapter);
314 : :
315 : 0 : g_assert (VALENT_IS_INPUT_ADAPTER (adapter));
316 : 0 : g_assert (VALENT_IS_XDP_INPUT (self));
317 : :
318 : 0 : if G_UNLIKELY (!ensure_session (self))
319 : : return;
320 : :
321 : 0 : xdp_session_pointer_motion (self->session, dx, dy);
322 : : }
323 : :
324 : :
325 : : /*
326 : : * ValentObject
327 : : */
328 : : static void
329 : 0 : valent_xdp_input_destroy (ValentObject *object)
330 : : {
331 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (object);
332 : :
333 : 0 : g_cancellable_cancel (self->cancellable);
334 : :
335 : 0 : if (self->session != NULL)
336 : 0 : xdp_session_close (self->session);
337 : :
338 : 0 : VALENT_OBJECT_CLASS (valent_xdp_input_parent_class)->destroy (object);
339 : 0 : }
340 : :
341 : : /*
342 : : * GObject
343 : : */
344 : : static void
345 : 0 : valent_xdp_input_finalize (GObject *object)
346 : : {
347 : 0 : ValentXdpInput *self = VALENT_XDP_INPUT (object);
348 : :
349 : 0 : g_clear_object (&self->cancellable);
350 : 0 : g_clear_object (&self->settings);
351 : 0 : g_clear_object (&self->session);
352 : :
353 : 0 : G_OBJECT_CLASS (valent_xdp_input_parent_class)->finalize (object);
354 : 0 : }
355 : :
356 : : static void
357 : 0 : valent_xdp_input_class_init (ValentXdpInputClass *klass)
358 : : {
359 : 0 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
360 : 0 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
361 : 0 : ValentInputAdapterClass *adapter_class = VALENT_INPUT_ADAPTER_CLASS (klass);
362 : :
363 : 0 : object_class->finalize = valent_xdp_input_finalize;
364 : :
365 : 0 : vobject_class->destroy = valent_xdp_input_destroy;
366 : :
367 : 0 : adapter_class->keyboard_keysym = valent_xdp_input_keyboard_keysym;
368 : 0 : adapter_class->pointer_axis = valent_xdp_input_pointer_axis;
369 : 0 : adapter_class->pointer_button = valent_xdp_input_pointer_button;
370 : 0 : adapter_class->pointer_motion = valent_xdp_input_pointer_motion;
371 : : }
372 : :
373 : : static void
374 : 0 : valent_xdp_input_init (ValentXdpInput *self)
375 : : {
376 : 0 : self->cancellable = g_cancellable_new ();
377 : 0 : self->settings = g_settings_new ("ca.andyholmes.Valent.Plugin.xdp");
378 : 0 : }
379 : :
|