VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper.cpp@ 101878

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

Additions: X11/Wayland: Add initial support for clipboard sharing with Gnome and Plasma Wayland guests (not yet enabled), bugref:10194.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.3 KB
Line 
1/* $Id: wayland-helper.cpp 101878 2023-11-06 15:36:24Z vboxsync $ */
2/** @file
3 * Guest Additions - Common code for Wayland Desktop Environment helpers.
4 */
5
6/*
7 * Copyright (C) 2006-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/env.h>
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/thread.h>
32#include <iprt/err.h>
33#include <iprt/time.h>
34
35#include "VBoxClient.h"
36#include "wayland-helper.h"
37
38static const char *g_pcszSessionDescClipardCopyToGuest = "Copy clipboard to the guest";
39static const char *g_pcszSessionDescClipardAnnounceToHost = "Announce and copy clipboard to the host";
40static const char *g_pcszSessionDescClipardCopyToHost = "Copy clipboard to the host";
41
42/**
43 * Interaction between VBoxClient and Wayland environment implies
44 * a sequential data exchange between both parties. In order to
45 * temporary store and protect this data, we put it into a Session.
46
47 * Session can be started, joined or ended.
48
49 * Each time when either host (via VBoxClient) or Wayland client
50 * initiates a new data transfer (such as clipboard sharing or drag-n-drop
51 * operation) a new session should be started. Due to current
52 * implementation,
53 * no more than one session can run in the same time. Therefore, before
54 * starting new session, previous one needs to be ended.
55
56 * When either VBoxClient or Wayland thread needs to access session data,
57 * it need to join session at first. Multiple threads can join the same
58 * session in parallel.
59
60 * When sequence of operations which were required to transfer data to
61 * either side is complete, session should be ended.
62 *
63 * Session has the following attributes:
64 *
65 * STATE - synchronization point which is used in order to prevent
66 * access to session data when it is not allowed (see session
67 * states description).
68 * TYPE - unique attribute which should be used for sanity checks
69 * when accessing session data.
70 * DESCRIPTION - a text attribute which is mostly used for logging purposes
71 * when monitoring multiple sequential sessions flow.
72 * REFERENCE COUNT - attribute which shows a number of session users who currently
73 * joined a session.
74 *
75 * Session state represents synchronization point when session data needs to
76 * be accessed. It can have permanent or intermediate value. The following states
77 * are defined: IDLE, STARTING, STARTED and TERMINATING.
78 *
79 * Default state is IDLE. It is set when session is just initialized or when
80 * it is ended.
81 *
82 * When user starts a new session, its state will be atomically switched from
83 * IDLE to STARTING. Then, initialization callback, provided by user, will be invoked.
84 * And, finally, state will be atomically transitioned from STARTING to STARTED.
85 * Intermediate state STARTING will ensure that no other user will be allowed to
86 * start, join or end session during this initialization step.
87 *
88 * User is only allowed to join session when it is in STARTED state. Once
89 * session is joined, its reference counted will be increased by 1, provided
90 * user callback will be called and, finally, reference counter will be dropped by 1.
91 *
92 * When user ends session, at first, its state will be atomically transitioned
93 * from STARTED to TERMINATING ensuring that no other user can start, join or end
94 * session in the same time. Then session will wait until all the users who joined
95 * the session are left by looking into session's reference counter. After this, it
96 * will invoke provided by user termination callback and atomically transition its state
97 * from TERMINATING to IDLE, allowing to start a new session.
98 */
99
100/**
101 * Try atomically enter session state within time interval.
102 *
103 *
104 *
105 * @returns IPRT status code.
106 * @param pSessionState Session state.
107 * @param to New session state ID to switch to.
108 * @param from Old session state ID which will be
109 * atomically compared with the current
110 * state before switching.
111 * @param u64TimeoutMs Operation timeout.
112 */
113static int vbcl_wayland_session_enter_state(volatile vbcl_wl_session_state_t *pSessionState,
114 vbcl_wl_session_state_t to, vbcl_wl_session_state_t from, uint64_t u64TimeoutMs)
115{
116 bool fRc = false;
117 uint64_t tsStart = RTTimeMilliTS();
118
119 while ( !(fRc = ASMAtomicCmpXchgU8((volatile uint8_t *)pSessionState, to, from))
120 && (RTTimeMilliTS() - tsStart) < u64TimeoutMs)
121 {
122 RTThreadSleep(VBCL_WAYLAND_RELAX_INTERVAL_MS);
123 }
124
125 return fRc ? VINF_SUCCESS : VERR_RESOURCE_BUSY;
126}
127
128
129
130RTDECL(void) vbcl_wayland_session_init(vbcl_wl_session_t *pSession)
131{
132 AssertPtrReturnVoid(pSession);
133
134 /* Reset internal data to default values. */
135 ASMAtomicWriteU32(&pSession->u32Magic, VBCL_WAYLAND_SESSION_MAGIC);
136 ASMAtomicWriteU8((volatile uint8_t *)&pSession->enmState, VBCL_WL_SESSION_STATE_IDLE);
137
138 pSession->enmType = VBCL_WL_SESSION_TYPE_INVALID;
139 pSession->pcszDesc = NULL;
140
141 ASMAtomicWriteU32(&pSession->cUsers, 0);
142}
143
144RTDECL(int) vbcl_wayland_session_start(vbcl_wl_session_t *pSession,
145 vbcl_wl_session_type_t enmType,
146 PFNVBCLWLSESSIONCB pfnStart,
147 void *pvUser)
148{
149 int rc;
150 const char *pcszDesc;
151
152 /* Make sure mandatory parameters were provided. */
153 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
154 AssertPtrReturn(pfnStart, VERR_INVALID_PARAMETER);
155
156 /* Make sure session was initialized. */
157 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
158 VERR_SEM_DESTROYED);
159
160 /* Set session description according to its type. */
161 if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST)
162 pcszDesc = g_pcszSessionDescClipardCopyToGuest;
163 else if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST)
164 pcszDesc = g_pcszSessionDescClipardAnnounceToHost;
165 else if (enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST)
166 pcszDesc = g_pcszSessionDescClipardCopyToHost;
167 else
168 pcszDesc = NULL;
169
170 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
171 VBCL_WL_SESSION_STATE_STARTING,
172 VBCL_WL_SESSION_STATE_IDLE,
173 RT_MS_1SEC);
174 if (RT_SUCCESS(rc))
175 {
176 int rcStart;
177
178 /* Make sure nobody is using the session. */
179 Assert(ASMAtomicReadU32(&pSession->cUsers) == 0);
180
181 /* Set session type. */
182 pSession->enmType = enmType;
183 /* Set session description. */
184 pSession->pcszDesc = pcszDesc;
185 /* Reset reference counter. */
186 ASMAtomicWriteU32(&pSession->cUsers, 1);
187
188 rcStart = pfnStart(enmType, pvUser);
189
190 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
191 VBCL_WL_SESSION_STATE_STARTED,
192 VBCL_WL_SESSION_STATE_STARTING,
193 RT_MS_1SEC);
194
195 VBClLogVerbose(2, "session start [%s], rc=%Rrc, rcStart=%Rrc\n",
196 pSession->pcszDesc, rc, rcStart);
197
198 if (RT_SUCCESS(rc))
199 rc = rcStart;
200 }
201 else
202 VBClLogError("cannot start session [%s], another session [%s] is currently in progress, rc=%Rrc\n",
203 pcszDesc, pSession->pcszDesc, rc);
204
205 return rc;
206}
207
208RTDECL(int) vbcl_wayland_session_join_ex(vbcl_wl_session_t *pSession,
209 PFNVBCLWLSESSIONCB pfnJoin, void *pvUser,
210 const char *pcszCallee)
211{
212 int rc;
213
214 /* Make sure mandatory parameters were provided. */
215 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
216 AssertPtrReturn(pfnJoin, VERR_INVALID_PARAMETER);
217
218 /* Make sure session was initialized. */
219 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
220 VERR_SEM_DESTROYED);
221
222 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
223 VBCL_WL_SESSION_STATE_STARTED,
224 VBCL_WL_SESSION_STATE_STARTED,
225 RT_MS_1SEC);
226 if (RT_SUCCESS(rc))
227 {
228 uint32_t cUsers;
229 const char *pcszDesc = pSession->pcszDesc;
230
231 /* Take session reference. */
232 cUsers = ASMAtomicIncU32(&pSession->cUsers);
233 VBClLogVerbose(2, "session join [%s @ %s], cUsers=%u\n", pcszDesc, pcszCallee, cUsers);
234
235 /* Process callback while holding reference to session. */
236 rc = pfnJoin(pSession->enmType, pvUser);
237
238 /* Release session reference. */
239 cUsers = ASMAtomicDecU32(&pSession->cUsers);
240 VBClLogVerbose(2, "session leave [%s @ %s], cUsers=%u, rc=%Rrc\n",
241 pcszDesc, pcszCallee, cUsers, rc);
242
243 }
244 else
245 VBClLogError("cannot join session from %s, rc=%Rrc\n", pcszCallee, rc);
246
247 return rc;
248}
249
250RTDECL(int) vbcl_wayland_session_end(vbcl_wl_session_t *pSession,
251 PFNVBCLWLSESSIONCB pfnEnd, void *pvUser)
252{
253 int rc;
254
255 /* Make sure mandatory parameters were provided.. */
256 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER);
257
258 /* Make sure session was initialized. */
259 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC,
260 VERR_SEM_DESTROYED);
261
262 /* Session not running? */
263 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
264 VBCL_WL_SESSION_STATE_IDLE,
265 VBCL_WL_SESSION_STATE_IDLE,
266 0 /* u64TimeoutMs */);
267 if (RT_SUCCESS(rc))
268 {
269 /* NOP. */
270 VBClLogVerbose(2, "session end: nothing to do, no active session is running\n");
271 }
272 else
273 {
274 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
275 VBCL_WL_SESSION_STATE_TERMINATING, VBCL_WL_SESSION_STATE_STARTED,
276 RT_MS_1SEC);
277 if (RT_SUCCESS(rc))
278 {
279 int rcEnd = VINF_SUCCESS;
280 const char *pcszDesc = pSession->pcszDesc;
281
282 /* Wait until for other callers to release session reference. */
283 while (ASMAtomicReadU32(&pSession->cUsers) > 1)
284 RTThreadSleep(VBCL_WAYLAND_RELAX_INTERVAL_MS);
285
286 if (RT_VALID_PTR(pfnEnd))
287 rcEnd = pfnEnd(pSession->enmType, pvUser);
288
289 pSession->enmType = VBCL_WL_SESSION_TYPE_INVALID;
290 pSession->pcszDesc = NULL;
291
292 ASMAtomicDecU32(&pSession->cUsers);
293
294 rc = vbcl_wayland_session_enter_state(&pSession->enmState,
295 VBCL_WL_SESSION_STATE_IDLE, VBCL_WL_SESSION_STATE_TERMINATING,
296 RT_MS_1SEC);
297
298 VBClLogVerbose(2, "session end [%s], rc=%Rrc, rcEnd=%Rrc\n", pcszDesc, rc, rcEnd);
299
300 if (RT_SUCCESS(rc))
301 rc = rcEnd;
302 }
303 else
304 VBClLogError("cannot end session, rc=%Rrc\n", rc);
305 }
306
307 return rc;
308}
309
310RTDECL(bool) vbcl_wayland_session_is_started(vbcl_wl_session_t *pSession)
311{
312 /* Make sure mandatory parameters were provided. */
313 AssertPtrReturn(pSession, false);
314
315 /* Make sure session was initialized. */
316 AssertReturn(ASMAtomicReadU32(&pSession->u32Magic) == VBCL_WAYLAND_SESSION_MAGIC, false);
317
318 return RT_BOOL(ASMAtomicReadU8((volatile uint8_t *)&pSession->enmState) == VBCL_WL_SESSION_STATE_STARTED);
319}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use