VirtualBox

Ignore:
Timestamp:
Nov 6, 2023 3:36:24 PM (15 months ago)
Author:
vboxsync
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/wayland.cpp

    r100248 r101878  
    2626 */
    2727
     28#include <iprt/asm.h>
     29#include <iprt/thread.h>
     30
     31#include <VBox/HostServices/GuestPropertySvc.h>
     32
    2833#include "VBoxClient.h"
     34#include "clipboard.h"
    2935#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)
    3041
    3142/** List of available Wayland Desktop Environment helpers. Sorted in order of preference. */
    3243static const VBCLWAYLANDHELPER *g_apWaylandHelpers[] =
    3344{
     45    &g_WaylandHelperDcp,    /* Device Control Protocol helper. */
    3446    &g_WaylandHelperGtk,    /* GTK helper. */
    35     &g_WaylandHelperDcp,    /* Device Control Protocol helper. */
    3647    NULL,                   /* Terminate list. */
    3748};
    3849
     50/** Global flag to tell service to go shutdown when needed. */
     51static bool volatile g_fShutdown = false;
     52
    3953/** Selected helpers for Clipboard and Drag-and-Drop. */
    40 static const VBCLWAYLANDHELPER *g_pWaylandHelperHelperClipboard = NULL;
    41 static const VBCLWAYLANDHELPER *g_pWaylandHelperHelperDnd       = NULL;
     54static const VBCLWAYLANDHELPER *g_pWaylandHelperClipboard = NULL;
     55static const VBCLWAYLANDHELPER *g_pWaylandHelperDnd       = NULL;
     56
     57/** Corresponding threads for host events handling. */
     58static RTTHREAD g_ClipboardThread;
     59static RTTHREAD g_DndThread;
     60static 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 */
     69static 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 */
     115static 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 */
     137static 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
    42209
    43210/**
     
    68235            /* Try Clipboard helper. */
    69236            if (   fCaps & VBOX_WAYLAND_HELPER_CAP_CLIPBOARD
    70                 && !RT_VALID_PTR(g_pWaylandHelperHelperClipboard))
     237                && !RT_VALID_PTR(g_pWaylandHelperClipboard))
    71238            {
    72239                if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit))
     
    74241                    rc = g_apWaylandHelpers[idxHelper]->pfnInit();
    75242                    if (RT_SUCCESS(rc))
    76                         g_pWaylandHelperHelperClipboard = g_apWaylandHelpers[idxHelper];
     243                        g_pWaylandHelperClipboard = g_apWaylandHelpers[idxHelper];
    77244                    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);
    79247                }
     248                else
     249                    VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n",
     250                                   g_apWaylandHelpers[idxHelper]->pszName);
    80251            }
    81252
    82253            /* Try DnD helper. */
    83254            if (   fCaps & VBOX_WAYLAND_HELPER_CAP_DND
    84                 && !RT_VALID_PTR(g_pWaylandHelperHelperDnd))
     255                && !RT_VALID_PTR(g_pWaylandHelperDnd))
    85256            {
    86257                if (RT_VALID_PTR(g_apWaylandHelpers[idxHelper]->pfnInit))
     
    88259                    rc = g_apWaylandHelpers[idxHelper]->pfnInit();
    89260                    if (RT_SUCCESS(rc))
    90                         g_pWaylandHelperHelperDnd = g_apWaylandHelpers[idxHelper];
     261                        g_pWaylandHelperDnd = g_apWaylandHelpers[idxHelper];
    91262                    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);
    93265                }
     266                else
     267                    VBClLogVerbose(1, "Wayland helper '%s' has no initializer, skipping\n",
     268                                   g_apWaylandHelpers[idxHelper]->pszName);
    94269            }
    95270        }
    96271
    97272        /* See if we found all the needed helpers. */
    98         if (   RT_VALID_PTR(g_pWaylandHelperHelperClipboard)
    99             && RT_VALID_PTR(g_pWaylandHelperHelperDnd))
     273        if (   RT_VALID_PTR(g_pWaylandHelperClipboard)
     274            && RT_VALID_PTR(g_pWaylandHelperDnd))
    100275            break;
    101276
     
    104279
    105280    /* Check result. */
    106     if (RT_VALID_PTR(g_pWaylandHelperHelperClipboard))
    107         VBClLogInfo("found Wayland Shared Clipboard helper '%s'\n", g_pWaylandHelperHelperClipboard->pszName);
     281    if (RT_VALID_PTR(g_pWaylandHelperClipboard))
     282        VBClLogInfo("found Wayland Shared Clipboard helper '%s'\n", g_pWaylandHelperClipboard->pszName);
    108283    else
    109284        VBClLogError("Wayland Shared Clipboard helper not found, clipboard sharing not possible\n");
    110285
    111286    /* Check result. */
    112     if (RT_VALID_PTR(g_pWaylandHelperHelperDnd))
    113         VBClLogInfo("found Wayland Drag-and-Drop helper '%s'\n", g_pWaylandHelperHelperDnd->pszName);
     287    if (RT_VALID_PTR(g_pWaylandHelperDnd))
     288        VBClLogInfo("found Wayland Drag-and-Drop helper '%s'\n", g_pWaylandHelperDnd->pszName);
    114289    else
    115290        VBClLogError("Wayland Drag-and-Drop helper not found, drag-and-drop not possible\n");
     
    123298static DECLCALLBACK(int) vbclWaylandWorker(bool volatile *pfShutdown)
    124299{
     300    int rc = VINF_SUCCESS;
     301
    125302    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;
    127358}
    128359
     
    132363static DECLCALLBACK(void) vbclWaylandStop(void)
    133364{
     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);
    134378}
    135379
     
    139383static DECLCALLBACK(int) vbclWaylandTerm(void)
    140384{
    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;
    142399}
    143400
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette