VirtualBox

Ignore:
Timestamp:
Sep 3, 2014 2:38:53 PM (10 years ago)
Author:
vboxsync
Message:

Additions/x11/VBoxClient: re-do virtual terminal switch monitoring to work with the kernel driver on X.Org Server 1.16 and later.

Location:
trunk/src/VBox/Additions/x11/VBoxClient
Files:
5 edited

Legend:

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

    r52562 r52586  
    5050    /** Run the service main loop */
    5151    int (*run)(struct VBCLSERVICE **ppInterface, bool fDaemonised);
    52     /** Pause the service loop.  This must be safe to call on a different thread
    53      * and potentially before @a run is or after it exits.
    54      * This is called by the VT monitoring thread to allow the service to disable
    55      * itself when the X server is switched out.  If the monitoring functionality
    56      * is available then @a pause or @a resume will be called as soon as it starts
    57      * up. */
     52    /** Pause the service loop.  This is used to allow the service to disable
     53     * itself when the X server is switched out.  It must be safe to call on a
     54     * different thread if the VT monitoring thread is used. */
    5855    int (*pause)(struct VBCLSERVICE **ppInterface);
    5956    /** Resume after pausing.  The same applies here as for @a pause. */
     
    7976}
    8077
     78union _XEvent;  /* We do not want to pull in the X11 header files here. */
     79extern void VBClCheckXOrgVT(union _XEvent *pEvent);
     80extern int VBClStartVTMonitor();
     81
    8182extern struct VBCLSERVICE **VBClGetClipboardService();
    8283extern struct VBCLSERVICE **VBClGetSeamlessService();
  • trunk/src/VBox/Additions/x11/VBoxClient/display.cpp

    r52562 r52586  
    349349{
    350350    struct DISPLAYSERVICE *pSelf = getClassFromInterface(ppInterface);
     351    int rc;
    351352
    352353    if (!pSelf->mfInit)
    353354        return VERR_WRONG_ORDER;
     355    rc = VBClStartVTMonitor();
     356    if (RT_FAILURE(rc))
     357        VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
    354358    runDisplay(&pSelf->mState);
    355359    return VERR_INTERNAL_ERROR;  /* "Should never reach here." */
  • trunk/src/VBox/Additions/x11/VBoxClient/main.cpp

    r52577 r52586  
    150150}
    151151
    152 /** Connect to the X server and return the "XFree86_VT" root window property,
    153  * or 0 on failure. */
    154 static unsigned long getXOrgVT(Display *pDisplay)
     152/** Check whether X.Org has acquired or lost the current virtual terminal and
     153 * call the service @a pause() or @a resume() call-back if appropriate.
     154 * The functionality is provided by the vboxvideo driver for pre-1.16 X servers
     155 * and by 1.16 and later series servers.
     156 * This can either be called directly from a service's event loop or the service
     157 * can call VBClStartVTMonitor() to start an event loop in a separate thread.
     158 * Property notification for the root window should be selected first.  Services
     159 * are not required to check VT changes if they do not need the information.
     160 * @param  pEvent an event received on a display connection which will be
     161 *                checked to see if it is change to the XFree86_has_VT property
     162 */
     163void VBClCheckXOrgVT(union _XEvent *pEvent)
    155164{
    156165    Atom actualType;
    157166    int actualFormat;
    158     unsigned long cItems, cbLeft, cVT = 0;
     167    unsigned long cItems, cbLeft;
     168    bool fHasVT = false;
    159169    unsigned long *pValue;
    160 
    161     XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay),
    162                        XInternAtom(pDisplay, "XFree86_VT", False), 0, 1, False,
    163                        XA_INTEGER, &actualType, &actualFormat, &cItems, &cbLeft,
    164                        (unsigned char **)&pValue);
     170    int rc;
     171    Display *pDisplay = pEvent->xany.display;
     172    Atom hasVT = XInternAtom(pDisplay, "XFree86_has_VT", False);
     173
     174    if (   pEvent->type != PropertyNotify
     175        || pEvent->xproperty.window != DefaultRootWindow(pDisplay)
     176        || pEvent->xproperty.atom != hasVT)
     177        return;
     178    XGetWindowProperty(pDisplay, DefaultRootWindow(pDisplay), hasVT, 0, 1,
     179                       False, XA_INTEGER, &actualType, &actualFormat, &cItems,
     180                       &cbLeft, (unsigned char **)&pValue);
    165181    if (cItems && actualFormat == 32)
    166182    {
    167         cVT = *pValue;
     183        fHasVT = *pValue != 0;
    168184        XFree(pValue);
    169185    }
    170     return cVT;
    171 }
    172 
    173 /** Check whether the current virtual terminal is the one running the X server.
    174  */
    175 static void checkVTSysfs(RTFILE hFile, uint32_t cVT)
    176 {
    177     char szTTY[7] = "";
    178     uint32_t cTTY;
    179     size_t cbRead;
    180     int rc;
    181     const char *pcszStage;
    182 
    183     do {
    184         pcszStage = "reading /sys/class/tty/tty0/active";
    185         rc = RTFileReadAt(hFile, 0, (void *)szTTY, sizeof(szTTY), &cbRead);
     186    else
     187        return;
     188    if (fHasVT)
     189    {
     190        rc = (*g_pService)->resume(g_pService);
    186191        if (RT_FAILURE(rc))
    187             break;
    188         szTTY[cbRead - 1] = '\0';
    189         pcszStage = "getting VT number from sysfs file";
    190         rc = RTStrToUInt32Full(&szTTY[3], 10, &cTTY);
     192            VBClFatalError(("Error resuming the service: %Rrc\n"));
     193    }
     194    if (!fHasVT)
     195    {
     196        rc = (*g_pService)->pause(g_pService);
    191197        if (RT_FAILURE(rc))
    192             break;
    193         pcszStage = "entering critical section";
    194         rc = RTCritSectEnter(&g_critSect);
    195         if (RT_FAILURE(rc))
    196             break;
    197         pcszStage = "asking service to pause or resume";
    198         if (cTTY == cVT)
    199             rc = (*g_pService)->resume(g_pService);
    200         else
    201             rc = (*g_pService)->pause(g_pService);
    202         if (RT_FAILURE(rc))
    203             break;
    204         pcszStage = "leaving critical section";
    205         rc = RTCritSectLeave(&g_critSect);
    206     } while(false);
    207     if (RT_FAILURE(rc))
    208     {
    209         LogRelFunc(("VBoxClient: failed at stage: \"%s\" rc: %Rrc cVT: %d szTTY: %s.\n",
    210                     pcszStage, rc, (int) cVT, szTTY));
    211         if (RTCritSectIsOwner(&g_critSect))
    212             RTCritSectLeave(&g_critSect);
    213         VBClCleanUp();
    214     }
    215 }
    216 
    217 /** Poll for TTY changes using sysfs and for X server disconnection.
    218  * Reading from the start of the pollable file "/sys/class/tty/tty0/active"
    219  * returns the currently active TTY as a string of the form "tty<n>", with n
    220  * greater than zero.  Polling for POLLPRI returns when the TTY changes.
    221  * @a cVT should be zero if we do not know the X server's VT. */
    222 static void pollTTYAndXServer(Display *pDisplay, uint32_t cVT)
    223 {
    224     RTFILE hFile = NIL_RTFILE;
    225     struct pollfd pollFD[2];
    226     unsigned cPollFD = 1;
    227     int rc;
    228 
    229     pollFD[1].fd = -1;
    230     pollFD[1].revents = 0;
    231     /* This block could be Linux-only, but keeping it on Solaris too, where it
    232      * should just fail gracefully, gives us more code path coverage. */
    233     if (cVT)
    234     {
    235         rc = RTFileOpen(&hFile, "/sys/class/tty/tty0/active",
    236                         RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
    237         if (RT_SUCCESS(rc))
    238         {
    239             pollFD[1].fd = RTFileToNative(hFile);
    240             pollFD[1].events = POLLPRI;
    241             cPollFD = 2;
    242         }
    243     }
    244     AssertRelease(pollFD[1].fd >= 0 || cPollFD == 1);
    245     pollFD[0].fd = ConnectionNumber(pDisplay);
    246     pollFD[0].events = POLLIN;
    247     while (true)
    248     {
    249         if (hFile != NIL_RTFILE)
    250             checkVTSysfs(hFile, cVT);
    251         /* The only point of this loop is to trigger the I/O error handler if
    252          * appropriate. */
    253         while (XPending(pDisplay))
    254         {
    255             XEvent ev;
    256 
    257             XNextEvent(pDisplay, &ev);
    258         }
    259         /* If we get caught in a tight loop for some reason try to limit the
    260          * damage. */
    261         if (poll(pollFD, cPollFD, 0) > 0)
    262         {
    263             LogRel(("Monitor thread: unexpectedly fast event, revents=0x%x, 0x%x.\n",
    264                     pollFD[0].revents, pollFD[1].revents));
    265             RTThreadYield();
    266         }
    267         if (   (poll(pollFD, cPollFD, -1) < 0 && errno != EINTR)
    268             || pollFD[0].revents & POLLNVAL
    269             || pollFD[1].revents & POLLNVAL)
    270         {
    271             LogRel(("Monitor thread: poll failed, stopping.\n"));
    272             VBClCleanUp();
    273         }
     198            VBClFatalError(("Error pausing the service: %Rrc\n"));
    274199    }
    275200}
     
    283208{
    284209    Display *pDisplay;
    285     unsigned long cVT;
    286     RTFILE hFile;
     210    bool fHasVT = true;
    287211   
    288212    pDisplay = XOpenDisplay(NULL);
    289213    if (!pDisplay)
    290         return VINF_SUCCESS;
    291     cVT = getXOrgVT(pDisplay);
    292     /* Note: cVT will be 0 if we failed to get it.  This is valid. */
    293     pollTTYAndXServer(pDisplay, (uint32_t) cVT);
    294     /* Should never get here. */
    295     return VINF_SUCCESS;
    296 }
    297 
    298 /**
    299  * Start the thread which notifies the service when we switch to a different
    300  * VT or back, and terminates us when the X server exits.  The first is best
    301  * effort functionality: XFree86 4.3 and older do not report their VT via the
    302  * "XFree86_VT" root window property at all, and pre-2.6.38 Linux does not
    303  * provide the interface in "sysfs" which we use.  If there is a need for this
    304  * to work with pre-2.6.38 Linux we can send the VT_GETSTATE ioctl to
    305  * /dev/console at regular intervals.
    306  */
    307 static int startMonitorThread()
     214        VBClFatalError(("Failed to open the X11 display\n"));
     215    XSelectInput(pDisplay, DefaultRootWindow(pDisplay), PropertyChangeMask);
     216    while (true)
     217    {
     218        XEvent event;
     219
     220        XNextEvent(pDisplay, &event);
     221        VBClCheckXOrgVT(&event);
     222    }
     223    return VINF_SUCCESS;  /* Should never be reached. */
     224}
     225
     226/**
     227 * Start a thread which notifies the service when we switch to a different
     228 * VT or back, and terminates us when the X server exits.  This should be called
     229 * by most services which do not regularly run an X11 event loop.
     230 */
     231int VBClStartVTMonitor()
    308232{
    309233    return RTThreadCreate(NULL, pfnMonitorThread, NULL, 0,
     
    441365    if (RT_FAILURE(rc))
    442366        VBClFatalError(("Initialising critical section: %Rrc\n", rc));
    443     if (RT_FAILURE(rc))
    444         VBClFatalError(("Initialising critical section: %Rrc\n", rc));
    445367    rc = RTPathUserHome(g_szPidFile, sizeof(g_szPidFile));
    446368    if (RT_FAILURE(rc))
     
    469391    if (RT_FAILURE(rc))
    470392        VBClFatalError(("Initialising service: %Rrc\n", rc));
    471     rc = startMonitorThread();
    472     if (RT_FAILURE(rc))
    473         VBClFatalError(("Starting monitor thread: %Rrc\n", rc));
    474     (*g_pService)->run(g_pService, fDaemonise);  /* Should never return. */
     393    rc = (*g_pService)->run(g_pService, fDaemonise);
     394    if (RT_FAILURE(rc))
     395        VBClFatalError(("Service main loop failed: %Rrc\n", rc));
    475396    VBClCleanUp();
    476     return 1;
    477 }
     397    return 0;
     398}
  • trunk/src/VBox/Additions/x11/VBoxClient/seamless.cpp

    r52564 r52586  
    377377    if (!pSelf->mIsInitialised)
    378378        return VERR_INTERNAL_ERROR;
     379    rc = VBClStartVTMonitor();
     380    if (RT_FAILURE(rc))
     381        VBClFatalError(("Failed to start the VT monitor thread: %Rrc\n", rc));
    379382    /* This only exits on error. */
    380383    rc = pSelf->mSeamless.run();
  • trunk/src/VBox/Additions/x11/VBoxClient/testcase/tstSeamlessX11.cpp

    r52562 r52586  
    3131    RTPrintf("Fatal error: %s", pszMessage);
    3232    exit(1);
     33}
     34
     35int VBClStartVTMonitor()
     36{
     37    return VINF_SUCCESS;
    3338}
    3439
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