VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/wayland.cpp

Last change on this file was 102039, checked in by vboxsync, 6 months ago

Additions: X11/Wayland: Another attempt to fix build (3), bugref:10194.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.9 KB
Line 
1/* $Id: wayland.cpp 102039 2023-11-09 15:14:57Z vboxsync $ */
2/** @file
3 * Guest Additions - Wayland Desktop Environment assistant.
4 */
5
6/*
7 * Copyright (C) 2017-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <iprt/asm.h>
29#include <iprt/thread.h>
30
31#include <VBox/HostServices/GuestPropertySvc.h>
32#include <VBox/HostServices/VBoxClipboardSvc.h>
33
34#include "VBoxClient.h"
35#include "clipboard.h"
36#include "wayland-helper.h"
37
38/** Polling interval for input focus monitoring task. */
39#define VBCL_WAYLAND_WAIT_HOST_FOCUS_TIMEOUT_MS (250)
40/** Relax interval for input focus monitoring task. */
41#define VBCL_WAYLAND_WAIT_HOST_FOCUS_RELAX_MS (100)
42
43/** List of available Wayland Desktop Environment helpers. Sorted in order of preference. */
44static const VBCLWAYLANDHELPER *g_apWaylandHelpers[] =
45{
46 &g_WaylandHelperDcp, /* Device Control Protocol helper. */
47 &g_WaylandHelperGtk, /* GTK helper. */
48 NULL, /* Terminate list. */
49};
50
51/** Global flag to tell service to go shutdown when needed. */
52static bool volatile g_fShutdown = false;
53
54/** Selected helpers for Clipboard and Drag-and-Drop. */
55static const VBCLWAYLANDHELPER *g_pWaylandHelperClipboard = NULL;
56static const VBCLWAYLANDHELPER *g_pWaylandHelperDnd = NULL;
57
58/** Corresponding threads for host events handling. */
59static RTTHREAD g_ClipboardThread;
60static RTTHREAD g_DndThread;
61static RTTHREAD g_HostInputFocusThread;
62
63/**
64 * Worker for Shared Clipboard events from host.
65 *
66 * @returns IPRT status code.
67 * @param hThreadSelf IPRT thread handle.
68 * @param pvUser User data (unused).
69 */
70static DECLCALLBACK(int) vbclWaylandClipboardWorker(RTTHREAD hThreadSelf, void *pvUser)
71{
72 SHCLCONTEXT ctx;
73 int rc;
74
75 RT_NOREF(pvUser);
76
77 RT_ZERO(ctx);
78
79 /* Connect to the host service. */
80 rc = VbglR3ClipboardConnectEx(&ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID);
81 /* Notify parent thread. */
82 RTThreadUserSignal(hThreadSelf);
83
84 if (RT_SUCCESS(rc))
85 {
86 /* Provide helper with host clipboard service connection handle. */
87 g_pWaylandHelperClipboard->pfnSetClipboardCtx(&ctx.CmdCtx);
88
89 /* Process host events. */
90 while (!ASMAtomicReadBool(&g_fShutdown))
91 {
92 rc = VBClClipboardReadHostEvent(&ctx, g_pWaylandHelperClipboard->pfnHGClipReport,
93 g_pWaylandHelperClipboard->pfnGHClipRead);
94 if (RT_FAILURE(rc))
95 {
96 VBClLogInfo("cannot process host clipboard event, rc=%Rrc\n", rc);
97 RTThreadSleep(RT_MS_1SEC / 2);
98 }
99 }
100
101 VbglR3ClipboardDisconnectEx(&ctx.CmdCtx);
102 }
103
104 VBClLogVerbose(2, "clipboard thread, rc=%Rrc\n", rc);
105
106 return rc;
107}
108
109/**
110 * Worker for Drag-and-Drop events from host.
111 *
112 * @returns IPRT status code.
113 * @param hThreadSelf IPRT thread handle.
114 * @param pvUser User data (unused).
115 */
116static DECLCALLBACK(int) vbclWaylandDndWorker(RTTHREAD hThreadSelf, void *pvUser)
117{
118 RT_NOREF(pvUser);
119
120 RTThreadUserSignal(hThreadSelf);
121 return VINF_SUCCESS;
122}
123
124/**
125 * Worker for VM window focus change polling thread.
126 *
127 * Some Wayland helpers need to be notified about VM
128 * window focus change events. This is needed in order to
129 * ask about, for example, if guest clipboard content was
130 * changed since last user interaction. Such guest are not
131 * able to notify host about clipboard content change and
132 * needed to be asked implicitly.
133 *
134 * @returns IPRT status code.
135 * @param hThreadSelf IPRT thread handle.
136 * @param pvUser User data (unused).
137 */
138static DECLCALLBACK(int) vbclWaylandHostInputFocusWorker(RTTHREAD hThreadSelf, void *pvUser)
139{
140 int rc;
141
142 RT_NOREF(pvUser);
143
144 HGCMCLIENTID idClient;
145
146 rc = VbglR3GuestPropConnect(&idClient);
147
148 RTThreadUserSignal(hThreadSelf);
149
150 if (RT_SUCCESS(rc))
151 {
152 while (!ASMAtomicReadBool(&g_fShutdown))
153 {
154 static char achBuf[GUEST_PROP_MAX_NAME_LEN];
155 char *pszName = NULL;
156 char *pszValue = NULL;
157 char *pszFlags = NULL;
158 bool fWasDeleted = false;
159 uint64_t u64Timestamp = 0;
160
161 rc = VbglR3GuestPropWait(idClient, VBOX_GUI_FOCUS_CHANGE_GUEST_PROP_NAME, achBuf, sizeof(achBuf), u64Timestamp,
162 VBCL_WAYLAND_WAIT_HOST_FOCUS_TIMEOUT_MS, &pszName, &pszValue, &u64Timestamp,
163 &pszFlags, NULL, &fWasDeleted);
164 if (RT_SUCCESS(rc))
165 {
166 uint32_t fFlags = 0;
167
168 VBClLogVerbose(1, "guest property change: name: %s, val: %s, flags: %s, fWasDeleted: %RTbool\n",
169 pszName, pszValue, pszFlags, fWasDeleted);
170
171 if (RT_SUCCESS(GuestPropValidateFlags(pszFlags, &fFlags)))
172 {
173 if (RTStrNCmp(pszName, VBOX_GUI_FOCUS_CHANGE_GUEST_PROP_NAME, GUEST_PROP_MAX_NAME_LEN) == 0)
174 {
175 if (fFlags & GUEST_PROP_F_RDONLYGUEST)
176 {
177 if (RT_VALID_PTR(g_pWaylandHelperClipboard))
178 {
179 if (RTStrNCmp(pszValue, "0", GUEST_PROP_MAX_NAME_LEN) == 0)
180 {
181 rc = g_pWaylandHelperClipboard->pfnPopup();
182 VBClLogVerbose(1, "trigger popup, rc=%Rrc\n", rc);
183 }
184 }
185 else
186 VBClLogVerbose(1, "will not trigger popup\n");
187 }
188 else
189 VBClLogError("property has invalid attributes\n");
190 }
191 else
192 VBClLogVerbose(1, "unknown property name '%s'\n", pszName);
193
194 } else
195 VBClLogError("guest property change: name: %s, val: %s, flags: %s, fWasDeleted: %RTbool: bad flags\n",
196 pszName, pszValue, pszFlags, fWasDeleted);
197
198 } else if ( rc != VERR_TIMEOUT
199 && rc != VERR_INTERRUPTED)
200 {
201 VBClLogError("error on waiting guest property notification, rc=%Rrc\n", rc);
202 RTThreadSleep(VBCL_WAYLAND_WAIT_HOST_FOCUS_RELAX_MS);
203 }
204 }
205 }
206
207 return rc;
208}
209
210
211/**
212 * @interface_method_impl{VBCLSERVICE,pfnInit}
213 */
214static DECLCALLBACK(int) vbclWaylandInit(void)
215{
216 int rc = VERR_NOT_SUPPORTED;
217 int idxHelper = 0;
218
219 /** Custom log prefix to be used for logger instance of this process. */
220 static const char *pszLogPrefix = "VBoxClient Wayland:";
221
222 VBClLogSetLogPrefix(pszLogPrefix);
223
224 /* Go through list of available helpers and try to pick up one. */
225 while (g_apWaylandHelpers[idxHelper])
226 {
227 if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnProbe))
228 {
229 int fCaps = VBOX_WAYLAND_HELPER_CAP_NONE;
230
231 VBClLogInfo("probing Wayland helper '%s'\n",
232 g_apWaylandHelpers[idxHelper]->pszName);
233
234 fCaps = g_apWaylandHelpers[idxHelper]->pfnProbe();
235
236 /* Try Clipboard helper. */
237 if ( fCaps & VBOX_WAYLAND_HELPER_CAP_CLIPBOARD
238 && !RT_VALID_PTR(g_pWaylandHelperClipboard))
239 {
240 if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit))
241 {
242 rc = g_apWaylandHelpers[idxHelper]->pfnInit();
243 if (RT_SUCCESS(rc))
244 g_pWaylandHelperClipboard = g_apWaylandHelpers[idxHelper];
245 else
246 VBClLogError("Wayland helper '%s' cannot be initialized, skipping\n",
247 g_apWaylandHelpers[idxHelper]->pszName);
248 }
249 else
250 VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n",
251 g_apWaylandHelpers[idxHelper]->pszName);
252 }
253
254 /* Try DnD helper. */
255 if ( fCaps & VBOX_WAYLAND_HELPER_CAP_DND
256 && !RT_VALID_PTR(g_pWaylandHelperDnd))
257 {
258 if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit))
259 {
260 rc = g_apWaylandHelpers[idxHelper]->pfnInit();
261 if (RT_SUCCESS(rc))
262 g_pWaylandHelperDnd = g_apWaylandHelpers[idxHelper];
263 else
264 VBClLogError("Wayland helper '%s' cannot be initialized, skipping\n",
265 g_apWaylandHelpers[idxHelper]->pszName);
266 }
267 else
268 VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n",
269 g_apWaylandHelpers[idxHelper]->pszName);
270 }
271 }
272
273 /* See if we found all the needed helpers. */
274 if ( RT_VALID_PTR(g_pWaylandHelperClipboard)
275 && RT_VALID_PTR(g_pWaylandHelperDnd))
276 break;
277
278 idxHelper++;
279 }
280
281 /* Check result. */
282 if (RT_VALID_PTR(g_pWaylandHelperClipboard))
283 VBClLogInfo("found Wayland Shared Clipboard helper '%s'\n", g_pWaylandHelperClipboard->pszName);
284 else
285 VBClLogError("Wayland Shared Clipboard helper not found, clipboard sharing not possible\n");
286
287 /* Check result. */
288 if (RT_VALID_PTR(g_pWaylandHelperDnd))
289 VBClLogInfo("found Wayland Drag-and-Drop helper '%s'\n", g_pWaylandHelperDnd->pszName);
290 else
291 VBClLogError("Wayland Drag-and-Drop helper not found, drag-and-drop not possible\n");
292
293 return rc;
294}
295
296/**
297 * @interface_method_impl{VBCLSERVICE,pfnWorker}
298 */
299static DECLCALLBACK(int) vbclWaylandWorker(bool volatile *pfShutdown)
300{
301 int rc = VINF_SUCCESS;
302
303 RT_NOREF(pfShutdown);
304
305 VBClLogVerbose(1, "starting wayland worker thread\n");
306
307 /* Start event loop for clipboard events processing from host. */
308 if (RT_VALID_PTR(g_pWaylandHelperClipboard))
309 {
310 rc = VBClClipboardThreadStart(&g_ClipboardThread, vbclWaylandClipboardWorker, "wl-clip", NULL);
311 VBClLogVerbose(1, "clipboard thread started, rc=%Rrc\n", rc);
312 }
313
314 /* Start event loop for DnD events processing from host. */
315 if ( RT_SUCCESS(rc)
316 && RT_VALID_PTR(g_pWaylandHelperDnd))
317 {
318 rc = VBClClipboardThreadStart(&g_DndThread, vbclWaylandDndWorker, "wl-dnd", NULL);
319 VBClLogVerbose(1, "DnD thread started, rc=%Rrc\n", rc);
320 }
321
322 /* Start polling host input focus events. */
323 if (RT_SUCCESS(rc))
324 {
325 rc = VBClClipboardThreadStart(&g_HostInputFocusThread, vbclWaylandHostInputFocusWorker, "wl-focus", NULL);
326 VBClLogVerbose(1, "host input focus polling thread started, rc=%Rrc\n", rc);
327 }
328
329 /* Notify parent thread that we are successfully started. */
330 RTThreadUserSignal(RTThreadSelf());
331
332 if (RT_SUCCESS(rc))
333 {
334 int rcThread = VINF_SUCCESS;
335
336 if (RT_VALID_PTR(g_pWaylandHelperClipboard))
337 {
338 rc = RTThreadWait(g_ClipboardThread, RT_INDEFINITE_WAIT, &rcThread);
339 VBClLogVerbose(1, "clipboard thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread);
340 }
341
342 if ( RT_SUCCESS(rc)
343 && RT_VALID_PTR(g_pWaylandHelperDnd))
344 {
345 rc = RTThreadWait(g_DndThread, RT_INDEFINITE_WAIT, &rcThread);
346 VBClLogVerbose(1, "DnD thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread);
347 }
348
349 if (RT_SUCCESS(rc))
350 {
351 rc = RTThreadWait(g_HostInputFocusThread, RT_INDEFINITE_WAIT, &rcThread);
352 VBClLogVerbose(1, "host input focus polling thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread);
353 }
354 }
355
356 VBClLogVerbose(1, "wayland worker thread finished, rc=%Rrc\n", rc);
357
358 return rc;
359}
360
361/**
362 * @interface_method_impl{VBCLSERVICE,pfnStop}
363 */
364static DECLCALLBACK(void) vbclWaylandStop(void)
365{
366 VBClLogVerbose(1, "terminating wayland service: clipboard & DnD host event loops\n");
367
368 /* This callback can be called twice (not good, needs to be fixed). Already was shut down? */
369 if (ASMAtomicReadBool(&g_fShutdown))
370 return;
371
372 ASMAtomicWriteBool(&g_fShutdown, true);
373
374 if (RT_VALID_PTR(g_pWaylandHelperClipboard))
375 RTThreadPoke(g_ClipboardThread);
376
377 if (RT_VALID_PTR(g_pWaylandHelperDnd))
378 RTThreadPoke(g_DndThread);
379}
380
381/**
382 * @interface_method_impl{VBCLSERVICE,pfnTerm}
383 */
384static DECLCALLBACK(int) vbclWaylandTerm(void)
385{
386 int rc = VINF_SUCCESS;
387
388 VBClLogVerbose(1, "shutting down wayland service: clipboard & DnD helpers\n");
389
390 if ( RT_VALID_PTR(g_pWaylandHelperClipboard)
391 && RT_VALID_PTR(g_pWaylandHelperClipboard->pfnTerm))
392 rc = g_pWaylandHelperClipboard->pfnTerm();
393
394 if ( RT_SUCCESS(rc)
395 && RT_VALID_PTR(g_pWaylandHelperDnd)
396 && RT_VALID_PTR(g_pWaylandHelperDnd->pfnTerm))
397 rc = g_pWaylandHelperDnd->pfnTerm();
398
399 return rc;
400}
401
402VBCLSERVICE g_SvcWayland =
403{
404 "wayland", /* szName */
405 "Wayland assistant", /* pszDescription */
406 ".vboxclient-wayland", /* pszPidFilePathTemplate */
407 NULL, /* pszUsage */
408 NULL, /* pszOptions */
409 NULL, /* pfnOption */
410 vbclWaylandInit, /* pfnInit */
411 vbclWaylandWorker, /* pfnWorker */
412 vbclWaylandStop, /* pfnStop */
413 vbclWaylandTerm, /* pfnTerm */
414};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use