Changeset 101878 in vbox
- Timestamp:
- Nov 6, 2023 3:36:24 PM (15 months ago)
- Location:
- trunk
- Files:
-
- 4 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/include/VBox/GuestHost/clipboard-helper.h
r98103 r101878 44 44 45 45 #include <VBox/GuestHost/SharedClipboard.h> 46 47 /** Guest property which is set by GUI in order to notify guest about VM window focus change. */ 48 #define VBOX_GUI_FOCUS_CHANGE_GUEST_PROP_NAME "/VirtualBox/GuestAdd/GuiOnFocus" 46 49 47 50 /** Constants needed for string conversions done by the Linux/Mac clipboard code. */ -
trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk
r100585 r101878 48 48 # 49 49 PROGRAMS.linux += VBoxDRMClient 50 51 ifdef VBOX_WITH_WAYLAND_ADDITIONS 52 PROGRAMS.linux += vboxwl 53 endif 50 54 51 55 # Common Guest / Host sources. … … 75 79 ifdef VBOX_WITH_WAYLAND_ADDITIONS 76 80 VBoxClient_DEFS += VBOX_WITH_WAYLAND_ADDITIONS 81 VBoxClient_INCS += ../wlInclude/ 77 82 VBoxClient_SOURCES += \ 78 83 wayland-helper-dcp.cpp \ 79 84 wayland-helper-gtk.cpp \ 80 wayland.cpp 85 wayland-helper.cpp \ 86 wayland.cpp \ 87 wayland-helper-ipc.cpp \ 88 ../wlInclude/wayland-protocol.c \ 89 ../wlInclude/wlr-data-control-unstable-v1.c 81 90 endif 82 91 … … 117 126 endif 118 127 endif 128 ifdef VBOX_WITH_WAYLAND_ADDITIONS 129 VBoxClient_LIBS.linux += glib-2.0 130 VBoxClient_LIBS.linux += gobject-2.0 131 endif 119 132 120 133 # XXX: -L comes from the template, but not rpath … … 185 198 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-x11.cpp \ 186 199 clipboard.cpp \ 200 clipboard-common.cpp \ 187 201 clipboard-x11.cpp 188 202 ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS … … 190 204 VBoxClient_SOURCES += \ 191 205 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp \ 192 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardPath.cpp 206 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/ClipboardPath.cpp \ 207 $(PATH_ROOT)/src/VBox/GuestHost/common/mime-type-converter.cpp 193 208 ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP 194 209 VBoxClient_DEFS += VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP … … 226 241 VBOX_VBOXCLIENT_LIBXMU_VER = 6 227 242 VBOX_VBOXCLIENT_LIBXRANDR_VER = 2 243 VBOX_VBOXCLIENT_LIBWAYLANDCLIENT_VER = 0 228 244 endif 229 245 ## Dynamic import no. 1: libX11.so … … 235 251 ## Dynamic import no. 4: libXrandr.so 236 252 $(evalcall2 def_libToLazyLoad,$(PATH_ROOT)/src/VBox/GuestHost/libXrandr.def,libXrandr,$(VBOX_VBOXCLIENT_LIBXRANDR_VER)) 253 ## Dynamic import no. 5: libwayland-client.so 254 $(evalcall2 def_libToLazyLoad,$(PATH_ROOT)/src/VBox/GuestHost/libwayland-client.def,libwayland-client,$(VBOX_VBOXCLIENT_LIBWAYLANDCLIENT_VER)) 255 endif 256 257 ifdef VBOX_WITH_WAYLAND_ADDITIONS 258 define def_libToLazyLoad2 259 vboxwl_SOURCES += \ 260 $$(vboxwl_0_OUTDIR)/$(2)LazyLoad.asm 261 vboxwl_CLEAN += \ 262 $$(vboxwl_0_OUTDIR)/$(2)LazyLoad.asm 263 $$$$(vboxwl_0_OUTDIR)/$(2)LazyLoad.asm: $$(PATH_ROOT)/src/VBox/GuestHost/$(2).def $(VBOX_DEF_2_LAZY_LOAD) | $$$$(dir $$@) 264 $$(call MSG_TOOL,VBoxDef2LazyLoad,$$(PATH_ROOT)/src/VBox/GuestHost/$(2).def,$$@) 265 $$(eval VBOX_VBOXWL_LIB_TO_LAZYLOAD_SUFF=$$$$(if $(3),$(SUFF_DLL).$(3),$(SUFF_DLL))) 266 $$(QUIET)$$(RM) -f -- "$$@" 267 $$(VBOX_DEF_2_LAZY_LOAD) --system --library $(2)$$(VBOX_VBOXWL_LIB_TO_LAZYLOAD_SUFF) --output "$$@" $$(PATH_ROOT)/src/VBox/GuestHost/$(2).def 268 endef 269 270 vboxwl_TEMPLATE = VBoxGuestR3Exe 271 vboxwl_DEFS += VBOX_WITH_SHARED_CLIPBOARD 272 ifdef VBOX_WITH_AUTOMATIC_DEFS_QUOTING 273 vboxwl_DEFS += VBOX_BUILD_TARGET="$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)" 274 else 275 vboxwl_DEFS += VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\" 276 endif 277 vboxwl_SOURCES = \ 278 vboxwl.cpp \ 279 logging.cpp \ 280 clipboard-common.cpp \ 281 wayland-helper-ipc.cpp \ 282 wayland-helper.cpp \ 283 $(PATH_ROOT)/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp \ 284 $(PATH_ROOT)/src/VBox/GuestHost/common/mime-type-converter.cpp 285 vboxwl_SOURCES += $(VBOX_GH_SOURCES) 286 287 VBOX_VBOXWL_LIBWAYLANDCLIENT_VER = 0 288 VBOX_VBOXWL_LIBGTK3_VER = 0 289 290 ## Dynamic import no. 5: libwayland-client.so 291 $(evalcall2 def_libToLazyLoad2,$(PATH_ROOT)/src/VBox/GuestHost/libwayland-client.def,libwayland-client,$(VBOX_VBOXWL_LIBWAYLANDCLIENT_VER)) 292 ## Dynamic import no. 6: libgtk-3.so 293 $(evalcall2 def_libToLazyLoad2,$(PATH_ROOT)/src/VBox/GuestHost/libgtk-3.def,libgtk-3,$(VBOX_VBOXWL_LIBGTK3_VER)) 294 295 vboxwl_LIBS.linux += glib-2.0 296 vboxwl_LIBS.linux += gobject-2.0 297 vboxwl_LIBS.linux += gio-2.0 237 298 endif 238 299 -
trunk/src/VBox/Additions/x11/VBoxClient/VBoxClient.h
r100246 r101878 38 38 39 39 #include <VBox/GuestHost/DisplayServerType.h> 40 41 /** A shortcut to log callback entering. */ 42 #define VBCL_LOG_CALLBACK VBClLogVerbose(3, "%s\n", __func__) 40 43 41 44 int VBClShowNotify(const char *pszHeader, const char *pszBody); -
trunk/src/VBox/Additions/x11/VBoxClient/main.cpp
r100246 r101878 206 206 static void vboxClientSignalHandler(int iSignal) 207 207 { 208 int rc = RTCritSectEnter(&g_csSignalHandler); 208 int rc; 209 210 /* On Wayland, SIGPIPE might be issued if compositor no longer wants 211 * to communicate. This should not be a reason for process termination. */ 212 if (iSignal == SIGPIPE) 213 return; 214 215 rc = RTCritSectEnter(&g_csSignalHandler); 209 216 if (RT_SUCCESS(rc)) 210 217 { -
trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper-dcp.cpp
r100246 r101878 1 1 /* $Id$ */ 2 2 /** @file 3 * Guest Additions - Wayland Desktop Environment helper which uses Data Control Protocol. 3 * Guest Additions - Data Control Protocol (DCP) helper for Wayland. 4 * 5 * This module implements Shared Clipboard support for Wayland guests 6 * using Data Control Protocol interface. 4 7 */ 5 8 … … 26 29 */ 27 30 31 #include <iprt/env.h> 32 #include <iprt/assert.h> 33 #include <iprt/string.h> 34 #include <iprt/thread.h> 35 36 #include <VBox/GuestHost/mime-type-converter.h> 37 38 #include "VBoxClient.h" 39 #include "clipboard.h" 28 40 #include "wayland-helper.h" 41 #include "wayland-helper-ipc.h" 42 43 #include "wayland-client-protocol.h" 44 #include "wlr-data-control-unstable-v1.h" 45 46 /** Environment variable which points to which Wayland compositor we should connect. 47 * Must always be checked. */ 48 #define VBCL_ENV_WAYLAND_DISPLAY "WAYLAND_DISPLAY" 49 50 /* Maximum length of Wayland interface name. */ 51 #define VBCL_WAYLAND_INTERFACE_NAME_MAX (64) 52 /* Maximum waiting time interval for Wayland socket I/O to start. */ 53 #define VBCL_WAYLAND_IO_TIMEOUT_MS (500) 54 55 /* Data chunk size when reading clipboard data from Wayland. */ 56 #define VBOX_WAYLAND_BUFFER_CHUNK_SIZE (_1M) 57 /* Data chunk increment size to grow local buffer when it is not big enough. */ 58 #define VBOX_WAYLAND_BUFFER_CHUNK_INC_SIZE (_4M) 59 /* Maximum length of clipboard buffer. */ 60 #define VBOX_WAYLAND_BUFFER_MAX (_16M) 61 62 /** Minimum version numbers of Wayland interfaces we expect a compositor to provide. */ 63 #define VBCL_WAYLAND_DATA_DEVICE_MANAGER_VERSION_MIN (3) 64 #define VBCL_WAYLAND_SEAT_VERSION_MIN (5) 65 #define VBCL_WAYLAND_ZWLR_DATA_CONTROL_MANAGER_VERSION_MIN (1) 66 67 /* A helper for matching interface and bind to it in registry callback.*/ 68 #define VBCL_WAYLAND_REGISTRY_ADD_MATCH(_pRegistry, _sIfaceName, _uIface, _iface_to_bind_to, _ctx_member, _ctx_member_type, _uVersion) \ 69 if (RTStrNCmp(_sIfaceName, _iface_to_bind_to.name, VBCL_WAYLAND_INTERFACE_NAME_MAX) == 0) \ 70 { \ 71 if (! _ctx_member) \ 72 { \ 73 _ctx_member = \ 74 (_ctx_member_type)wl_registry_bind(_pRegistry, _uIface, &_iface_to_bind_to, _uVersion); \ 75 VBClLogVerbose(4, "binding to Wayland interface '%s' (%u) v%u\n", _iface_to_bind_to.name, _uIface, wl_proxy_get_version((struct wl_proxy *) _ctx_member)); \ 76 } \ 77 AssertPtrReturnVoid(_ctx_member); \ 78 } 79 80 /* Node of mime-types list. */ 81 typedef struct 82 { 83 /** IPRT list node. */ 84 RTLISTNODE Node; 85 /** Data mime-type in string representation. */ 86 char *pszMimeType; 87 } vbox_wl_dcp_mime_t; 88 89 /** 90 * DCP session data. 91 * 92 * A structure which accumulates all the necessary data required to 93 * maintain session between host and Wayland for clipboard sharing. */ 94 typedef struct 95 { 96 /** Generic VBoxClient Wayland session data (synchronization point). */ 97 vbcl_wl_session_t Base; 98 99 /** Session data for clipboard sharing. 100 * 101 * This data will be filled sequentially piece by piece by both 102 * sides - host event loop and Wayland event loop until clipboard 103 * buffer is obtained. 104 */ 105 struct 106 { 107 /** List of mime-types which are being advertised by guest. */ 108 vbox_wl_dcp_mime_t mimeTypesList; 109 110 /** Bitmask which represents list of clipboard formats which 111 * are being advertised either by host or guest depending 112 * on session type. */ 113 vbcl::Waitable<volatile SHCLFORMATS> fFmts; 114 115 /** Clipboard format which either host or guest wants to 116 * obtain depending on session type. */ 117 vbcl::Waitable<volatile SHCLFORMAT> uFmt; 118 119 /** Clipboard buffer which contains requested data. */ 120 vbcl::Waitable<volatile uint64_t> pvClipboardBuf; 121 122 /** Size of clipboard buffer. */ 123 vbcl::Waitable<volatile uint32_t> cbClipboardBuf; 124 } clip; 125 } vbox_wl_dcp_session_t; 126 127 /** 128 * A set of objects required to handle clipboard sharing over 129 * Data Control Protocol. */ 130 typedef struct 131 { 132 /** Wayland event loop thread. */ 133 RTTHREAD Thread; 134 135 /** A flag which indicates that Wayland event loop should terminate. */ 136 volatile bool fShutdown; 137 138 /** Communication session between host event loop and Wayland. */ 139 vbox_wl_dcp_session_t Session; 140 141 /** When set, incoming clipboard announcements will 142 * be ignored. This flag is used in order to prevent a feedback 143 * loop when host advertises clipboard data to Wayland. In this case, 144 * Wayland will send the same advertisements back to us. */ 145 bool fIngnoreWlClipIn; 146 147 /** A flag which indicates that host has announced new clipboard content 148 * and now Wayland event loop thread should pass this information to 149 * other Wayland clients. */ 150 vbcl::Waitable<volatile bool> fSendToGuest; 151 152 /** Connection handle to the host clipboard service. */ 153 PVBGLR3SHCLCMDCTX pClipboardCtx; 154 155 /** Wayland compositor connection object. */ 156 struct wl_display *pDisplay; 157 158 /** Wayland registry object. */ 159 struct wl_registry *pRegistry; 160 161 /** Wayland Seat object. */ 162 struct wl_seat *pSeat; 163 164 /** Wayland Data Device object. */ 165 struct zwlr_data_control_device_v1 *pDataDevice; 166 167 /** Wayland Data Control Manager object. */ 168 struct zwlr_data_control_manager_v1 *pDataControlManager; 169 } vbox_wl_dcp_ctx_t; 170 171 /** Data required to write clipboard content to Wayland. */ 172 struct vbcl_wl_dcp_write_ctx 173 { 174 /** Content mime-type in string representation. */ 175 const char *sMimeType; 176 /** Active file descriptor to write data into. */ 177 int32_t fd; 178 }; 179 180 /** Helper context. */ 181 static vbox_wl_dcp_ctx_t g_DcpCtx; 182 183 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_hg_clip_report_join2_cb(vbcl_wl_session_type_t enmSessionType, void *pvUser); 184 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_hg_clip_report_join3_cb(vbcl_wl_session_type_t enmSessionType, void *pvUser); 185 186 187 /********************************************************************************************************************************** 188 * Wayland low level operations. 189 *********************************************************************************************************************************/ 190 191 192 /** 193 * A helper function which reallocates buffer to bigger size. 194 * 195 * This function will attempt to re-allocate specified buffer by cbChunk bytes. 196 * If failed, caller is responsible for freeing input buffer. On success, output 197 * buffer must be freed by caller. 198 * 199 * @returns IPRT status code. 200 * @param pvBufIn Previously allocated buffer which size needs to be increased. 201 * @param cbBufIn Size of input buffer. 202 * @param cbChunk Amount of bytes by which caller wants to increase buffer size. 203 * @param cbMax Maximum size of output buffer. 204 * @param ppBufOut Output buffer (must be freed by caller). 205 * @param pcbBufOut Size of output buffer. 206 */ 207 RTDECL(int) vbcl_wayland_hlp_dcp_grow_buffer(void *pvBufIn, size_t cbBufIn, size_t cbChunk, size_t cbMax, 208 void **ppBufOut, size_t *pcbBufOut) 209 { 210 int rc = VERR_NO_MEMORY; 211 212 /* How many chunks were already added to the buffer. */ 213 int cChunks = cbBufIn / cbChunk; 214 /* Size of a chunk to be added to already allocated buffer. */ 215 size_t cbCurrentChunk = 0; 216 217 if (cbBufIn < cbMax) 218 { 219 void *pvBuf; 220 221 /* Calculate size of a chunk which can be added to already allocated memory 222 * in a way that resulting buffer size will not exceed cbMax. Always add 223 * the extra '\0' byte to the end of allocated area for safety reasons. */ 224 cbCurrentChunk = RT_MIN(cbMax, cbChunk * (cChunks + 1)) - cbBufIn + 1; 225 pvBuf = RTMemReallocZ(pvBufIn, cbBufIn, cbBufIn + cbCurrentChunk); 226 if (RT_VALID_PTR(pvBuf)) 227 { 228 LogRel(("Wayland: buffer size increased from %u to %u bytes\n", cbBufIn, cbBufIn + cbCurrentChunk)); 229 *ppBufOut = pvBuf; 230 *pcbBufOut = cbBufIn + cbCurrentChunk; 231 rc = VINF_SUCCESS; 232 } 233 else 234 { 235 LogRel(("Wayland: unable to allocate buffer of size of %u bytes: no memory\n", cbBufIn + cbCurrentChunk)); 236 rc = VERR_NO_MEMORY; 237 } 238 } 239 else 240 { 241 LogRel(("Shared Clipboard: unable to re-allocate buffer: size of %u bytes exceeded\n", cbMax)); 242 rc = VERR_BUFFER_OVERFLOW; 243 } 244 245 return rc; 246 } 247 248 /** 249 * A helper function for reading from file descriptor until EOF. 250 * 251 * Reads clipboard data from Wayland via file descriptor. 252 * 253 * @returns IPRT status code. 254 * @param fd A file descriptor to read data from. 255 * @param ppvBuf Newly allocated output buffer (must be freed by caller). 256 * @param pcbBuf Size of output buffer. 257 */ 258 RTDECL(int) vbcl_wayland_hlp_dcp_read_wl_fd(int fd, void **ppvBuf, size_t *pcbBuf) 259 { 260 int rc = VERR_NO_MEMORY; 261 262 struct timeval tv; 263 fd_set rfds; 264 265 /* Amount of payload actually read from Wayland fd in bytes. */ 266 size_t cbDst = 0; 267 /* Dynamically growing buffer to store Wayland clipboard. */ 268 void *pvDst = NULL; 269 /* Number of bytes currently allocated to read entire 270 * Wayland buffer content (actual size of pvDst). */ 271 size_t cbGrowingBuffer = 0; 272 /* Number of bytes read from Wayland fd per attempt. */ 273 size_t cbRead = 0; 274 275 /* Start with allocating one chunk and grow buffer later if needed. */ 276 cbGrowingBuffer = VBOX_WAYLAND_BUFFER_CHUNK_INC_SIZE + 1 /* '\0' */; 277 pvDst = RTMemAllocZ(cbGrowingBuffer); 278 if (RT_VALID_PTR(pvDst)) 279 { 280 /* Read everything from given fd. */ 281 while (1) 282 { 283 tv.tv_sec = 0; 284 tv.tv_usec = VBCL_WAYLAND_IO_TIMEOUT_MS * 1000; 285 286 FD_ZERO(&rfds); 287 FD_SET(fd, &rfds); 288 289 /* Wait until data is available. */ 290 if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0) 291 { 292 /* Check if backing buffer size is big enough to store one more data chunk 293 * read from fd. If not, try to increase buffer by size of chunk x 2. */ 294 if (cbDst + VBOX_WAYLAND_BUFFER_CHUNK_SIZE > cbGrowingBuffer) 295 { 296 void *pBufTmp = NULL; 297 298 rc = vbcl_wayland_hlp_dcp_grow_buffer( 299 pvDst, cbGrowingBuffer, VBOX_WAYLAND_BUFFER_CHUNK_INC_SIZE, 300 VBOX_WAYLAND_BUFFER_MAX, &pBufTmp, &cbGrowingBuffer); 301 302 if (RT_FAILURE(rc)) 303 { 304 RTMemFree(pvDst); 305 break; 306 } 307 else 308 pvDst = pBufTmp; 309 } 310 311 /* Read all data from fd until EOF. */ 312 cbRead = read(fd, (void *)((uint8_t *)pvDst + cbDst), VBOX_WAYLAND_BUFFER_CHUNK_SIZE); 313 if (cbRead > 0) 314 { 315 LogRel(("Wayland: read chunk of %u bytes from Wayland\n", cbRead)); 316 cbDst += cbRead; 317 } 318 else 319 { 320 /* EOF has been reached. */ 321 LogRel(("Wayland: read %u bytes from Wayland\n", cbDst)); 322 323 if (cbDst > 0) 324 { 325 rc = VINF_SUCCESS; 326 *ppvBuf = pvDst; 327 *pcbBuf = cbDst; 328 } 329 else 330 { 331 rc = VERR_NO_DATA; 332 RTMemFree(pvDst); 333 } 334 335 break; 336 } 337 } 338 else 339 { 340 rc = VERR_TIMEOUT; 341 break; 342 } 343 } 344 } 345 346 return rc; 347 } 348 349 /** 350 * A helper function for writing to a file descriptor provided by Wayland. 351 * 352 * @returns IPRT status code. 353 * @param fd A file descriptor to write data to. 354 * @param ppvBuf Data buffer. 355 * @param pcbBuf Size of data buffer. 356 */ 357 RTDECL(int) vbcl_wayland_hlp_dcp_write_wl_fd(int fd, void *pvBuf, size_t cbBuf) 358 { 359 struct timeval tv; 360 fd_set wfds; 361 362 int rc = VINF_SUCCESS; 363 364 tv.tv_sec = 0; 365 tv.tv_usec = VBCL_WAYLAND_IO_TIMEOUT_MS * 1000; 366 367 FD_ZERO(&wfds); 368 FD_SET(fd, &wfds); 369 370 /* Wait until data is available. */ 371 if (select(fd + 1, NULL, &wfds, NULL, &tv) > 0) 372 { 373 if (FD_ISSET(fd, &wfds)) 374 { 375 ssize_t cbWritten = write(fd, pvBuf, cbBuf); 376 if (cbWritten != (ssize_t)cbBuf) 377 { 378 VBClLogError("cannot write clipboard data, written %d out of %d bytes\n", 379 cbWritten, cbBuf); 380 rc = VERR_PIPE_NOT_CONNECTED; 381 } 382 else 383 VBClLogVerbose(5, "written %u bytes to Wayland clipboard\n", cbWritten); 384 } 385 else 386 { 387 VBClLogError("cannot write fd\n"); 388 rc = VERR_TIMEOUT; 389 } 390 } 391 else 392 rc = VERR_TIMEOUT; 393 394 return rc; 395 } 396 397 /** 398 * Read the next event from Wayland compositor. 399 * 400 * Implements custom reader function which can be interrupted 401 * on service termination request. 402 * 403 * @returns IPRT status code. 404 * @param pCtx Context data. 405 */ 406 static int vbcl_wayland_hlp_dcp_next_event(vbox_wl_dcp_ctx_t *pCtx) 407 { 408 int rc = VINF_SUCCESS; 409 410 struct timeval tv; 411 fd_set rfds, efds; 412 int fd; 413 414 /* Instead of using wl_display_dispatch() directly, implement 415 * custom event loop handling as recommended in Wayland documentation. 416 * Thus, we can have a control over Wayland fd polling and in turn 417 * can request event loop thread to shutdown when needed. */ 418 419 tv.tv_sec = 0; 420 tv.tv_usec = VBCL_WAYLAND_IO_TIMEOUT_MS * 1000; 421 422 fd = wl_display_get_fd(pCtx->pDisplay); 423 424 FD_ZERO(&rfds); 425 FD_SET(fd, &rfds); 426 427 FD_ZERO(&efds); 428 FD_SET(fd, &efds); 429 430 while (wl_display_prepare_read(pCtx->pDisplay) != 0) 431 wl_display_dispatch(pCtx->pDisplay); 432 433 wl_display_flush(pCtx->pDisplay); 434 435 if (select(fd + 1, &rfds, NULL, &efds, &tv) > 0) 436 wl_display_read_events(pCtx->pDisplay); 437 else 438 { 439 wl_display_cancel_read(pCtx->pDisplay); 440 rc = VERR_TIMEOUT; 441 } 442 443 wl_display_dispatch_pending(pCtx->pDisplay); 444 445 return rc; 446 } 447 448 449 /********************************************************************************************************************************** 450 * Host Clipboard service callbacks. 451 *********************************************************************************************************************************/ 452 453 454 /** 455 * Release session resources. 456 * 457 * @returns IPRT status code. 458 * @param pSession Session data. 459 */ 460 static void vbcl_wayland_hlp_dcp_session_release(vbox_wl_dcp_session_t *pSession) 461 { 462 void *pvData; 463 464 if (!RTListIsEmpty(&pSession->clip.mimeTypesList.Node)) 465 { 466 vbox_wl_dcp_mime_t *pEntry, *pNextEntry; 467 468 RTListForEachSafe(&pSession->clip.mimeTypesList.Node, pEntry, pNextEntry, vbox_wl_dcp_mime_t, Node) 469 { 470 RTListNodeRemove(&pEntry->Node); 471 RTStrFree(pEntry->pszMimeType); 472 RTMemFree(pEntry); 473 } 474 } 475 476 pvData = (void *)pSession->clip.pvClipboardBuf.reset(); 477 if (RT_VALID_PTR(pvData)) 478 RTMemFree(pvData); 479 } 480 481 /** 482 * Initialize session. 483 * 484 * @returns IPRT status code. 485 * @param pSession Session data. 486 */ 487 static void vbcl_wayland_hlp_dcp_session_init(vbox_wl_dcp_session_t *pSession) 488 { 489 RTListInit(&pSession->clip.mimeTypesList.Node); 490 491 pSession->clip.fFmts.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS); 492 pSession->clip.uFmt.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS); 493 pSession->clip.pvClipboardBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS); 494 pSession->clip.cbClipboardBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS); 495 } 496 497 /** 498 * Reset previously initialized session. 499 * 500 * @returns IPRT status code. 501 * @param pSession Session data. 502 */ 503 static void vbcl_wayland_hlp_dcp_session_prepare(vbox_wl_dcp_session_t *pSession) 504 { 505 vbcl_wayland_hlp_dcp_session_release(pSession); 506 vbcl_wayland_hlp_dcp_session_init(pSession); 507 #if 0 508 void *pvData; 509 510 if (!RTListIsEmpty(&pSession->clip.mimeTypesList.Node)) 511 { 512 vbox_wl_dcp_mime_t *pEntry, *pNextEntry; 513 514 RTListForEachSafe(&pSession->clip.mimeTypesList.Node, pEntry, pNextEntry, vbox_wl_dcp_mime_t, Node) 515 { 516 RTListNodeRemove(&pEntry->Node); 517 RTStrFree(pEntry->pszMimeType); 518 RTMemFree(pEntry); 519 } 520 } 521 522 pvData = (void *)pSession->clip.pvClipboardBuf.reset(); 523 if (RT_VALID_PTR(pvData)) 524 RTMemFree(pvData); 525 526 527 RTListInit(&pSession->clip.mimeTypesList.Node); 528 529 pSession->clip.fFmts.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS); 530 pSession->clip.uFmt.init(VBOX_SHCL_FMT_NONE, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS); 531 pSession->clip.pvClipboardBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS); 532 pSession->clip.cbClipboardBuf.init(0, VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS); 533 #endif 534 } 535 536 /** 537 * Session callback: Generic session initializer. 538 * 539 * This callback starts new session. 540 * 541 * @returns IPRT status code. 542 * @param enmSessionType Session type (unused). 543 * @param pvUser User data (unused). 544 */ 545 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_session_start_generic_cb( 546 vbcl_wl_session_type_t enmSessionType, void *pvUser) 547 { 548 RT_NOREF(enmSessionType, pvUser); 549 550 VBCL_LOG_CALLBACK; 551 552 vbcl_wayland_hlp_dcp_session_prepare(&g_DcpCtx.Session); 553 554 return VINF_SUCCESS; 555 } 556 557 /** 558 * Wayland registry global handler. 559 * 560 * This callback is triggered when Wayland Registry listener is registered. 561 * Wayland client library will trigger it individually for each available global 562 * object. 563 * 564 * @param pvUser Context data. 565 * @param pRegistry Wayland Registry object. 566 * @param uName Numeric name of the global object. 567 * @param sIface Name of interface implemented by the object. 568 * @param uVersion Interface version. 569 */ 570 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_registry_global_handler( 571 void *pvUser, struct wl_registry *pRegistry, uint32_t uName, const char *sIface, uint32_t uVersion) 572 { 573 vbox_wl_dcp_ctx_t *pCtx = (vbox_wl_dcp_ctx_t *)pvUser; 574 575 RT_NOREF(pRegistry); 576 RT_NOREF(uVersion); 577 578 AssertPtrReturnVoid(pCtx); 579 AssertPtrReturnVoid(sIface); 580 581 /* Wrappers around 'if' statement. */ 582 VBCL_WAYLAND_REGISTRY_ADD_MATCH(pRegistry, sIface, uName, wl_seat_interface, pCtx->pSeat, struct wl_seat *, VBCL_WAYLAND_SEAT_VERSION_MIN) 583 else VBCL_WAYLAND_REGISTRY_ADD_MATCH(pRegistry, sIface, uName, zwlr_data_control_manager_v1_interface, pCtx->pDataControlManager, struct zwlr_data_control_manager_v1 *, VBCL_WAYLAND_ZWLR_DATA_CONTROL_MANAGER_VERSION_MIN) 584 else 585 VBClLogVerbose(5, "ignoring Wayland interface %s\n", sIface); 586 } 587 588 /** 589 * Wayland registry global remove handler. 590 * 591 * Triggered when global object is removed from Wayland registry. 592 * 593 * @param pvUser Context data. 594 * @param pRegistry Wayland Registry object. 595 * @param uName Numeric name of the global object. 596 */ 597 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_registry_global_remove_handler( 598 void *pvUser, struct wl_registry *pRegistry, uint32_t uName) 599 { 600 RT_NOREF(pvUser); 601 RT_NOREF(pRegistry); 602 RT_NOREF(uName); 603 } 604 605 /** Wayland global registry callbacks. */ 606 static const struct wl_registry_listener g_vbcl_wayland_hlp_registry_cb = 607 { 608 vbcl_wayland_hlp_dcp_registry_global_handler, /* .global */ 609 vbcl_wayland_hlp_dcp_registry_global_remove_handler /* .global_remove */ 610 }; 611 612 613 614 /********************************************************************************************************************************** 615 * Wayland Data Control Offer callbacks. 616 *********************************************************************************************************************************/ 617 618 619 /** 620 * Session callback: Collect clipboard format advertised by guest. 621 * 622 * This callback must be executed in context of Wayland event thread 623 * in order to be able to access Wayland clipboard content. 624 * 625 * This callback adds mime-type just advertised by Wayland into a list 626 * of mime-types which in turn later will be advertised to the host. 627 * 628 * @returns IPRT status code. 629 * @param enmSessionType Session type, must be verified as 630 * a consistency check. 631 * @param pvUser User data (Wayland mime-type). 632 */ 633 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_gh_add_fmt_cb( 634 vbcl_wl_session_type_t enmSessionType, void *pvUser) 635 { 636 const char *sMimeType = (const char *)pvUser; 637 AssertPtrReturn(sMimeType, VERR_INVALID_PARAMETER); 638 639 SHCLFORMAT uFmt = VBoxMimeConvGetIdByMime(sMimeType); 640 641 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST) 642 ? VINF_SUCCESS : VERR_WRONG_ORDER; 643 644 VBCL_LOG_CALLBACK; 645 646 if (RT_SUCCESS(rc)) 647 { 648 if (uFmt != VBOX_SHCL_FMT_NONE) 649 { 650 vbox_wl_dcp_mime_t *pNode = (vbox_wl_dcp_mime_t *)RTMemAllocZ(sizeof(vbox_wl_dcp_mime_t)); 651 if (RT_VALID_PTR(pNode)) 652 { 653 pNode->pszMimeType = RTStrDup((char *)sMimeType); 654 if (RT_VALID_PTR(pNode->pszMimeType)) 655 RTListAppend(&g_DcpCtx.Session.clip.mimeTypesList.Node, &pNode->Node); 656 else 657 RTMemFree(pNode); 658 } 659 660 if ( !RT_VALID_PTR(pNode) 661 || !RT_VALID_PTR(pNode->pszMimeType)) 662 { 663 rc = VERR_NO_MEMORY; 664 } 665 } 666 else 667 rc = VERR_NO_DATA; 668 } 669 670 return rc; 671 } 672 673 674 /** 675 * Data Control Offer advertise callback. 676 * 677 * Triggered when other Wayland client advertises new clipboard content. 678 * 679 * @param pvUser Context data. 680 * @param pOffer Wayland Data Control Offer object. 681 * @param sMimeType Mime-type of newly available clipboard data. 682 */ 683 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_control_offer_offer( 684 void *pvUser, struct zwlr_data_control_offer_v1 *pOffer, const char *sMimeType) 685 { 686 vbox_wl_dcp_ctx_t *pCtx = (vbox_wl_dcp_ctx_t *)pvUser; 687 int rc; 688 689 RT_NOREF(pOffer); 690 691 VBCL_LOG_CALLBACK; 692 693 rc = vbcl_wayland_session_join(&pCtx->Session.Base, 694 &vbcl_wayland_hlp_dcp_gh_add_fmt_cb, 695 (void *)sMimeType); 696 if (RT_FAILURE(rc)) 697 VBClLogError("cannot save formats announced by the guest, rc=%Rrc\n", rc); 698 } 699 700 /** Wayland Data Control Offer interface callbacks. */ 701 static const struct zwlr_data_control_offer_v1_listener g_data_control_offer_listener = 702 { 703 vbcl_wayland_hlp_dcp_data_control_offer_offer, 704 }; 705 706 707 /********************************************************************************************************************************** 708 * Wayland Data Control Device callbacks. 709 *********************************************************************************************************************************/ 710 711 712 /** 713 * Convert list of mime-types in string representation into bitmask of VBox formats. 714 * 715 * @returns Formats bitmask. 716 * @param pList List of mime-types in string representation. 717 */ 718 static SHCLFORMATS vbcl_wayland_hlp_dcp_match_formats(vbox_wl_dcp_mime_t *pList) 719 { 720 SHCLFORMATS fFmts = VBOX_SHCL_FMT_NONE; 721 722 if (!RTListIsEmpty(&pList->Node)) 723 { 724 vbox_wl_dcp_mime_t *pEntry; 725 RTListForEach(&pList->Node, pEntry, vbox_wl_dcp_mime_t, Node) 726 { 727 AssertPtrReturn(pEntry, VERR_INVALID_PARAMETER); 728 AssertPtrReturn(pEntry->pszMimeType, VERR_INVALID_PARAMETER); 729 730 fFmts |= VBoxMimeConvGetIdByMime(pEntry->pszMimeType); 731 } 732 } 733 734 return fFmts; 735 } 736 737 /** 738 * Find first matching clipboard mime-type for given format ID. 739 * 740 * @returns Matching mime-type in string representation or NULL if not found. 741 * @param uFmt Format in VBox representation to match. 742 * @param pList List of Wayland mime-types in string representation. 743 */ 744 static char *vbcl_wayland_hlp_dcp_match_mime_type(SHCLFORMAT uFmt, vbox_wl_dcp_mime_t *pList) 745 { 746 char *pszMimeType = NULL; 747 748 if (!RTListIsEmpty(&pList->Node)) 749 { 750 vbox_wl_dcp_mime_t *pEntry; 751 RTListForEach(&pList->Node, pEntry, vbox_wl_dcp_mime_t, Node) 752 { 753 AssertPtrReturn(pEntry, NULL); 754 AssertPtrReturn(pEntry->pszMimeType, NULL); 755 756 if (uFmt == VBoxMimeConvGetIdByMime(pEntry->pszMimeType)) 757 { 758 pszMimeType = pEntry->pszMimeType; 759 break; 760 } 761 } 762 } 763 764 return pszMimeType; 765 } 766 767 /** 768 * Read clipboard buffer from Wayland in specified format. 769 * 770 * @returns IPRT status code. 771 * @param pCtx DCP context data. 772 * @param pOffer Data offer object. 773 * @param pszMimeType Requested mime-type in string representation. 774 */ 775 static int vbcl_wayland_hlp_dcp_receive_offer( 776 vbox_wl_dcp_ctx_t *pCtx, zwlr_data_control_offer_v1 *pOffer, SHCLFORMAT uFmt, char *pszMimeType) 777 { 778 int rc = VERR_PIPE_NOT_CONNECTED; 779 780 int aFds[2]; 781 void *pvBuf = NULL; 782 size_t cbBuf = 0; 783 784 RT_NOREF(uFmt); 785 786 if (pipe(aFds) == 0) 787 { 788 zwlr_data_control_offer_v1_receive( 789 (struct zwlr_data_control_offer_v1 *)pOffer, pszMimeType, aFds[1]); 790 791 close(aFds[1]); 792 wl_display_flush(pCtx->pDisplay); 793 794 rc = vbcl_wayland_hlp_dcp_read_wl_fd(aFds[0], &pvBuf, &cbBuf); 795 if (RT_SUCCESS(rc)) 796 { 797 void *pvBufOut = NULL; 798 size_t cbBufOut = 0; 799 800 rc = VBoxMimeConvNativeToVBox(pszMimeType, pvBuf, cbBuf, &pvBufOut, &cbBufOut); 801 if (RT_SUCCESS(rc)) 802 { 803 pCtx->Session.clip.pvClipboardBuf.set((uint64_t)pvBufOut); 804 pCtx->Session.clip.cbClipboardBuf.set((uint64_t)cbBufOut); 805 } 806 807 RTMemFree(pvBuf); 808 } 809 } 810 else 811 VBClLogError("cannot read mime-type '%s' from Wayland, rc=%Rrc\n", pszMimeType, rc); 812 813 return rc; 814 } 815 816 /** 817 * Session callback: Advertise clipboard to the host. 818 * 819 * This callback must be executed in context of Wayland event thread 820 * in order to be able to access Wayland clipboard content. 821 * 822 * This callback (1) coverts Wayland clipboard formats into VBox 823 * representation, (2) sets formats to the session, (3) waits for 824 * host to request clipboard data in certain format, and (4) 825 * receives Wayland clipboard in requested format. 826 * 827 * @returns IPRT status code. 828 * @param enmSessionType Session type, must be verified as 829 * a consistency check. 830 * @param pvUser User data (data offer object). 831 */ 832 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_gh_clip_report_cb( 833 vbcl_wl_session_type_t enmSessionType, void *pvUser) 834 { 835 struct zwlr_data_control_offer_v1 *pOffer = (struct zwlr_data_control_offer_v1 *)pvUser; 836 SHCLFORMATS fFmts = VBOX_SHCL_FMT_NONE; 837 838 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST) 839 ? VINF_SUCCESS : VERR_WRONG_ORDER; 840 841 AssertPtrReturn(pOffer, VERR_INVALID_PARAMETER); 842 843 VBCL_LOG_CALLBACK; 844 845 if (RT_SUCCESS(rc)) 846 { 847 fFmts = vbcl_wayland_hlp_dcp_match_formats(&g_DcpCtx.Session.clip.mimeTypesList); 848 if (fFmts != VBOX_SHCL_FMT_NONE) 849 { 850 SHCLFORMAT uFmt; 851 852 g_DcpCtx.Session.clip.fFmts.set(fFmts); 853 854 if (RT_VALID_PTR(g_DcpCtx.pClipboardCtx)) 855 { 856 rc = VbglR3ClipboardReportFormats(g_DcpCtx.pClipboardCtx->idClient, fFmts); 857 if (RT_SUCCESS(rc)) 858 { 859 uFmt = g_DcpCtx.Session.clip.uFmt.wait(); 860 if (uFmt != g_DcpCtx.Session.clip.uFmt.defaults()) 861 { 862 char *pszMimeType = 863 vbcl_wayland_hlp_dcp_match_mime_type(uFmt, &g_DcpCtx.Session.clip.mimeTypesList); 864 865 if (RT_VALID_PTR(pszMimeType)) 866 { 867 rc = vbcl_wayland_hlp_dcp_receive_offer(&g_DcpCtx, pOffer, uFmt, pszMimeType); 868 869 VBClLogVerbose(5, "will send fmt=0x%x (%s) to the host\n", uFmt, pszMimeType); 870 } 871 else 872 rc = VERR_NO_DATA; 873 } 874 else 875 rc = VERR_TIMEOUT; 876 } 877 else 878 VBClLogError("cannot report formats to host, rc=%Rrc\n", rc); 879 } 880 else 881 { 882 VBClLogVerbose(2, "cannot announce to guest, no host service connection yet\n"); 883 rc = VERR_TRY_AGAIN; 884 } 885 } 886 else 887 rc = VERR_NO_DATA; 888 889 zwlr_data_control_offer_v1_destroy((struct zwlr_data_control_offer_v1 *)pOffer); 890 891 VBClLogVerbose(5, "announcing fFmts=0x%x to host, rc=%Rrc\n", fFmts, rc); 892 } 893 894 return rc; 895 } 896 897 /** 898 * Data Control Device offer callback. 899 * 900 * Triggered when other Wayland client advertises new clipboard content. 901 * When this callback is triggered, a new zwlr_data_control_offer_v1 object 902 * is created. This callback should setup listener callbacks for this object. 903 * 904 * @param pvUser Context data. 905 * @param pDevice Wayland Data Control Device object. 906 * @param pOffer Wayland Data Control Offer object. 907 */ 908 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_device_data_offer( 909 void *pvUser, struct zwlr_data_control_device_v1 *pDevice, struct zwlr_data_control_offer_v1 *pOffer) 910 { 911 vbox_wl_dcp_ctx_t *pCtx = (vbox_wl_dcp_ctx_t *)pvUser; 912 int rc; 913 914 RT_NOREF(pDevice); 915 916 VBCL_LOG_CALLBACK; 917 918 if (pCtx->fIngnoreWlClipIn) 919 { 920 VBClLogVerbose(5, "ignoring Wayland clipboard data offer, we advertising new clipboard ourselves\n"); 921 return; 922 } 923 924 rc = vbcl_wayland_session_end(&pCtx->Session.Base, NULL, NULL); 925 if (RT_SUCCESS(rc)) 926 { 927 rc = vbcl_wayland_session_start(&pCtx->Session.Base, 928 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST, 929 &vbcl_wayland_hlp_dcp_session_start_generic_cb, 930 &pCtx->Session); 931 if (RT_SUCCESS(rc)) 932 { 933 zwlr_data_control_offer_v1_add_listener(pOffer, &g_data_control_offer_listener, pvUser); 934 935 /* Receive all the advertised mime types. */ 936 wl_display_roundtrip(pCtx->pDisplay); 937 938 /* Try to send an announcement to the host. */ 939 rc = vbcl_wayland_session_join(&pCtx->Session.Base, 940 &vbcl_wayland_hlp_dcp_gh_clip_report_cb, 941 pOffer); 942 } 943 else 944 VBClLogError("unable to start session, rc=%Rrc\n", rc); 945 } 946 else 947 VBClLogError("unable to start session, previous session is still running, rc=%Rrc\n", rc); 948 } 949 950 /** 951 * Data Control Device selection callback. 952 * 953 * Triggered when Wayland client advertises new clipboard content. 954 * In this callback, actual clipboard data is received from Wayland client. 955 * 956 * @param pvUser Context data. 957 * @param pDevice Wayland Data Control Device object. 958 * @param pOffer Wayland Data Control Offer object. 959 */ 960 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_device_selection( 961 void *pvUser, struct zwlr_data_control_device_v1 *pDevice, struct zwlr_data_control_offer_v1 *pOffer) 962 { 963 RT_NOREF(pDevice, pvUser, pOffer); 964 965 VBCL_LOG_CALLBACK; 966 } 967 968 /** 969 * Data Control Device finished callback. 970 * 971 * Triggered when Data Control Device object is no longer valid and 972 * needs to be destroyed. 973 * 974 * @param pvUser Context data. 975 * @param pDevice Wayland Data Control Device object. 976 */ 977 static void vbcl_wayland_hlp_dcp_data_device_finished( 978 void *data, struct zwlr_data_control_device_v1 *pDevice) 979 { 980 RT_NOREF(data); 981 982 VBCL_LOG_CALLBACK; 983 984 zwlr_data_control_device_v1_destroy(pDevice); 985 } 986 987 /** 988 * Data Control Device primary selection callback. 989 * 990 * Same as shcl_wl_data_control_device_selection, but triggered for 991 * primary selection case. 992 * 993 * @param pvUser Context data. 994 * @param pDevice Wayland Data Control Device object. 995 * @param pOffer Wayland Data Control Offer object. 996 */ 997 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_device_primary_selection( 998 void *pvUser, struct zwlr_data_control_device_v1 *pDevice, struct zwlr_data_control_offer_v1 *pOffer) 999 { 1000 RT_NOREF(pDevice, pvUser, pOffer); 1001 1002 VBCL_LOG_CALLBACK; 1003 } 1004 1005 1006 /** Data Control Device interface callbacks. */ 1007 static const struct zwlr_data_control_device_v1_listener g_data_device_listener = 1008 { 1009 vbcl_wayland_hlp_dcp_data_device_data_offer, 1010 vbcl_wayland_hlp_dcp_data_device_selection, 1011 vbcl_wayland_hlp_dcp_data_device_finished, 1012 vbcl_wayland_hlp_dcp_data_device_primary_selection, 1013 }; 1014 1015 1016 /********************************************************************************************************************************** 1017 * Wayland Data Control Source callbacks. 1018 *********************************************************************************************************************************/ 1019 1020 /** 1021 * Wayland data send callback. 1022 * 1023 * Triggered when other Wayland client wants to read clipboard 1024 * data from us. 1025 * 1026 * @param pvUser VBox private data. 1027 * @param pSourceSource Wayland Data Control Source object. 1028 * @param sMimeType A mime-type of requested data. 1029 * @param fd A file descriptor to write clipboard content into. 1030 */ 1031 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_source_send( 1032 void *pvUser, struct zwlr_data_control_source_v1 *pDataSource, 1033 const char *sMimeType, int32_t fd) 1034 { 1035 vbox_wl_dcp_ctx_t *pCtx = (vbox_wl_dcp_ctx_t *)pvUser; 1036 int rc; 1037 1038 struct vbcl_wl_dcp_write_ctx priv; 1039 1040 RT_NOREF(pDataSource); 1041 1042 VBCL_LOG_CALLBACK; 1043 1044 RT_ZERO(priv); 1045 1046 priv.sMimeType = sMimeType; 1047 priv.fd = fd; 1048 1049 rc = vbcl_wayland_session_join(&pCtx->Session.Base, 1050 &vbcl_wayland_hlp_dcp_hg_clip_report_join3_cb, 1051 &priv); 1052 1053 VBClLogVerbose(5, "vbcl_wayland_hlp_dcp_data_source_send, rc=%Rrc\n", rc); 1054 close(fd); 1055 } 1056 1057 /** 1058 * Wayland data canceled callback. 1059 * 1060 * Triggered when data source was replaced by another data source 1061 * and no longer valid. 1062 * 1063 * @param pvData VBox private data. 1064 * @param pDataSource Wayland Data Control Source object. 1065 */ 1066 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_data_source_cancelled( 1067 void *pvData, struct zwlr_data_control_source_v1 *pDataSource) 1068 { 1069 RT_NOREF(pvData); 1070 1071 VBCL_LOG_CALLBACK; 1072 1073 zwlr_data_control_source_v1_destroy(pDataSource); 1074 } 1075 1076 1077 /** Wayland Data Control Source interface callbacks. */ 1078 static const struct zwlr_data_control_source_v1_listener g_data_source_listener = 1079 { 1080 vbcl_wayland_hlp_dcp_data_source_send, 1081 vbcl_wayland_hlp_dcp_data_source_cancelled, 1082 }; 1083 1084 1085 /********************************************************************************************************************************** 1086 * Helper specific code and session callbacks. 1087 *********************************************************************************************************************************/ 1088 1089 1090 /** 1091 * Setup or reset helper context. 1092 * 1093 * This function is used on helper init and termination. In case of 1094 * init, memory is not initialized yet, so it needs to be zeroed. 1095 * In case of shutdown, memory is already initialized and previously 1096 * allocated resources must be freed. 1097 * 1098 * @param pCtx Context data. 1099 * @param fShutdown A flag to indicate if session resources 1100 * need to be deallocated. 1101 */ 1102 static void vbcl_wayland_hlp_dcp_reset_ctx(vbox_wl_dcp_ctx_t *pCtx, bool fShutdown) 1103 { 1104 pCtx->Thread = NIL_RTTHREAD; 1105 pCtx->fShutdown = false; 1106 pCtx->fIngnoreWlClipIn = false; 1107 pCtx->fSendToGuest.init(false, VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS); 1108 pCtx->pClipboardCtx = NULL; 1109 pCtx->pDisplay = NULL; 1110 pCtx->pRegistry = NULL; 1111 pCtx->pSeat = NULL; 1112 pCtx->pDataDevice = NULL; 1113 pCtx->pDataControlManager = NULL; 1114 1115 if (fShutdown) 1116 vbcl_wayland_hlp_dcp_session_release(&pCtx->Session); 1117 1118 vbcl_wayland_hlp_dcp_session_init(&pCtx->Session); 1119 } 1120 1121 /** 1122 * Disconnect from Wayland compositor. 1123 * 1124 * Close connection, release resources and reset context data. 1125 * 1126 * @param pCtx Context data. 1127 */ 1128 static void vbcl_wayland_hlp_dcp_disconnect(vbox_wl_dcp_ctx_t *pCtx) 1129 { 1130 if (RT_VALID_PTR(pCtx->pDataControlManager)) 1131 zwlr_data_control_manager_v1_destroy(pCtx->pDataControlManager); 1132 1133 if (RT_VALID_PTR(pCtx->pDataDevice)) 1134 zwlr_data_control_device_v1_destroy(pCtx->pDataDevice); 1135 1136 if (RT_VALID_PTR(pCtx->pSeat)) 1137 wl_seat_destroy(pCtx->pSeat); 1138 1139 if (RT_VALID_PTR(pCtx->pRegistry)) 1140 wl_registry_destroy(pCtx->pRegistry); 1141 1142 if (RT_VALID_PTR(pCtx->pDisplay)) 1143 wl_display_disconnect(pCtx->pDisplay); 1144 1145 vbcl_wayland_hlp_dcp_reset_ctx(pCtx, true); 1146 } 1147 1148 /** 1149 * Connect to Wayland compositor. 1150 * 1151 * Establish connection, bind to all required interfaces. 1152 * 1153 * @returns TRUE on success, FALSE otherwise. 1154 * @param pCtx Context data. 1155 */ 1156 static bool vbcl_wayland_hlp_dcp_connect(vbox_wl_dcp_ctx_t *pCtx) 1157 { 1158 const char *csWaylandDisplay = RTEnvGet(VBCL_ENV_WAYLAND_DISPLAY); 1159 bool fConnected = false; 1160 1161 if (RT_VALID_PTR(csWaylandDisplay)) 1162 pCtx->pDisplay = wl_display_connect(csWaylandDisplay); 1163 else 1164 VBClLogError("cannot connect to Wayland compositor " 1165 VBCL_ENV_WAYLAND_DISPLAY " environment variable not set\n"); 1166 1167 if (RT_VALID_PTR(pCtx->pDisplay)) 1168 { 1169 pCtx->pRegistry = wl_display_get_registry(pCtx->pDisplay); 1170 if (RT_VALID_PTR(pCtx->pRegistry)) 1171 { 1172 wl_registry_add_listener(pCtx->pRegistry, &g_vbcl_wayland_hlp_registry_cb, (void *)pCtx); 1173 wl_display_roundtrip(pCtx->pDisplay); 1174 1175 if (RT_VALID_PTR(pCtx->pDataControlManager)) 1176 { 1177 if (RT_VALID_PTR(pCtx->pSeat)) 1178 { 1179 pCtx->pDataDevice = zwlr_data_control_manager_v1_get_data_device(pCtx->pDataControlManager, pCtx->pSeat); 1180 if (RT_VALID_PTR(pCtx->pDataDevice)) 1181 { 1182 if (RT_VALID_PTR(pCtx->pDataControlManager)) 1183 fConnected = true; 1184 else 1185 VBClLogError("cannot get Wayland data control manager interface\n"); 1186 } 1187 else 1188 VBClLogError("cannot get Wayland data device interface\n"); 1189 } 1190 else 1191 VBClLogError("cannot get Wayland seat interface\n"); 1192 } 1193 else 1194 VBClLogError("cannot get Wayland device manager interface\n"); 1195 } 1196 else 1197 VBClLogError("cannot connect to Wayland registry\n"); 1198 } 1199 else 1200 VBClLogError("cannot connect to Wayland compositor\n"); 1201 1202 if (!fConnected) 1203 vbcl_wayland_hlp_dcp_disconnect(pCtx); 1204 1205 return fConnected; 1206 } 1207 1208 1209 /** 1210 * Main loop for Wayland compositor events. 1211 * 1212 * All requests to Wayland compositor must be performed in context 1213 * of this thread. 1214 * 1215 * @returns IPRT status code. 1216 * @param hThreadSelf IPRT thread object. 1217 * @param pvUser Context data. 1218 */ 1219 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_event_loop(RTTHREAD hThreadSelf, void *pvUser) 1220 { 1221 vbox_wl_dcp_ctx_t *pCtx = (vbox_wl_dcp_ctx_t *)pvUser; 1222 int rc = VERR_TRY_AGAIN; 1223 1224 if (vbcl_wayland_hlp_dcp_connect(pCtx)) 1225 { 1226 /* Start listening Data Control Device interface. */ 1227 if (zwlr_data_control_device_v1_add_listener(pCtx->pDataDevice, &g_data_device_listener, (void *)pCtx) == 0) 1228 { 1229 /* Tell parent thread we are ready. */ 1230 RTThreadUserSignal(hThreadSelf); 1231 1232 while (1) 1233 { 1234 rc = vbcl_wayland_hlp_dcp_next_event(pCtx); 1235 if ( rc != VERR_TIMEOUT 1236 && RT_FAILURE(rc)) 1237 { 1238 VBClLogError("cannot read event from Wayland, rc=%Rrc\n", rc); 1239 } 1240 1241 if (pCtx->fSendToGuest.reset()) 1242 { 1243 rc = vbcl_wayland_session_join(&pCtx->Session.Base, 1244 &vbcl_wayland_hlp_dcp_hg_clip_report_join2_cb, 1245 NULL); 1246 } 1247 1248 /* Handle graceful thread termination. */ 1249 if (pCtx->fShutdown) 1250 { 1251 rc = VINF_SUCCESS; 1252 break; 1253 } 1254 } 1255 } 1256 else 1257 { 1258 rc = VERR_NOT_SUPPORTED; 1259 VBClLogError("cannot subscribe to Data Control Device events\n"); 1260 } 1261 1262 vbcl_wayland_hlp_dcp_disconnect(pCtx); 1263 } 1264 1265 /* Notify parent thread if we failed to start, so it won't be 1266 * waiting 30 sec to figure this out. */ 1267 if (RT_FAILURE(rc)) 1268 RTThreadUserSignal(hThreadSelf); 1269 1270 return rc; 1271 } 29 1272 30 1273 /** … … 33 1276 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_probe(void) 34 1277 { 35 return VBOX_WAYLAND_HELPER_CAP_NONE; 1278 vbox_wl_dcp_ctx_t probeCtx; 1279 int fCaps = VBOX_WAYLAND_HELPER_CAP_NONE; 1280 VBGHDISPLAYSERVERTYPE enmDisplayServerType = VBGHDisplayServerTypeDetect(); 1281 1282 vbcl_wayland_hlp_dcp_reset_ctx(&probeCtx, false /* fShutdown */); 1283 vbcl_wayland_session_init(&probeCtx.Session.Base); 1284 1285 if (VBGHDisplayServerTypeIsWaylandAvailable(enmDisplayServerType)) 1286 { 1287 if (vbcl_wayland_hlp_dcp_connect(&probeCtx)) 1288 { 1289 fCaps |= VBOX_WAYLAND_HELPER_CAP_CLIPBOARD; 1290 vbcl_wayland_hlp_dcp_disconnect(&probeCtx); 1291 } 1292 } 1293 1294 return fCaps; 36 1295 } 37 1296 … … 41 1300 RTDECL(int) vbcl_wayland_hlp_dcp_init(void) 42 1301 { 43 return VERR_NOT_SUPPORTED; 1302 vbcl_wayland_hlp_dcp_reset_ctx(&g_DcpCtx, false /* fShutdown */); 1303 vbcl_wayland_session_init(&g_DcpCtx.Session.Base); 1304 1305 return VBClClipboardThreadStart(&g_DcpCtx.Thread, vbcl_wayland_hlp_dcp_event_loop, "wl-dcp", &g_DcpCtx); 44 1306 } 45 1307 … … 49 1311 RTDECL(int) vbcl_wayland_hlp_dcp_term(void) 50 1312 { 51 return VERR_NOT_SUPPORTED; 1313 int rc; 1314 int rcThread = 0; 1315 1316 /* Set termination flag. Wayland event loop should pick it up 1317 * on the next iteration. */ 1318 g_DcpCtx.fShutdown = true; 1319 1320 /* Wait for Wayland event loop thread to shutdown. */ 1321 rc = RTThreadWait(g_DcpCtx.Thread, RT_MS_30SEC, &rcThread); 1322 if (RT_SUCCESS(rc)) 1323 VBClLogInfo("Wayland event thread exited with status, rc=%Rrc\n", rcThread); 1324 else 1325 VBClLogError("unable to stop Wayland event thread, rc=%Rrc\n", rc); 1326 1327 return rc; 1328 } 1329 1330 /** 1331 * @interface_method_impl{VBCLWAYLANDHELPER,pfnSetClipboardCtx} 1332 */ 1333 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_set_clipboard_ctx(PVBGLR3SHCLCMDCTX pCtx) 1334 { 1335 g_DcpCtx.pClipboardCtx = pCtx; 1336 } 1337 1338 /** 1339 * @interface_method_impl{VBCLWAYLANDHELPER,pfnPopup} 1340 */ 1341 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_popup(void) 1342 { 1343 return VINF_SUCCESS; 1344 } 1345 1346 /** 1347 * Session callback: Copy clipboard to the guest. 1348 * 1349 * This callback must be executed in context of Wayland event thread 1350 * in order to be able to inject clipboard content into Wayland. It is 1351 * triggered when Wayland client already decided data in which format 1352 * it wants to request. 1353 * 1354 * This callback (1) sets requested clipboard format to the session, 1355 * (2) waits for clipboard data to be copied from the host, (3) converts 1356 * host clipboard data into guest representation, and (4) sends clipboard 1357 * to the guest by writing given file descriptor. 1358 * 1359 * @returns IPRT status code. 1360 * @param enmSessionType Session type, must be verified as 1361 * a consistency check. 1362 * @param pvUser User data (Wayland I/O context). 1363 */ 1364 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_hg_clip_report_join3_cb( 1365 vbcl_wl_session_type_t enmSessionType, void *pvUser) 1366 { 1367 struct vbcl_wl_dcp_write_ctx *pPriv = (struct vbcl_wl_dcp_write_ctx *)pvUser; 1368 AssertPtrReturn(pPriv, VERR_INVALID_PARAMETER); 1369 1370 void *pvBuf; 1371 uint32_t cbBuf; 1372 1373 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 1374 ? VINF_SUCCESS : VERR_WRONG_ORDER; 1375 1376 VBCL_LOG_CALLBACK; 1377 1378 if (RT_SUCCESS(rc)) 1379 { 1380 if (RT_VALID_PTR(g_DcpCtx.pClipboardCtx)) 1381 { 1382 /* Set requested format to the session. */ 1383 g_DcpCtx.Session.clip.uFmt.set(VBoxMimeConvGetIdByMime(pPriv->sMimeType)); 1384 1385 /* Wait for data in requested format. */ 1386 pvBuf = (void *)g_DcpCtx.Session.clip.pvClipboardBuf.wait(); 1387 cbBuf = g_DcpCtx.Session.clip.cbClipboardBuf.wait(); 1388 if ( cbBuf != g_DcpCtx.Session.clip.cbClipboardBuf.defaults() 1389 && pvBuf != (void *)g_DcpCtx.Session.clip.pvClipboardBuf.defaults()) 1390 { 1391 void *pvBufOut; 1392 size_t cbOut; 1393 1394 /* Convert clipboard data from VBox representation into guest format. */ 1395 rc = VBoxMimeConvVBoxToNative(pPriv->sMimeType, pvBuf, cbBuf, &pvBufOut, &cbOut); 1396 if (RT_SUCCESS(rc)) 1397 { 1398 rc = vbcl_wayland_hlp_dcp_write_wl_fd(pPriv->fd, pvBufOut, cbOut); 1399 RTMemFree(pvBufOut); 1400 } 1401 else 1402 VBClLogError("cannot convert '%s' to native format, rc=%Rrc\n", rc); 1403 } 1404 else 1405 rc = VERR_TIMEOUT; 1406 } 1407 else 1408 { 1409 VBClLogVerbose(2, "cannot send to guest, no host service connection yet\n"); 1410 rc = VERR_TRY_AGAIN; 1411 } 1412 1413 g_DcpCtx.fIngnoreWlClipIn = false; 1414 } 1415 1416 return rc; 1417 } 1418 1419 /** 1420 * Enumeration callback used for sending clipboard offers to Wayland client. 1421 * 1422 * When host announces its clipboard content, this call back is used in order 1423 * to send corresponding offers to other Wayland clients. 1424 * 1425 * Callback must be executed in context of Wayland event thread. 1426 * 1427 * @param pcszMimeType Mime-type to advertise. 1428 * @param pvUser User data (DCP data source object). 1429 */ 1430 static DECLCALLBACK(void) vbcl_wayland_hlp_dcp_send_offers(const char *pcszMimeType, void *pvUser) 1431 { 1432 zwlr_data_control_source_v1 *pDataSource = (zwlr_data_control_source_v1 *)pvUser; 1433 zwlr_data_control_source_v1_offer(pDataSource, pcszMimeType); 1434 } 1435 1436 /** 1437 * Session callback: Advertise clipboard to the guest. 1438 * 1439 * This callback must be executed in context of Wayland event thread 1440 * in order to be able to inject clipboard content into Wayland. 1441 * 1442 * This callback (1) prevents Wayland event loop from processing 1443 * incoming clipboard advertisements before sending any data to 1444 * other Wayland clients (this is needed in order to avoid feedback 1445 * loop from our own advertisements), (2) waits for the list of clipboard 1446 * formats available on the host side (set by vbcl_wayland_hlp_dcp_hg_clip_report_join_cb), 1447 * and (3) sends data offers for available host clipboard to other clients. 1448 * 1449 * @returns IPRT status code. 1450 * @param enmSessionType Session type, must be verified as 1451 * a consistency check. 1452 * @param pvUser User data (unused). 1453 */ 1454 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_hg_clip_report_join2_cb( 1455 vbcl_wl_session_type_t enmSessionType, void *pvUser) 1456 { 1457 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 1458 ? VINF_SUCCESS : VERR_WRONG_ORDER; 1459 1460 RT_NOREF(pvUser); 1461 1462 VBCL_LOG_CALLBACK; 1463 1464 if (RT_SUCCESS(rc)) 1465 { 1466 g_DcpCtx.fIngnoreWlClipIn = true; 1467 1468 SHCLFORMATS fFmts = g_DcpCtx.Session.clip.fFmts.wait(); 1469 if (fFmts != g_DcpCtx.Session.clip.fFmts.defaults()) 1470 { 1471 zwlr_data_control_source_v1 *pDataSource = 1472 zwlr_data_control_manager_v1_create_data_source(g_DcpCtx.pDataControlManager); 1473 1474 if (RT_VALID_PTR(pDataSource)) 1475 { 1476 zwlr_data_control_source_v1_add_listener( 1477 (struct zwlr_data_control_source_v1 *)pDataSource, &g_data_source_listener, &g_DcpCtx); 1478 1479 VBoxMimeConvEnumerateMimeById(fFmts, 1480 vbcl_wayland_hlp_dcp_send_offers, 1481 pDataSource); 1482 1483 zwlr_data_control_device_v1_set_selection(g_DcpCtx.pDataDevice, pDataSource); 1484 } 1485 else 1486 rc = VERR_NO_MEMORY; 1487 } 1488 else 1489 rc = VERR_NO_DATA; 1490 } 1491 1492 return rc; 1493 } 1494 1495 /** 1496 * Session callback: Copy clipboard from the host. 1497 * 1498 * This callback (1) sets host clipboard formats list to the session, 1499 * (2) asks Wayland event thread to advertise these formats to the guest, 1500 * (3) waits for guest to request clipboard in specific format, (4) read 1501 * host clipboard in this format, and (5) sets clipboard data to the session, 1502 * so Wayland events thread can inject it into the guest. 1503 * 1504 * This callback should not return until clipboard data is read from 1505 * the host or error occurred. It must block host events loop until 1506 * current host event is fully processed. 1507 * 1508 * @returns IPRT status code. 1509 * @param enmSessionType Session type, must be verified as 1510 * a consistency check. 1511 * @param pvUser User data (host clipboard formats). 1512 */ 1513 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_hg_clip_report_join_cb( 1514 vbcl_wl_session_type_t enmSessionType, void *pvUser) 1515 { 1516 SHCLFORMATS *pfFmts = (SHCLFORMATS *)pvUser; 1517 AssertPtrReturn(pfFmts, VERR_INVALID_PARAMETER); 1518 1519 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 1520 ? VINF_SUCCESS : VERR_WRONG_ORDER; 1521 1522 VBCL_LOG_CALLBACK; 1523 1524 if (RT_SUCCESS(rc)) 1525 { 1526 SHCLFORMAT uFmt; 1527 void *pvData; 1528 uint32_t cbData; 1529 1530 /* Set list of host clipboard formats to the session. */ 1531 g_DcpCtx.Session.clip.fFmts.set(*pfFmts); 1532 1533 /* Ask Wayland event thread to advertise formats to the guest. */ 1534 g_DcpCtx.fSendToGuest.set(true); 1535 RTThreadPoke(g_DcpCtx.Thread); 1536 1537 /* Wait for the guest to request certain clipboard format. */ 1538 uFmt = g_DcpCtx.Session.clip.uFmt.wait(); 1539 if (uFmt != g_DcpCtx.Session.clip.uFmt.defaults()) 1540 { 1541 /* Read host clipboard in specified format. */ 1542 rc = VBClClipboardReadHostClipboard(g_DcpCtx.pClipboardCtx, uFmt, &pvData, &cbData); 1543 if (RT_SUCCESS(rc)) 1544 { 1545 /* Set clipboard data to the session. */ 1546 g_DcpCtx.Session.clip.pvClipboardBuf.set((uint64_t)pvData); 1547 g_DcpCtx.Session.clip.cbClipboardBuf.set((uint64_t)cbData); 1548 } 1549 } 1550 else 1551 rc = VERR_TIMEOUT; 1552 1553 } 1554 1555 return rc; 1556 } 1557 1558 /** 1559 * @interface_method_impl{VBCLWAYLANDHELPER,pfnHGClipReport} 1560 */ 1561 static int vbcl_wayland_hlp_dcp_hg_clip_report(SHCLFORMATS fFormats) 1562 { 1563 int rc = VERR_NO_DATA; 1564 1565 VBCL_LOG_CALLBACK; 1566 1567 if (fFormats != VBOX_SHCL_FMT_NONE) 1568 { 1569 rc = vbcl_wayland_session_end(&g_DcpCtx.Session.Base, NULL, NULL); 1570 if (RT_SUCCESS(rc)) 1571 { 1572 rc = vbcl_wayland_session_start(&g_DcpCtx.Session.Base, 1573 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST, 1574 &vbcl_wayland_hlp_dcp_session_start_generic_cb, 1575 NULL); 1576 1577 if (RT_SUCCESS(rc)) 1578 rc = vbcl_wayland_session_join(&g_DcpCtx.Session.Base, 1579 vbcl_wayland_hlp_dcp_hg_clip_report_join_cb, 1580 &fFormats); 1581 } 1582 else 1583 VBClLogError("unable to start session, previous session is still running rc=%Rrc\n", rc); 1584 } 1585 1586 return rc; 1587 } 1588 1589 /** 1590 * Session callback: Copy clipboard to the host. 1591 * 1592 * This callback sets clipboard format to the session as requested 1593 * by host, waits for guest clipboard data in requested format and 1594 * sends data to the host. 1595 * 1596 * This callback should not return until clipboard data is sent to 1597 * the host or error occurred. It must block host events loop until 1598 * current host event is fully processed. 1599 * 1600 * @returns IPRT status code. 1601 * @param enmSessionType Session type, must be verified as 1602 * a consistency check. 1603 * @param pvUser User data (requested format). 1604 */ 1605 static DECLCALLBACK(int) vbcl_wayland_hlp_dcp_gh_clip_read_join_cb( 1606 vbcl_wl_session_type_t enmSessionType, void *pvUser) 1607 { 1608 SHCLFORMAT *puFmt = (SHCLFORMAT *)pvUser; 1609 AssertPtrReturn(puFmt, VERR_INVALID_PARAMETER); 1610 1611 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST) 1612 ? VINF_SUCCESS : VERR_WRONG_ORDER; 1613 1614 VBCL_LOG_CALLBACK; 1615 1616 if (RT_SUCCESS(rc)) 1617 { 1618 void *pvData; 1619 size_t cbData; 1620 1621 /* Store requested clipboard format to the session. */ 1622 g_DcpCtx.Session.clip.uFmt.set(*puFmt); 1623 1624 /* Wait for data in requested format. */ 1625 pvData = (void *)g_DcpCtx.Session.clip.pvClipboardBuf.wait(); 1626 cbData = g_DcpCtx.Session.clip.cbClipboardBuf.wait(); 1627 if ( cbData != g_DcpCtx.Session.clip.cbClipboardBuf.defaults() 1628 && pvData != (void *)g_DcpCtx.Session.clip.pvClipboardBuf.defaults()) 1629 { 1630 /* Send clipboard data to the host. */ 1631 rc = VbglR3ClipboardWriteDataEx(g_DcpCtx.pClipboardCtx, *puFmt, pvData, cbData); 1632 } 1633 else 1634 rc = VERR_TIMEOUT; 1635 } 1636 1637 return rc; 1638 } 1639 1640 /** 1641 * @interface_method_impl{VBCLWAYLANDHELPER,pfnGHClipRead} 1642 */ 1643 static int vbcl_wayland_hlp_dcp_gh_clip_read(SHCLFORMAT uFmt) 1644 { 1645 int rc; 1646 1647 VBCL_LOG_CALLBACK; 1648 1649 rc = vbcl_wayland_session_join(&g_DcpCtx.Session.Base, 1650 &vbcl_wayland_hlp_dcp_gh_clip_read_join_cb, 1651 &uFmt); 1652 return rc; 52 1653 } 53 1654 … … 55 1656 const VBCLWAYLANDHELPER g_WaylandHelperDcp = 56 1657 { 57 "wayland-dcp", /* .pszName */ 58 vbcl_wayland_hlp_dcp_probe, /* .pfnProbe */ 59 vbcl_wayland_hlp_dcp_init, /* .pfnInit */ 60 vbcl_wayland_hlp_dcp_term, /* .pfnTerm */ 1658 "wayland-dcp", /* .pszName */ 1659 vbcl_wayland_hlp_dcp_probe, /* .pfnProbe */ 1660 vbcl_wayland_hlp_dcp_init, /* .pfnInit */ 1661 vbcl_wayland_hlp_dcp_term, /* .pfnTerm */ 1662 vbcl_wayland_hlp_dcp_set_clipboard_ctx, /* .pfnSetClipboardCtx */ 1663 vbcl_wayland_hlp_dcp_popup, /* .pfnPopup */ 1664 vbcl_wayland_hlp_dcp_hg_clip_report, /* .pfnHGClipReport */ 1665 vbcl_wayland_hlp_dcp_gh_clip_read, /* .pfnGHClipRead */ 61 1666 }; -
trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper-gtk.cpp
r100246 r101878 1 1 /* $Id$ */ 2 2 /** @file 3 * Guest Additions - Wayland Desktop Environment helper which uses GTK library. 3 * Guest Additions - Gtk helper for Wayland. 4 * 5 * This module implements Shared Clipboard and Drag-n-Drop 6 * support for Wayland guests using Gtk library. 4 7 */ 5 8 … … 26 29 */ 27 30 31 #include <iprt/localipc.h> 32 #include <iprt/rand.h> 33 #include <iprt/semaphore.h> 34 35 #include <VBox/GuestHost/DisplayServerType.h> 36 #include <VBox/GuestHost/clipboard-helper.h> 37 #include <VBox/GuestHost/mime-type-converter.h> 38 39 #include "VBoxClient.h" 40 #include "clipboard.h" 28 41 #include "wayland-helper.h" 42 #include "wayland-helper-ipc.h" 43 44 #include "vbox-gtk.h" 45 46 /** Gtk session data. 47 * 48 * A structure which accumulates all the necessary data required to 49 * maintain session between host and Wayland for clipboard sharing 50 * and drag-n-drop.*/ 51 typedef struct 52 { 53 /* Generic VBoxClient Wayland session data (synchronization point). */ 54 vbcl_wl_session_t Base; 55 /** Randomly generated session ID, should be used by 56 * both VBoxClient and vboxwl tool. */ 57 uint32_t uSessionId; 58 /** IPC connection flow control between VBoxClient and vboxwl tool. */ 59 vbcl::ipc::clipboard::ClipboardIpc *oClipboardIpc; 60 /** IPC connection handle. */ 61 RTLOCALIPCSESSION hIpcSession; 62 /** Popup window process handle. */ 63 RTPROCESS popupProc; 64 } vbox_wl_gtk_ipc_session_t; 65 66 /** 67 * A set of objects required to handle clipboard sharing over 68 * and drag-n-drop using Gtk library.. */ 69 typedef struct 70 { 71 /** Wayland event loop thread. */ 72 RTTHREAD Thread; 73 74 /** A flag which indicates that Wayland event loop should terminate. */ 75 volatile bool fShutdown; 76 77 /** Communication session between host event loop and Wayland. */ 78 vbox_wl_gtk_ipc_session_t Session; 79 80 /** Connection to the host clipboard service. */ 81 PVBGLR3SHCLCMDCTX pClipboardCtx; 82 83 /** Local IPC server object. */ 84 RTLOCALIPCSERVER hIpcServer; 85 } vbox_wl_gtk_ctx_t; 86 87 /** Helper context. */ 88 static vbox_wl_gtk_ctx_t g_GtkCtx; 89 90 /** 91 * Start popup process. 92 * 93 * @returns IPRT status code. 94 * @param pSession Session data. 95 */ 96 static int vbcl_wayland_hlp_gtk_session_popup(vbox_wl_gtk_ipc_session_t *pSession) 97 { 98 int rc = VINF_SUCCESS; 99 100 /* Make sure valid session is in progress. */ 101 AssertReturn(pSession->uSessionId > 0, VERR_INVALID_PARAMETER); 102 103 char *pszSessionId = RTStrAPrintf2("%u", pSession->uSessionId); 104 if (RT_VALID_PTR(pszSessionId)) 105 { 106 /* List of vboxwl command line arguments.*/ 107 const char *apszArgs[] = 108 { 109 VBOXWL_PATH, 110 NULL, 111 VBOXWL_ARG_SESSION_ID, 112 pszSessionId, 113 NULL, 114 NULL 115 }; 116 117 /* Log verbosity level to be passed to vboxwl. */ 118 char pszVerobsity[ VBOXWL_VERBOSITY_MAX 119 + 2 /* add space for '-' and '\0' */]; 120 RT_ZERO(pszVerobsity); 121 122 /* Select vboxwl action depending on session type. */ 123 if (pSession->Base.enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 124 apszArgs[1] = VBOXWL_ARG_CLIP_HG_COPY; 125 else if (pSession->Base.enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST) 126 apszArgs[1] = VBOXWL_ARG_CLIP_GH_ANNOUNCE; 127 else if (pSession->Base.enmType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST) 128 apszArgs[1] = VBOXWL_ARG_CLIP_GH_COPY; 129 else 130 rc = VERR_INVALID_PARAMETER; 131 132 /* Once VBoxClient was started with log verbosity level, pass the 133 * same verbosity level to vboxwl as well. */ 134 if ( RT_SUCCESS(rc) 135 && g_cVerbosity > 0) 136 { 137 pszVerobsity[0] = '-'; 138 139 memset(&pszVerobsity[1], 'v', 140 RT_MIN(g_cVerbosity, VBOXWL_VERBOSITY_MAX)); 141 142 /* Insert verbosity level into the rest of vboxwl 143 * command line arguments. */ 144 apszArgs[4] = pszVerobsity; 145 } 146 147 /* Run vboxwl in background. */ 148 if (RT_SUCCESS(rc)) 149 rc = RTProcCreate(VBOXWL_PATH, 150 apszArgs, RTENV_DEFAULT, 151 RTPROC_FLAGS_SEARCH_PATH, &pSession->popupProc); 152 153 VBClLogVerbose(2, "start '%s' command [sid=%u]: rc=%Rrc\n", 154 VBOXWL_PATH, pSession->uSessionId, rc); 155 156 RTStrFree(pszSessionId); 157 } 158 else 159 rc = VERR_NO_MEMORY; 160 161 return rc; 162 } 163 164 /** 165 * Prepare new session and start popup process. 166 * 167 * @returns IPRT status code. 168 * @param pSession Session data. 169 */ 170 static int vbcl_wayland_hlp_gtk_session_prepare(vbox_wl_gtk_ipc_session_t *pSession) 171 { 172 int rc = VINF_SUCCESS; 173 174 /* Make sure there is no leftovers from previous session. */ 175 Assert(pSession->uSessionId == 0); 176 177 /* Initialize session. */ 178 pSession->uSessionId = RTRandU32Ex(1, 0xFFFFFFFF); 179 180 pSession->oClipboardIpc = new vbcl::ipc::clipboard::ClipboardIpc(); 181 if (RT_VALID_PTR(pSession->oClipboardIpc)) 182 { 183 pSession->oClipboardIpc->init(vbcl::ipc::FLOW_DIRECTION_SERVER, 184 pSession->uSessionId); 185 } 186 else 187 rc = VERR_NO_MEMORY; 188 189 /* Start helper tool. */ 190 if (RT_SUCCESS(rc)) 191 { 192 rc = vbcl_wayland_hlp_gtk_session_popup(pSession); 193 VBClLogVerbose(1, "session id=%u: started: rc=%Rrc\n", 194 pSession->uSessionId, rc); 195 } 196 197 return rc; 198 } 199 200 /** 201 * Session callback: Generic session initializer. 202 * 203 * This callback starts new session. 204 * 205 * @returns IPRT status code. 206 * @param enmSessionType Session type (unused). 207 * @param pvUser User data (unused). 208 */ 209 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_session_start_generic_cb( 210 vbcl_wl_session_type_t enmSessionType, void *pvUser) 211 { 212 RT_NOREF(enmSessionType, pvUser); 213 214 VBCL_LOG_CALLBACK; 215 216 return vbcl_wayland_hlp_gtk_session_prepare(&g_GtkCtx.Session); 217 } 218 219 /** 220 * Reset session, terminate popup process and free allocated resources. 221 * 222 * @returns IPRT status code. 223 * @param enmSessionType Session type (unused). 224 * @param pvUser User data (session to reset). 225 */ 226 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_session_end_cb( 227 vbcl_wl_session_type_t enmSessionType, void *pvUser) 228 { 229 vbox_wl_gtk_ipc_session_t *pSession = (vbox_wl_gtk_ipc_session_t *)pvUser; 230 AssertPtrReturn(pSession, VERR_INVALID_PARAMETER); 231 232 int rc; 233 234 RT_NOREF(enmSessionType); 235 236 /* Make sure valid session is in progress. */ 237 AssertReturn(pSession->uSessionId > 0, VERR_INVALID_PARAMETER); 238 239 rc = RTProcWait(pSession->popupProc, RTPROCWAIT_FLAGS_BLOCK, NULL); 240 if (RT_FAILURE(rc)) 241 rc = RTProcTerminate(pSession->popupProc); 242 if (RT_FAILURE(rc)) 243 { 244 VBClLogError("session %u: unable to stop popup window process: rc=%Rrc\n", 245 pSession->uSessionId, rc); 246 } 247 248 if (RT_SUCCESS(rc)) 249 { 250 pSession->uSessionId = 0; 251 252 pSession->oClipboardIpc->reset(); 253 delete pSession->oClipboardIpc; 254 } 255 256 return rc; 257 } 258 259 /** 260 * Session callback: Handle sessions started by host events. 261 * 262 * @returns IPRT status code. 263 * @param enmSessionType Session type, must be verified as 264 * a consistency check. 265 * @param pvUser User data (IPC connection handle 266 * to vboxwl tool). 267 */ 268 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_worker_join_cb( 269 vbcl_wl_session_type_t enmSessionType, void *pvUser) 270 { 271 PRTLOCALIPCSESSION phIpcSession = (RTLOCALIPCSESSION *)pvUser; 272 AssertPtrReturn(phIpcSession, VERR_INVALID_PARAMETER); 273 274 const vbcl::ipc::flow_t *pFlow; 275 276 int rc = VINF_SUCCESS; 277 278 VBCL_LOG_CALLBACK; 279 280 /* Make sure valid session is in progress. */ 281 AssertReturn(g_GtkCtx.Session.uSessionId > 0, VERR_INVALID_PARAMETER); 282 283 /* Select corresponding IPC flow depending on session type. */ 284 if (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 285 pFlow = vbcl::ipc::clipboard::HGCopyFlow; 286 else if (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST) 287 pFlow = vbcl::ipc::clipboard::GHAnnounceAndCopyFlow; 288 else if (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST) 289 pFlow = vbcl::ipc::clipboard::GHCopyFlow; 290 else 291 rc = VERR_INVALID_PARAMETER; 292 293 /* Proceed with selected flow. */ 294 if (RT_SUCCESS(rc)) 295 rc = g_GtkCtx.Session.oClipboardIpc->flow(pFlow, *phIpcSession); 296 297 return rc; 298 } 299 300 /** 301 * IPC server thread worker. 302 * 303 * @returns IPRT status code. 304 * @param hThreadSelf IPRT thread handle. 305 * @param pvUser Helper context data. 306 */ 307 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_worker(RTTHREAD hThreadSelf, void *pvUser) 308 { 309 int rc; 310 311 vbox_wl_gtk_ctx_t *pCtx = (vbox_wl_gtk_ctx_t *)pvUser; 312 char szIpcServerName[128]; 313 314 RTThreadUserSignal(hThreadSelf); 315 316 VBClLogVerbose(1, "starting IPC\n"); 317 318 rc = vbcl_wayland_hlp_gtk_ipc_srv_name(szIpcServerName, sizeof(szIpcServerName)); 319 320 if (RT_SUCCESS(rc)) 321 rc = RTLocalIpcServerCreate(&pCtx->hIpcServer, szIpcServerName, 0); 322 323 if (RT_SUCCESS(rc)) 324 rc = RTLocalIpcServerSetAccessMode(pCtx->hIpcServer, RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); 325 326 if (RT_SUCCESS(rc)) 327 { 328 VBClLogVerbose(1, "started IPC server '%s'\n", szIpcServerName); 329 330 vbcl_wayland_session_init(&pCtx->Session.Base); 331 332 while (!ASMAtomicReadBool(&pCtx->fShutdown)) 333 { 334 RTLOCALIPCSESSION hClientSession; 335 336 rc = RTLocalIpcServerListen(pCtx->hIpcServer, &hClientSession); 337 if (RT_SUCCESS(rc)) 338 { 339 RTUID uUid; 340 341 /* Authenticate remote user. Only allow connection from 342 * process who belongs to the same UID. */ 343 rc = RTLocalIpcSessionQueryUserId(hClientSession, &uUid); 344 if (RT_SUCCESS(rc)) 345 { 346 RTUID uLocalUID = geteuid(); 347 if ( uLocalUID != 0 348 && uLocalUID == uUid) 349 { 350 VBClLogVerbose(1, "new IPC connection\n"); 351 352 rc = vbcl_wayland_session_join(&pCtx->Session.Base, 353 &vbcl_wayland_hlp_gtk_worker_join_cb, 354 &hClientSession); 355 356 VBClLogVerbose(1, "IPC flow completed, rc=%Rrc\n", rc); 357 358 rc = vbcl_wayland_session_end(&pCtx->Session.Base, 359 &vbcl_wayland_hlp_gtk_session_end_cb, 360 &pCtx->Session); 361 VBClLogVerbose(1, "IPC session ended, rc=%Rrc\n", rc); 362 363 } 364 else 365 VBClLogError("incoming IPC connection rejected: UID mismatch: %d/%d\n", 366 uLocalUID, uUid); 367 } 368 else 369 VBClLogError("failed to get remote IPC UID, rc=%Rrc\n", rc); 370 371 RTLocalIpcSessionClose(hClientSession); 372 } 373 else if (rc != VERR_CANCELLED) 374 VBClLogVerbose(1, "IPC connection has failed, rc=%Rrc\n", rc); 375 } 376 377 rc = RTLocalIpcServerDestroy(pCtx->hIpcServer); 378 } 379 else 380 VBClLogError("failed to start IPC, rc=%Rrc\n", rc); 381 382 VBClLogVerbose(1, "IPC stopped\n"); 383 384 return rc; 385 } 29 386 30 387 /** … … 33 390 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_probe(void) 34 391 { 35 return VBOX_WAYLAND_HELPER_CAP_NONE; 392 int fCaps = VBOX_WAYLAND_HELPER_CAP_NONE; 393 394 if (VBGHDisplayServerTypeIsGtkAvailable()) 395 fCaps |= VBOX_WAYLAND_HELPER_CAP_CLIPBOARD; 396 397 return fCaps; 36 398 } 37 399 … … 41 403 RTDECL(int) vbcl_wayland_hlp_gtk_init(void) 42 404 { 43 return VERR_NOT_SUPPORTED; 405 VBCL_LOG_CALLBACK; 406 407 RT_ZERO(g_GtkCtx); 408 409 return VBClClipboardThreadStart(&g_GtkCtx.Thread, vbcl_wayland_hlp_gtk_worker, "wl-gtk-ipc", &g_GtkCtx); 44 410 } 45 411 … … 49 415 RTDECL(int) vbcl_wayland_hlp_gtk_term(void) 50 416 { 51 return VERR_NOT_SUPPORTED; 417 int rc; 418 int rcThread = 0; 419 420 /* Set termination flag. */ 421 g_GtkCtx.fShutdown = true; 422 423 /* Cancel IPC loop. */ 424 rc = RTLocalIpcServerCancel(g_GtkCtx.hIpcServer); 425 if (RT_FAILURE(rc)) 426 VBClLogError("unable to notify IPC server about shutdown, rc=%Rrc\n", rc); 427 428 if (RT_SUCCESS(rc)) 429 { 430 /* Wait for Gtk event loop thread to shutdown. */ 431 rc = RTThreadWait(g_GtkCtx.Thread, RT_MS_30SEC, &rcThread); 432 VBClLogInfo("gtk event thread exited with status, rc=%Rrc\n", rcThread); 433 } 434 else 435 VBClLogError("unable to stop gtk thread, rc=%Rrc\n", rc); 436 437 return rc; 438 } 439 440 /** 441 * @interface_method_impl{VBCLWAYLANDHELPER,pfnSetClipboardCtx} 442 */ 443 static DECLCALLBACK(void) vbcl_wayland_hlp_gtk_set_clipboard_ctx(PVBGLR3SHCLCMDCTX pCtx) 444 { 445 g_GtkCtx.pClipboardCtx = pCtx; 446 } 447 448 /** 449 * Session callback: Announce clipboard to the host. 450 * 451 * This callback (1) waits for the guest to report its clipboard content 452 * via IPC connection from vboxwl tool, and (2) reports these formats 453 * to the host. 454 * 455 * @returns IPRT status code. 456 * @param enmSessionType Session type, must be verified as 457 * a consistency check. 458 * @param pvUser User data (unused). 459 */ 460 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_popup_join_cb( 461 vbcl_wl_session_type_t enmSessionType, void *pvUser) 462 { 463 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST) 464 ? VINF_SUCCESS : VERR_WRONG_ORDER; 465 466 RT_NOREF(pvUser); 467 468 VBCL_LOG_CALLBACK; 469 470 if (RT_SUCCESS(rc)) 471 { 472 SHCLFORMATS fFmts = g_GtkCtx.Session.oClipboardIpc->m_fFmts.wait(); 473 if (fFmts != g_GtkCtx.Session.oClipboardIpc->m_fFmts.defaults()) 474 rc = VbglR3ClipboardReportFormats(g_GtkCtx.pClipboardCtx->idClient, fFmts); 475 else 476 rc = VERR_TIMEOUT; 477 } 478 479 return rc; 480 } 481 482 /** 483 * @interface_method_impl{VBCLWAYLANDHELPER,pfnPopup} 484 */ 485 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_popup(void) 486 { 487 int rc; 488 489 VBCL_LOG_CALLBACK; 490 491 rc = vbcl_wayland_session_start(&g_GtkCtx.Session.Base, 492 VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST, 493 &vbcl_wayland_hlp_gtk_session_start_generic_cb, 494 &g_GtkCtx.Session); 495 if (RT_SUCCESS(rc)) 496 { 497 rc = vbcl_wayland_session_join(&g_GtkCtx.Session.Base, 498 &vbcl_wayland_hlp_gtk_popup_join_cb, 499 NULL); 500 } 501 502 return rc; 503 } 504 505 /** 506 * Session callback: Copy clipboard from the host. 507 * 508 * This callback (1) sets host clipboard formats list to the session, 509 * (2) waits for guest to request clipboard in specific format, (3) read 510 * host clipboard in this format, and (4) sets clipboard data to the session, 511 * so Gtk event thread can inject it into the guest. 512 * 513 * This callback should not return until clipboard data is read from 514 * the host or error occurred. It must block host events loop until 515 * current host event is fully processed. 516 * 517 * @returns IPRT status code. 518 * @param enmSessionType Session type, must be verified as 519 * a consistency check. 520 * @param pvUser User data (host clipboard formats). 521 */ 522 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_hg_clip_report_join_cb( 523 vbcl_wl_session_type_t enmSessionType, void *pvUser) 524 { 525 SHCLFORMATS *pfFmts = (SHCLFORMATS *)pvUser; 526 AssertPtrReturn(pfFmts, VERR_INVALID_PARAMETER); 527 528 SHCLFORMAT uFmt; 529 530 int rc = (enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST) 531 ? VINF_SUCCESS : VERR_WRONG_ORDER; 532 533 VBCL_LOG_CALLBACK; 534 535 if (RT_SUCCESS(rc)) 536 { 537 g_GtkCtx.Session.oClipboardIpc->m_fFmts.set(*pfFmts); 538 539 uFmt = g_GtkCtx.Session.oClipboardIpc->m_uFmt.wait(); 540 if (uFmt != g_GtkCtx.Session.oClipboardIpc->m_uFmt.defaults()) 541 { 542 void *pvData; 543 uint32_t cbData; 544 545 rc = VBClClipboardReadHostClipboard(g_GtkCtx.pClipboardCtx, uFmt, &pvData, &cbData); 546 if (RT_SUCCESS(rc)) 547 { 548 g_GtkCtx.Session.oClipboardIpc->m_pvClipboardBuf.set((uint64_t)pvData); 549 g_GtkCtx.Session.oClipboardIpc->m_cbClipboardBuf.set((uint64_t)cbData); 550 } 551 } 552 else 553 rc = VERR_TIMEOUT; 554 } 555 556 return rc; 557 } 558 559 /** 560 * @interface_method_impl{VBCLWAYLANDHELPER,pfnHGClipReport} 561 */ 562 static int vbcl_wayland_hlp_gtk_hg_clip_report(SHCLFORMATS fFormats) 563 { 564 int rc = VERR_NO_DATA; 565 566 VBCL_LOG_CALLBACK; 567 568 if (fFormats != VBOX_SHCL_FMT_NONE) 569 { 570 rc = vbcl_wayland_session_start(&g_GtkCtx.Session.Base, 571 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST, 572 &vbcl_wayland_hlp_gtk_session_start_generic_cb, 573 &g_GtkCtx.Session); 574 if (RT_SUCCESS(rc)) 575 { 576 rc = vbcl_wayland_session_join(&g_GtkCtx.Session.Base, 577 &vbcl_wayland_hlp_gtk_hg_clip_report_join_cb, 578 &fFormats); 579 } 580 } 581 582 return rc; 583 } 584 585 /** 586 * Session callback: Copy clipboard to the host. 587 * 588 * This callback sets clipboard format to the session as requested 589 * by host, waits for guest clipboard data in requested format and 590 * sends data to the host. 591 * 592 * This callback should not return until clipboard data is sent to 593 * the host or error occurred. It must block host events loop until 594 * current host event is fully processed. 595 * 596 * @returns IPRT status code. 597 * @param enmSessionType Session type, must be verified as 598 * a consistency check. 599 * @param pvUser User data (requested format). 600 */ 601 static DECLCALLBACK(int) vbcl_wayland_hlp_gtk_gh_clip_read_join_cb( 602 vbcl_wl_session_type_t enmSessionType, void *pvUser) 603 { 604 SHCLFORMAT *puFmt = (SHCLFORMAT *)pvUser; 605 AssertPtrReturn(puFmt, VERR_INVALID_PARAMETER); 606 607 int rc = ( enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST 608 || enmSessionType == VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST) 609 ? VINF_SUCCESS : VERR_WRONG_ORDER; 610 611 VBCL_LOG_CALLBACK; 612 613 if (RT_SUCCESS(rc)) 614 { 615 void *pvData; 616 uint32_t cbData; 617 618 /* Store requested clipboard format to the session. */ 619 g_GtkCtx.Session.oClipboardIpc->m_uFmt.set(*puFmt); 620 621 /* Wait for data in requested format. */ 622 pvData = (void *)g_GtkCtx.Session.oClipboardIpc->m_pvClipboardBuf.wait(); 623 cbData = g_GtkCtx.Session.oClipboardIpc->m_cbClipboardBuf.wait(); 624 if ( cbData != g_GtkCtx.Session.oClipboardIpc->m_cbClipboardBuf.defaults() 625 && pvData != (void *)g_GtkCtx.Session.oClipboardIpc->m_pvClipboardBuf.defaults()) 626 { 627 /* Send clipboard data to the host. */ 628 rc = VbglR3ClipboardWriteDataEx(g_GtkCtx.pClipboardCtx, *puFmt, pvData, cbData); 629 } 630 else 631 rc = VERR_TIMEOUT; 632 } 633 634 return rc; 635 } 636 637 /** 638 * @interface_method_impl{VBCLWAYLANDHELPER,pfnGHClipRead} 639 */ 640 static int vbcl_wayland_hlp_gtk_gh_clip_read(SHCLFORMAT uFmt) 641 { 642 int rc = VINF_SUCCESS; 643 644 VBCL_LOG_CALLBACK; 645 646 if (uFmt != VBOX_SHCL_FMT_NONE) 647 { 648 VBClLogVerbose(2, "host wants fmt 0x%x\n", uFmt); 649 650 /* This callback can be called in two cases: 651 * 652 * 1. Guest has just announced a list of its clipboard 653 * formats to the host, and vboxwl tool is still running, 654 * IPC session is still active as well. In this case the 655 * host can immediately ask for content in specified format. 656 * 657 * 2. Guest has already announced list of its clipboard 658 * formats to the host some time ago, vboxwl tool is no 659 * longer running and IPC session is not active. In this 660 * case some app on the host side might want to read 661 * clipboard in specific format. 662 * 663 * In case (2), we need to start new IPC session and restart 664 * vboxwl tool again 665 */ 666 if (!vbcl_wayland_session_is_started(&g_GtkCtx.Session.Base)) 667 { 668 rc = vbcl_wayland_session_start(&g_GtkCtx.Session.Base, 669 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST, 670 &vbcl_wayland_hlp_gtk_session_start_generic_cb, 671 NULL); 672 } 673 674 if (RT_SUCCESS(rc)) 675 { 676 rc = vbcl_wayland_session_join(&g_GtkCtx.Session.Base, 677 &vbcl_wayland_hlp_gtk_gh_clip_read_join_cb, 678 &uFmt); 679 } 680 } 681 682 VBClLogVerbose(2, "vbcl_wayland_hlp_gtk_gh_clip_read ended rc=%Rrc\n", rc); 683 684 return rc; 52 685 } 53 686 … … 55 688 const VBCLWAYLANDHELPER g_WaylandHelperGtk = 56 689 { 57 "wayland-gtk", /* .pszName */ 58 vbcl_wayland_hlp_gtk_probe, /* .pfnProbe */ 59 vbcl_wayland_hlp_gtk_init, /* .pfnInit */ 60 vbcl_wayland_hlp_gtk_term, /* .pfnTerm */ 690 "wayland-gtk", /* .pszName */ 691 vbcl_wayland_hlp_gtk_probe, /* .pfnProbe */ 692 vbcl_wayland_hlp_gtk_init, /* .pfnInit */ 693 vbcl_wayland_hlp_gtk_term, /* .pfnTerm */ 694 vbcl_wayland_hlp_gtk_set_clipboard_ctx, /* .pfnSetClipboardCtx */ 695 vbcl_wayland_hlp_gtk_popup, /* .pfnPopup */ 696 vbcl_wayland_hlp_gtk_hg_clip_report, /* .pfnHGClipReport */ 697 vbcl_wayland_hlp_gtk_gh_clip_read, /* .pfnGHClipRead */ 61 698 }; -
trunk/src/VBox/Additions/x11/VBoxClient/wayland-helper.h
r100246 r101878 1 1 /* $Id$ */ 2 2 /** @file 3 * Guest Additions - Definitions for Wayland Desktop Environmentshelpers.3 * Guest Additions - Definitions for Wayland helpers. 4 4 */ 5 5 … … 32 32 #endif 33 33 34 #include <iprt/cdefs.h> 35 #include <iprt/err.h> 36 34 #include <iprt/asm.h> 35 #include <iprt/time.h> 36 37 #include <VBox/VBoxGuestLib.h> 38 #include <VBox/GuestHost/clipboard-helper.h> 39 40 #include "clipboard.h" 37 41 38 42 /** Helper capabilities list. */ 43 44 /** Indicates that helper does not support any functionality (initializer). */ 45 #define VBOX_WAYLAND_HELPER_CAP_NONE (0) 46 /** Indicates that helper supported shared clipboard functionality. */ 47 #define VBOX_WAYLAND_HELPER_CAP_CLIPBOARD RT_BIT(1) 48 /** Indicates that helper supported drag-and-drop functionality. */ 49 #define VBOX_WAYLAND_HELPER_CAP_DND RT_BIT(2) 50 51 /** Default time interval to wait for value to arrive over IPC. */ 52 #define VBCL_WAYLAND_VALUE_WAIT_TIMEOUT_MS (1000) 53 /** Default time interval to wait for clipboard content to arrive over IPC. */ 54 #define VBCL_WAYLAND_DATA_WAIT_TIMEOUT_MS (2000) 55 /** Generic relax interval while polling value changes. */ 56 #define VBCL_WAYLAND_RELAX_INTERVAL_MS (50) 57 /** Maximum number of participants who can join session. */ 58 #define VBCL_WAYLAND_SESSION_USERS_MAX (10) 59 /** Value which determines if session structure was initialized. */ 60 #define VBCL_WAYLAND_SESSION_MAGIC (0xDEADBEEF) 61 62 /** Session states. */ 39 63 typedef enum 40 64 { 41 /** Indicates that helper does not support any functionality (initializer). */ 42 VBOX_WAYLAND_HELPER_CAP_NONE = 0, 43 /** Indicates that helper supported shared clipboard functionality. */ 44 VBOX_WAYLAND_HELPER_CAP_CLIPBOARD, 45 /** Indicates that helper supported drag-and-drop functionality. */ 46 VBOX_WAYLAND_HELPER_CAP_DND 47 } vbox_wayland_helper_cap_t; 65 /** Session is not active. */ 66 VBCL_WL_SESSION_STATE_IDLE, 67 /** Session is being initialized. */ 68 VBCL_WL_SESSION_STATE_STARTING, 69 /** Session has started and now can be joined. */ 70 VBCL_WL_SESSION_STATE_STARTED, 71 /** Session is terminating. */ 72 VBCL_WL_SESSION_STATE_TERMINATING 73 } vbcl_wl_session_state_t; 74 75 /** Session type. 76 * 77 * Type determines the purpose of session. It is 78 * also serves a sanity check purpose when different 79 * participants join session with certain intention. 80 * Session type can only be set when session is 81 * in STARTING state and reset when session is TERMINATING. 82 */ 83 typedef enum 84 { 85 /** Initializer. */ 86 VBCL_WL_SESSION_TYPE_INVALID, 87 /** Copy clipboard data to the guest. */ 88 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_GUEST, 89 /** Announce clipboard formats to the host. */ 90 VBCL_WL_CLIPBOARD_SESSION_TYPE_ANNOUNCE_TO_HOST, 91 /** Copy clipboard data to the host. */ 92 VBCL_WL_CLIPBOARD_SESSION_TYPE_COPY_TO_HOST, 93 } vbcl_wl_session_type_t; 94 95 /** Session private data. */ 96 typedef struct 97 { 98 /** Magic number which indicates if session 99 * was previously initialized. */ 100 volatile uint32_t u32Magic; 101 102 /** Session state, synchronization element. */ 103 volatile vbcl_wl_session_state_t enmState; 104 105 /** Session type. */ 106 vbcl_wl_session_type_t enmType; 107 108 /** Session description, used for logging purpose 109 * to distinguish between operations flow. */ 110 const char *pcszDesc; 111 112 /** Current number of session users. When session 113 * switches into TERMINATING state, it will wait 114 * number of participants to drop to 1 before releasing 115 * session resources and resetting internal data to 116 * default values. */ 117 volatile uint32_t cUsers; 118 } vbcl_wl_session_t; 119 120 /** Session state change callback. 121 * 122 * Data which belongs to a session must be accessed from 123 * session callback only. It ensures session data integrity 124 * and prevents access to data when session is not yet 125 * initialized or already terminated. 126 * 127 * @returns IPRT status code. 128 * @param enmSessionType Session type, provided for consistency 129 * check to make sure given callback is 130 * intended to be triggered in context of 131 * given session type. 132 * @param pvUser Optional user data. 133 */ 134 typedef DECLCALLBACKTYPE(int, FNVBCLWLSESSIONCB, (vbcl_wl_session_type_t enmSessionType, void *pvUser)); 135 /** Pointer to FNVBCLWLSESSIONCB. */ 136 typedef FNVBCLWLSESSIONCB *PFNVBCLWLSESSIONCB; 48 137 49 138 /** … … 61 150 * which is compatible with the helper. 62 151 * 63 * @returns Helpercapabilities bitmask as described by vbox_wayland_helper_cap_t.152 * @returns Helpercapabilities bitmask as described by VBOX_WAYLAND_HELPER_CAP_XXX. 64 153 */ 65 154 DECLCALLBACKMEMBER(int, pfnProbe, (void)); … … 79 168 DECLCALLBACKMEMBER(int, pfnTerm, (void)); 80 169 170 /** 171 * Callback to set host clipboard connection handle. 172 * 173 * @param pCtx Host service connection context. 174 */ 175 DECLCALLBACKMEMBER(void, pfnSetClipboardCtx, (PVBGLR3SHCLCMDCTX pCtx)); 176 177 /** 178 * Callback to force guest to announce its clipboard content. 179 * 180 * @returns IPRT status code. 181 */ 182 DECLCALLBACKMEMBER(int, pfnPopup, (void)); 183 184 PFNHOSTCLIPREPORTFMTS pfnHGClipReport; 185 PFNHOSTCLIPREAD pfnGHClipRead; 186 81 187 } VBCLWAYLANDHELPER; 188 189 namespace vbcl 190 { 191 /** 192 * This is abstract one-shot data type which can be set by writer and waited 193 * by reader in a thread-safe way. 194 * 195 * Method wait() will wait within predefined interval of time until 196 * value of this type will be changed to anything different from default 197 * value which is defined during initialization. Reader must compare 198 * returned value with what defaults() method returns in order to make 199 * sure that value was actually set by writer. 200 * 201 * Method reset() will atomically reset current value to defaults and 202 * return previous value. This is useful when writer has previously 203 * dynamically allocated chunk of memory and reader needs to deallocete 204 * it in the end. 205 */ 206 template <class T> class Waitable 207 { 208 public: 209 210 Waitable() 211 {}; 212 213 /** 214 * Initialize data type. 215 * 216 * @param default_value Default value, used while 217 * waiting for value change. 218 * @param timeoutMs Time interval to wait for 219 * value change before returning. 220 */ 221 void init(T default_value, uint64_t timeoutMs) 222 { 223 m_Value = default_value; 224 m_Default = default_value; 225 m_TimeoutMs = timeoutMs; 226 } 227 228 /** 229 * Atomically set value. 230 * 231 * @param value Value to set. 232 */ 233 void set(T value) 234 { 235 ASMAtomicWriteU64(&m_Value, value); 236 } 237 238 /** 239 * Atomically reset value to defaults and return previous value. 240 * 241 * @returns Value which was assigned before reset. 242 */ 243 uint64_t reset() 244 { 245 return ASMAtomicXchgU64(&m_Value, m_Default); 246 } 247 248 /** 249 * Wait until value will be changed from defaults and return it. 250 * 251 * @returns Current value. 252 */ 253 T wait() 254 { 255 uint64_t tsStart = RTTimeMilliTS(); 256 257 while( (RTTimeMilliTS() - tsStart) < m_TimeoutMs 258 && (ASMAtomicReadU64(&m_Value)) == m_Default) 259 { 260 RTThreadSleep(VBCL_WAYLAND_RELAX_INTERVAL_MS); 261 } 262 263 return m_Value; 264 } 265 266 /** 267 * Get default value which was set during initialization. 268 * 269 * @returns Default value. 270 */ 271 T defaults() 272 { 273 return m_Default; 274 } 275 276 protected: 277 278 /** Value itself. */ 279 uint64_t m_Value; 280 /** Default value. */ 281 uint64_t m_Default; 282 /** Value change waiting timeout. */ 283 uint64_t m_TimeoutMs; 284 }; 285 } 286 287 /** 288 * Initialize session. 289 * 290 * This function should be called only once, during initialization step. 291 * 292 * @param pSession A pointer to session data. 293 */ 294 RTDECL(void) vbcl_wayland_session_init(vbcl_wl_session_t *pSession); 295 296 /** 297 * Start new session. 298 * 299 * Attempt to change session state from IDLE to STARTED and 300 * execute initialization callback in between. If current 301 * session state is different from IDLE, state transition will 302 * not be possible and error will be returned. 303 * 304 * @returns IPRT status code. 305 * @param pSession Session object. 306 * @param enmType Session type. 307 * @param pfnStart Initialization callback. 308 * @param pvUser User data to pass to initialization callback. 309 */ 310 RTDECL(int) vbcl_wayland_session_start(vbcl_wl_session_t *pSession, 311 vbcl_wl_session_type_t enmType, 312 PFNVBCLWLSESSIONCB pfnStart, 313 void *pvUser); 314 315 /** 316 * Join session. 317 * 318 * Attempt to grab a reference to a session, execute provided 319 * callback while holding a reference and release reference. 320 * This function will fail if current session state is different 321 * from STARTED. 322 * 323 * @returns IPRT status code. 324 * @param pSession Session object. 325 * @param pfnJoin A callback to run while holding session reference. 326 * @param pvUser User data to pass to callback. 327 * @param pcszCallee Text tag which corresponds to calling function (only 328 * for logging) 329 */ 330 RTDECL(int) vbcl_wayland_session_join_ex(vbcl_wl_session_t *pSession, 331 PFNVBCLWLSESSIONCB pfnJoin, void *pvUser, 332 const char *pcszCallee); 333 334 /** 335 * Join session (wrapper for vbcl_wayland_session_join_ex). 336 */ 337 #define vbcl_wayland_session_join(pSession, pfnJoin, pvUser) \ 338 vbcl_wayland_session_join_ex(pSession, pfnJoin, pvUser, __func__) 339 340 /** 341 * End session. 342 * 343 * Attempt to wait until session is no longer in use, execute 344 * terminating callback and reset session to IDLE state. 345 * 346 * @returns IPRT status code. 347 * @param pSession Session object. 348 * @param pfnEnd Termination callback. 349 * @param pvUser User data to pass to termination callback. 350 */ 351 RTDECL(int) vbcl_wayland_session_end(vbcl_wl_session_t *pSession, 352 PFNVBCLWLSESSIONCB pfnEnd, void *pvUser); 353 354 /** 355 * Check if session was started. 356 * 357 * @returns True if session is started, False otherwise. 358 * @param pSession Session object. 359 */ 360 RTDECL(bool) vbcl_wayland_session_is_started(vbcl_wl_session_t *pSession); 82 361 83 362 /** Wayland helper which uses GTK library. */ -
trunk/src/VBox/Additions/x11/VBoxClient/wayland.cpp
r100248 r101878 26 26 */ 27 27 28 #include <iprt/asm.h> 29 #include <iprt/thread.h> 30 31 #include <VBox/HostServices/GuestPropertySvc.h> 32 28 33 #include "VBoxClient.h" 34 #include "clipboard.h" 29 35 #include "wayland-helper.h" 36 37 /** Polling interval for input focus monitoring task. */ 38 #define VBCL_WAYLAND_WAIT_HOST_FOCUS_TIMEOUT_MS (250) 39 /** Relax interval for input focus monitoring task. */ 40 #define VBCL_WAYLAND_WAIT_HOST_FOCUS_RELAX_MS (100) 30 41 31 42 /** List of available Wayland Desktop Environment helpers. Sorted in order of preference. */ 32 43 static const VBCLWAYLANDHELPER *g_apWaylandHelpers[] = 33 44 { 45 &g_WaylandHelperDcp, /* Device Control Protocol helper. */ 34 46 &g_WaylandHelperGtk, /* GTK helper. */ 35 &g_WaylandHelperDcp, /* Device Control Protocol helper. */36 47 NULL, /* Terminate list. */ 37 48 }; 38 49 50 /** Global flag to tell service to go shutdown when needed. */ 51 static bool volatile g_fShutdown = false; 52 39 53 /** Selected helpers for Clipboard and Drag-and-Drop. */ 40 static const VBCLWAYLANDHELPER *g_pWaylandHelperHelperClipboard = NULL; 41 static const VBCLWAYLANDHELPER *g_pWaylandHelperHelperDnd = NULL; 54 static const VBCLWAYLANDHELPER *g_pWaylandHelperClipboard = NULL; 55 static const VBCLWAYLANDHELPER *g_pWaylandHelperDnd = NULL; 56 57 /** Corresponding threads for host events handling. */ 58 static RTTHREAD g_ClipboardThread; 59 static RTTHREAD g_DndThread; 60 static RTTHREAD g_HostInputFocusThread; 61 62 /** 63 * Worker for Shared Clipboard events from host. 64 * 65 * @returns IPRT status code. 66 * @param hThreadSelf IPRT thread handle. 67 * @param pvUser User data (unused). 68 */ 69 static DECLCALLBACK(int) vbclWaylandClipboardWorker(RTTHREAD hThreadSelf, void *pvUser) 70 { 71 SHCLCONTEXT ctx; 72 int rc; 73 74 RT_NOREF(pvUser); 75 76 RT_ZERO(ctx); 77 78 /* Connect to the host service. */ 79 rc = VbglR3ClipboardConnectEx(&ctx.CmdCtx, VBOX_SHCL_GF_0_CONTEXT_ID); 80 /* Notify parent thread. */ 81 RTThreadUserSignal(hThreadSelf); 82 83 if (RT_SUCCESS(rc)) 84 { 85 /* Provide helper with host clipboard service connection handle. */ 86 g_pWaylandHelperClipboard->pfnSetClipboardCtx(&ctx.CmdCtx); 87 88 /* Process host events. */ 89 while (!ASMAtomicReadBool(&g_fShutdown)) 90 { 91 rc = VBClClipboardReadHostEvent(&ctx, g_pWaylandHelperClipboard->pfnHGClipReport, 92 g_pWaylandHelperClipboard->pfnGHClipRead); 93 if (RT_FAILURE(rc)) 94 { 95 VBClLogInfo("cannot process host clipboard event, rc=%Rrc\n", rc); 96 RTThreadSleep(RT_MS_1SEC / 2); 97 } 98 } 99 100 VbglR3ClipboardDisconnectEx(&ctx.CmdCtx); 101 } 102 103 VBClLogVerbose(2, "clipboard thread, rc=%Rrc\n", rc); 104 105 return rc; 106 } 107 108 /** 109 * Worker for Drag-and-Drop events from host. 110 * 111 * @returns IPRT status code. 112 * @param hThreadSelf IPRT thread handle. 113 * @param pvUser User data (unused). 114 */ 115 static DECLCALLBACK(int) vbclWaylandDndWorker(RTTHREAD hThreadSelf, void *pvUser) 116 { 117 RT_NOREF(pvUser); 118 119 RTThreadUserSignal(hThreadSelf); 120 return VINF_SUCCESS; 121 } 122 123 /** 124 * Worker for VM window focus change polling thread. 125 * 126 * Some Wayland helpers need to be notified about VM 127 * window focus change events. This is needed in order to 128 * ask about, for example, if guest clipboard content was 129 * changed since last user interaction. Such guest are not 130 * able to notify host about clipboard content change and 131 * needed to be asked implicitly. 132 * 133 * @returns IPRT status code. 134 * @param hThreadSelf IPRT thread handle. 135 * @param pvUser User data (unused). 136 */ 137 static DECLCALLBACK(int) vbclWaylandHostInputFocusWorker(RTTHREAD hThreadSelf, void *pvUser) 138 { 139 int rc; 140 141 RT_NOREF(pvUser); 142 143 HGCMCLIENTID idClient; 144 145 rc = VbglR3GuestPropConnect(&idClient); 146 147 RTThreadUserSignal(hThreadSelf); 148 149 if (RT_SUCCESS(rc)) 150 { 151 while (!ASMAtomicReadBool(&g_fShutdown)) 152 { 153 static char achBuf[GUEST_PROP_MAX_NAME_LEN]; 154 char *pszName = NULL; 155 char *pszValue = NULL; 156 char *pszFlags = NULL; 157 bool fWasDeleted = false; 158 uint64_t u64Timestamp = 0; 159 160 rc = VbglR3GuestPropWait(idClient, VBOX_GUI_FOCUS_CHANGE_GUEST_PROP_NAME, achBuf, sizeof(achBuf), u64Timestamp, 161 VBCL_WAYLAND_WAIT_HOST_FOCUS_TIMEOUT_MS, &pszName, &pszValue, &u64Timestamp, 162 &pszFlags, NULL, &fWasDeleted); 163 if (RT_SUCCESS(rc)) 164 { 165 uint32_t fFlags = 0; 166 167 VBClLogVerbose(1, "guest property change: name: %s, val: %s, flags: %s, fWasDeleted: %RTbool\n", 168 pszName, pszValue, pszFlags, fWasDeleted); 169 170 if (RT_SUCCESS(GuestPropValidateFlags(pszFlags, &fFlags))) 171 { 172 if (RTStrNCmp(pszName, VBOX_GUI_FOCUS_CHANGE_GUEST_PROP_NAME, GUEST_PROP_MAX_NAME_LEN) == 0) 173 { 174 if (fFlags & GUEST_PROP_F_RDONLYGUEST) 175 { 176 if (RT_VALID_PTR(g_pWaylandHelperClipboard)) 177 { 178 if (RTStrNCmp(pszValue, "0", GUEST_PROP_MAX_NAME_LEN) == 0) 179 { 180 rc = g_pWaylandHelperClipboard->pfnPopup(); 181 VBClLogVerbose(1, "trigger popup, rc=%Rrc\n", rc); 182 } 183 } 184 else 185 VBClLogVerbose(1, "will not trigger popup\n"); 186 } 187 else 188 VBClLogError("property has invalid attributes\n"); 189 } 190 else 191 VBClLogVerbose(1, "unknown property name '%s'\n", pszName); 192 193 } else 194 VBClLogError("guest property change: name: %s, val: %s, flags: %s, fWasDeleted: %RTbool: bad flags\n", 195 pszName, pszValue, pszFlags, fWasDeleted); 196 197 } else if ( rc != VERR_TIMEOUT 198 && rc != VERR_INTERRUPTED) 199 { 200 VBClLogError("error on waiting guest property notification, rc=%Rrc\n", rc); 201 RTThreadSleep(VBCL_WAYLAND_WAIT_HOST_FOCUS_RELAX_MS); 202 } 203 } 204 } 205 206 return rc; 207 } 208 42 209 43 210 /** … … 68 235 /* Try Clipboard helper. */ 69 236 if ( fCaps & VBOX_WAYLAND_HELPER_CAP_CLIPBOARD 70 && !RT_VALID_PTR(g_pWaylandHelper HelperClipboard))237 && !RT_VALID_PTR(g_pWaylandHelperClipboard)) 71 238 { 72 239 if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit)) … … 74 241 rc = g_apWaylandHelpers[idxHelper]->pfnInit(); 75 242 if (RT_SUCCESS(rc)) 76 g_pWaylandHelper HelperClipboard = g_apWaylandHelpers[idxHelper];243 g_pWaylandHelperClipboard = g_apWaylandHelpers[idxHelper]; 77 244 else 78 VBClLogError("Wayland helper '%s' cannot be initialized, skipping"); 245 VBClLogError("Wayland helper '%s' cannot be initialized, skipping\n", 246 g_apWaylandHelpers[idxHelper]->pszName); 79 247 } 248 else 249 VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n", 250 g_apWaylandHelpers[idxHelper]->pszName); 80 251 } 81 252 82 253 /* Try DnD helper. */ 83 254 if ( fCaps & VBOX_WAYLAND_HELPER_CAP_DND 84 && !RT_VALID_PTR(g_pWaylandHelper HelperDnd))255 && !RT_VALID_PTR(g_pWaylandHelperDnd)) 85 256 { 86 257 if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit)) … … 88 259 rc = g_apWaylandHelpers[idxHelper]->pfnInit(); 89 260 if (RT_SUCCESS(rc)) 90 g_pWaylandHelper HelperDnd = g_apWaylandHelpers[idxHelper];261 g_pWaylandHelperDnd = g_apWaylandHelpers[idxHelper]; 91 262 else 92 VBClLogError("Wayland helper '%s' cannot be initialized, skipping"); 263 VBClLogError("Wayland helper '%s' cannot be initialized, skipping\n", 264 g_apWaylandHelpers[idxHelper]->pszName); 93 265 } 266 else 267 VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n", 268 g_apWaylandHelpers[idxHelper]->pszName); 94 269 } 95 270 } 96 271 97 272 /* See if we found all the needed helpers. */ 98 if ( RT_VALID_PTR(g_pWaylandHelper HelperClipboard)99 && RT_VALID_PTR(g_pWaylandHelper HelperDnd))273 if ( RT_VALID_PTR(g_pWaylandHelperClipboard) 274 && RT_VALID_PTR(g_pWaylandHelperDnd)) 100 275 break; 101 276 … … 104 279 105 280 /* Check result. */ 106 if (RT_VALID_PTR(g_pWaylandHelper HelperClipboard))107 VBClLogInfo("found Wayland Shared Clipboard helper '%s'\n", g_pWaylandHelper HelperClipboard->pszName);281 if (RT_VALID_PTR(g_pWaylandHelperClipboard)) 282 VBClLogInfo("found Wayland Shared Clipboard helper '%s'\n", g_pWaylandHelperClipboard->pszName); 108 283 else 109 284 VBClLogError("Wayland Shared Clipboard helper not found, clipboard sharing not possible\n"); 110 285 111 286 /* Check result. */ 112 if (RT_VALID_PTR(g_pWaylandHelper HelperDnd))113 VBClLogInfo("found Wayland Drag-and-Drop helper '%s'\n", g_pWaylandHelper HelperDnd->pszName);287 if (RT_VALID_PTR(g_pWaylandHelperDnd)) 288 VBClLogInfo("found Wayland Drag-and-Drop helper '%s'\n", g_pWaylandHelperDnd->pszName); 114 289 else 115 290 VBClLogError("Wayland Drag-and-Drop helper not found, drag-and-drop not possible\n"); … … 123 298 static DECLCALLBACK(int) vbclWaylandWorker(bool volatile *pfShutdown) 124 299 { 300 int rc = VINF_SUCCESS; 301 125 302 RT_NOREF(pfShutdown); 126 return VERR_NOT_SUPPORTED; 303 304 VBClLogVerbose(1, "starting wayland worker thread\n"); 305 306 /* Start event loop for clipboard events processing from host. */ 307 if (RT_VALID_PTR(g_pWaylandHelperClipboard)) 308 { 309 rc = VBClClipboardThreadStart(&g_ClipboardThread, vbclWaylandClipboardWorker, "wl-clip", NULL); 310 VBClLogVerbose(1, "clipboard thread started, rc=%Rrc\n", rc); 311 } 312 313 /* Start event loop for DnD events processing from host. */ 314 if ( RT_SUCCESS(rc) 315 && RT_VALID_PTR(g_pWaylandHelperDnd)) 316 { 317 rc = VBClClipboardThreadStart(&g_DndThread, vbclWaylandDndWorker, "wl-dnd", NULL); 318 VBClLogVerbose(1, "DnD thread started, rc=%Rrc\n", rc); 319 } 320 321 /* Start polling host input focus events. */ 322 if (RT_SUCCESS(rc)) 323 { 324 rc = VBClClipboardThreadStart(&g_HostInputFocusThread, vbclWaylandHostInputFocusWorker, "wl-focus", NULL); 325 VBClLogVerbose(1, "host input focus polling thread started, rc=%Rrc\n", rc); 326 } 327 328 /* Notify parent thread that we are successfully started. */ 329 RTThreadUserSignal(RTThreadSelf()); 330 331 if (RT_SUCCESS(rc)) 332 { 333 int rcThread = VINF_SUCCESS; 334 335 if (RT_VALID_PTR(g_pWaylandHelperClipboard)) 336 { 337 rc = RTThreadWait(g_ClipboardThread, RT_INDEFINITE_WAIT, &rcThread); 338 VBClLogVerbose(1, "clipboard thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread); 339 } 340 341 if ( RT_SUCCESS(rc) 342 && RT_VALID_PTR(g_pWaylandHelperDnd)) 343 { 344 rc = RTThreadWait(g_DndThread, RT_INDEFINITE_WAIT, &rcThread); 345 VBClLogVerbose(1, "DnD thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread); 346 } 347 348 if (RT_SUCCESS(rc)) 349 { 350 rc = RTThreadWait(g_HostInputFocusThread, RT_INDEFINITE_WAIT, &rcThread); 351 VBClLogVerbose(1, "host input focus polling thread finished, rc=%Rrc, rcThread=%Rrc\n", rc, rcThread); 352 } 353 } 354 355 VBClLogVerbose(1, "wayland worker thread finished, rc=%Rrc\n", rc); 356 357 return rc; 127 358 } 128 359 … … 132 363 static DECLCALLBACK(void) vbclWaylandStop(void) 133 364 { 365 VBClLogVerbose(1, "terminating wayland service: clipboard & DnD host event loops\n"); 366 367 /* This callback can be called twice (not good, needs to be fixed). Already was shut down? */ 368 if (ASMAtomicReadBool(&g_fShutdown)) 369 return; 370 371 ASMAtomicWriteBool(&g_fShutdown, true); 372 373 if (RT_VALID_PTR(g_pWaylandHelperClipboard)) 374 RTThreadPoke(g_ClipboardThread); 375 376 if (RT_VALID_PTR(g_pWaylandHelperDnd)) 377 RTThreadPoke(g_DndThread); 134 378 } 135 379 … … 139 383 static DECLCALLBACK(int) vbclWaylandTerm(void) 140 384 { 141 return VERR_NOT_SUPPORTED; 385 int rc = VINF_SUCCESS; 386 387 VBClLogVerbose(1, "shutting down wayland service: clipboard & DnD helpers\n"); 388 389 if ( RT_VALID_PTR(g_pWaylandHelperClipboard) 390 && RT_VALID_PTR(g_pWaylandHelperClipboard->pfnTerm)) 391 rc = g_pWaylandHelperClipboard->pfnTerm(); 392 393 if ( RT_SUCCESS(rc) 394 && RT_VALID_PTR(g_pWaylandHelperDnd) 395 && RT_VALID_PTR(g_pWaylandHelperDnd->pfnTerm)) 396 rc = g_pWaylandHelperDnd->pfnTerm(); 397 398 return rc; 142 399 } 143 400
Note:
See TracChangeset
for help on using the changeset viewer.