Index: trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk
===================================================================
--- trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk	(revision 93370)
+++ trunk/src/VBox/Additions/x11/VBoxClient/Makefile.kmk	(revision 93371)
@@ -56,4 +56,5 @@
 VBoxDRMClient_SOURCES = \
     display-drm.cpp \
+    display-ipc.cpp \
     logging.cpp
 
@@ -125,5 +126,11 @@
  VBoxClient_DEFS    += VBOX_WITH_VMSVGA
  VBoxClient_SOURCES += \
-	display-svga-x11.cpp
+	display-svga-x11.cpp \
+	display-svga-session.cpp \
+	display-ipc.cpp \
+	display-helper-gnome3.cpp \
+	display-helper-generic.cpp
+
+### include $(PATH_SUB_CURRENT)/helpers/Makefile.kmk
 endif
 
Index: trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp
===================================================================
--- trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp	(revision 93370)
+++ trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp	(revision 93371)
@@ -1,6 +1,7 @@
 /* $Id$ */
 /** @file
- * X11 guest client - VMSVGA emulation resize event pass-through to drm guest
- * driver.
+ * A user space daemon which communicates with VirtualBox host interface
+ * and performs VMSVGA-specific guest screen resize and communicates with
+ * Desktop Environment helper daemon over IPC.
  */
 
@@ -18,28 +19,62 @@
 
 /*
- * Known things to test when changing this code.  All assume a guest with VMSVGA
- * active and controlled by X11 or Wayland, and Guest Additions installed and
- * running, unless otherwise stated.
- *  - On Linux 4.6 and later guests, VBoxClient --vmsvga should be running as
- *    root and not as the logged-in user.  Dynamic resizing should work for all
- *    screens in any environment which handles kernel resize notifications,
- *    including at log-in screens.  Test GNOME Shell Wayland and GNOME Shell
- *    under X.Org or Unity or KDE at the log-in screen and after log-in.
- *  - Linux 4.10 changed the user-kernel-ABI introduced in 4.6: test both.
- *  - On other guests (than Linux 4.6 or later) running X.Org Server 1.3 or
- *    later, VBoxClient --vmsvga should never be running as root, and should run
- *    (and dynamic resizing and screen enable/disable should work for all
- *    screens) whenever a user is logged in to a supported desktop environment.
- *  - On guests running X.Org Server 1.2 or older, VBoxClient --vmsvga should
- *    never run as root and should run whenever a user is logged in to a
- *    supported desktop environment.  Dynamic resizing should work for the first
- *    screen, and enabling others should not be possible.
- *  - When VMSVGA is not enabled, VBoxClient --vmsvga should never stay running.
+ * General notes
+ *
+ * This service supposed to be started on early boot. On start it will try to find
+ * compatible VMSVGA graphics card and terminate immediately if not found.
+ * VMSVGA functionality implemented here is only supported starting from vmgfx
+ * driver version 2.10 which was introduced in vanilla Linux kernel 4.6. When compatible
+ * graphics card is found, service will start a worker loop in order to receive screen
+ * update data from host and apply it to local DRM stack.
+ *
+ * In addition, it will start a local IPC server in order to communicate with Desktop
+ * Environment specific service(s). Currently, it will propagate to IPC client information
+ * which display should be set as primary on Desktop Environment level. As well as
+ * receive screen layout change events obtained on Desktop Environment level and send it
+ * back to host, so host and guest will have the same screen layout representation.
+ *
+ * Logging is implemented in a way that errors are always printed out, VBClLogVerbose(1) and
+ * VBClLogVerbose(2) are used for debugging purposes. Verbosity level 1 is for messages related
+ * to daemon itself (excluding IPC), level 2 is for IPC communication debugging. In order to see
+ * logging on a host side it is enough to do:
+ *
+ *     echo 1 > /sys/module/vboxguest/parameters/r3_log_to_host.
+ *
+ *
+ * Threads
+ *
+ * DrmResizeThread - this thread listens for display layout update events from host.
+ *     Once event is received, it either injects new screen layout data into DRM stack,
+ *     and/or asks IPC client(s) to set primary display. This thread is accessing IPC
+ *     client connection list when it needs to sent new primary display data to all the
+ *     connected clients.
+ *
+ * DrmIpcSRV - this thread is a main loop for IPC server. It accepts new connection(s),
+ *     authenticates it and starts new client thread IpcCLT-XXX for processing client
+ *     requests. This thread is accessing IPC client connection list by adding a new
+ *     connection data into it.
+ *
+ * IpcCLT-%u - this thread processes all the client data. Suffix '-%u' in thread name is PID
+ *     of a remote client process. Typical name for client thread would be IpcCLT-1234. This
+ *     thread is accessing IPC client connection list when it removes connection data from it
+ *     when actual IPC connection is closed. Due to IPRT thread name limitation, actual thread
+ *     name will be cropped by 15 characters.
+ *
+ *
+ * Locking
+ *
+ * g_ipcClientConnectionsListCritSect - protects access to list of IPC client connections.
+ *     It is used by each thread - DrmResizeThread, DrmIpcSRV and IpcCLT-XXX.
+ *
+ * g_monitorPositionsCritSect - serializes access to host interface when guest Desktop
+ *     Environment reports display layout changes.
  */
 
 #include "VBoxClient.h"
+#include "display-ipc.h"
 
 #include <VBox/VBoxGuestLib.h>
 
+#include <iprt/getopt.h>
 #include <iprt/assert.h>
 #include <iprt/file.h>
@@ -50,8 +85,12 @@
 #include <iprt/thread.h>
 #include <iprt/asm.h>
+#include <iprt/localipc.h>
+
 #include <unistd.h>
 #include <stdio.h>
 #include <limits.h>
 #include <signal.h>
+#include <grp.h>
+#include <errno.h>
 
 #ifdef RT_OS_LINUX
@@ -76,14 +115,18 @@
 #define VMW_RENDER_DEVICE_MINOR_END     (192)
 
-/** Maximum number of supported screens.  DRM and X11 both limit this to 32. */
-/** @todo if this ever changes, dynamically allocate resizeable arrays in the
- *  context structure. */
-#define VMW_MAX_HEADS                   (32)
-
 /** Name of DRM resize thread. */
 #define DRM_RESIZE_THREAD_NAME          "DrmResizeThread"
 
-/* Time in milliseconds to wait for host events. */
-#define DRM_HOST_EVENT_RX_TIMEOUT_MS    (500)
+/** Name of DRM IPC server thread. */
+#define DRM_IPC_SERVER_THREAD_NAME      "DrmIpcSRV"
+/** Maximum length of thread name. */
+#define DRM_IPC_THREAD_NAME_MAX         (16)
+/** Name pattern of DRM IPC client thread. */
+#define DRM_IPC_CLIENT_THREAD_NAME_PTR  "IpcCLT-%u"
+/** Maximum number of simultaneous IPC client connections. */
+#define DRM_IPC_SERVER_CONNECTIONS_MAX  (16)
+
+/** IPC client connections counter. */
+static volatile uint32_t g_cDrmIpcConnections = 0;
 
 /** DRM version structure. */
@@ -102,14 +145,4 @@
 AssertCompileSize(struct DRMVERSION, 8 + 7 * sizeof(void *));
 
-/** Rectangle structure for geometry of a single screen. */
-struct DRMVMWRECT
-{
-    int32_t x;
-    int32_t y;
-    uint32_t w;
-    uint32_t h;
-};
-AssertCompileSize(struct DRMVMWRECT, 16);
-
 /** Preferred screen layout information for DRM_VMW_UPDATE_LAYOUT IoCtl.  The
  *  rects argument is a cast pointer to an array of drm_vmw_rect. */
@@ -122,6 +155,28 @@
 AssertCompileSize(struct DRMVMWUPDATELAYOUT, 16);
 
-/** These two parameters are mostly unused. Defined here in order to satisfy linking requirements. */
+/** A node of IPC client connections list. */
+typedef struct VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE
+{
+    /** The list node. */
+    RTLISTNODE      Node;
+    /** List node payload. */
+    PVBOX_DRMIPC_CLIENT   pClient;
+} VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE;
+
+/* Pointer to VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE. */
+typedef VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE *PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE;
+
+/** IPC client connections list.  */
+static VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE g_ipcClientConnectionsList;
+
+/** IPC client connections list critical section. */
+static RTCRITSECT g_ipcClientConnectionsListCritSect;
+
+/** Critical section used for reporting monitors position back to host. */
+static RTCRITSECT g_monitorPositionsCritSect;
+
+/** Counter of how often our daemon has been re-spawned. */
 unsigned g_cRespawn = 0;
+/** Logging verbosity level. */
 unsigned g_cVerbosity = 0;
 
@@ -133,12 +188,21 @@
 
 /**
+ * Go over all existing IPC client connection and put set-primary-screen request
+ * data into TX queue of each of them .
+ *
+ * @return  IPRT status code.
+ * @param   u32PrimaryDisplay   Primary display ID.
+ */
+static int vbDrmIpcBroadcastPrimaryDisplay(uint32_t u32PrimaryDisplay);
+
+/**
  * Attempts to open DRM device by given path and check if it is
- * compatible for screen resize.
- *
- * @return  DRM device handle on success or NIL_RTFILE otherwise.
+ * capable for screen resize.
+ *
+ * @return  DRM device handle on success, NIL_RTFILE otherwise.
  * @param   szPathPattern       Path name pattern to the DRM device.
  * @param   uInstance           Driver / device instance.
  */
-static RTFILE drmTryDevice(const char *szPathPattern, uint8_t uInstance)
+static RTFILE vbDrmTryDevice(const char *szPathPattern, uint8_t uInstance)
 {
     int rc = VERR_NOT_FOUND;
@@ -170,5 +234,5 @@
                         && vmwgfxVersion.cMinor >= DRM_DRIVER_VERSION_MINOR_MIN)))
             {
-                VBClLogInfo("VBoxDRMClient: found compatible device: %s\n", szPath);
+                VBClLogInfo("found compatible device: %s\n", szPath);
             }
             else
@@ -182,5 +246,5 @@
     else
     {
-        VBClLogError("VBoxDRMClient: unable to construct path to DRM device: %Rrc\n", rc);
+        VBClLogError("unable to construct path to DRM device: %Rrc\n", rc);
     }
 
@@ -191,7 +255,7 @@
  * Attempts to find and open DRM device to be used for screen resize.
  *
- * @return  DRM device handle on success or NIL_RTFILE otherwise.
- */
-static RTFILE drmOpenVmwgfx(void)
+ * @return  DRM device handle on success, NIL_RTFILE otherwise.
+ */
+static RTFILE vbDrmOpenVmwgfx(void)
 {
     /* Control devices for drm graphics driver control devices go from
@@ -207,5 +271,5 @@
     for (i = VMW_CONTROL_DEVICE_MINOR_START; i < VMW_RENDER_DEVICE_MINOR_START; i++)
     {
-        hDevice = drmTryDevice("/dev/dri/controlD%u", i);
+        hDevice = vbDrmTryDevice("/dev/dri/controlD%u", i);
         if (hDevice != NIL_RTFILE)
             return hDevice;
@@ -215,10 +279,10 @@
     for (i = VMW_RENDER_DEVICE_MINOR_START; i <= VMW_RENDER_DEVICE_MINOR_END; i++)
     {
-        hDevice = drmTryDevice("/dev/dri/renderD%u", i);
+        hDevice = vbDrmTryDevice("/dev/dri/renderD%u", i);
         if (hDevice != NIL_RTFILE)
             return hDevice;
     }
 
-    VBClLogError("VBoxDRMClient: unable to find DRM device\n");
+    VBClLogError("unable to find DRM device\n");
 
     return hDevice;
@@ -229,18 +293,20 @@
  * into monitors layout array to be passed to DRM stack.
  *
- * @return  VINF_SUCCESS on success, IPRT error code otherwise.
+ * @return  VINF_SUCCESS on success, VERR_DUPLICATE if monitors layout was not changed, IPRT error code otherwise.
  * @param   aDisplaysIn         Input displays array.
  * @param   cDisplaysIn         Number of elements in input displays array.
  * @param   aDisplaysOut        Output displays array.
  * @param   cDisplaysOutMax     Number of elements in output displays array.
+ * @param   pu32PrimaryDisplay  ID of a display which marked as primary.
  * @param   pcActualDisplays    Number of displays to report to DRM stack (number of enabled displays).
  */
-static int drmValidateLayout(VMMDevDisplayDef *aDisplaysIn, uint32_t cDisplaysIn,
-                             struct DRMVMWRECT *aDisplaysOut, uint32_t cDisplaysOutMax, uint32_t *pcActualDisplays)
+static int vbDrmValidateLayout(VMMDevDisplayDef *aDisplaysIn, uint32_t cDisplaysIn,
+                             struct VBOX_DRMIPC_VMWRECT *aDisplaysOut, uint32_t *pu32PrimaryDisplay,
+                             uint32_t cDisplaysOutMax, uint32_t *pcActualDisplays)
 {
     /* This array is a cache of what was received from DevVMM so far.
      * DevVMM may send to us partial information bout scree layout. This
      * cache remembers entire picture. */
-    static struct VMMDevDisplayDef aVmMonitorsCache[VMW_MAX_HEADS];
+    static struct VMMDevDisplayDef aVmMonitorsCache[VBOX_DRMIPC_MONITORS_MAX];
     /* Number of valid (enabled) displays in output array. */
     uint32_t cDisplaysOut = 0;
@@ -249,8 +315,8 @@
 
     /* Make sure input array fits cache size. */
-    if (cDisplaysIn > VMW_MAX_HEADS)
-    {
-        VBClLogError("VBoxDRMClient: unable to validate screen layout: input (%u) array does not fit to cache size (%u)\n",
-                     cDisplaysIn, VMW_MAX_HEADS);
+    if (cDisplaysIn > VBOX_DRMIPC_MONITORS_MAX)
+    {
+        VBClLogError("unable to validate screen layout: input (%u) array does not fit to cache size (%u)\n",
+                         cDisplaysIn, VBOX_DRMIPC_MONITORS_MAX);
         return VERR_INVALID_PARAMETER;
     }
@@ -259,6 +325,6 @@
     if (cDisplaysIn > cDisplaysOutMax)
     {
-        VBClLogError("VBoxDRMClient: unable to validate screen layout: input array (%u) is bigger than output one (%u)\n",
-                     cDisplaysIn, cDisplaysOut);
+        VBClLogError("unable to validate screen layout: input array (%u) is bigger than output one (%u)\n",
+                         cDisplaysIn, cDisplaysOut);
         return VERR_INVALID_PARAMETER;
     }
@@ -267,6 +333,6 @@
     if (!(cDisplaysIn > 0 && cDisplaysOutMax > 0))
     {
-        VBClLogError("VBoxDRMClient: unable to validate screen layout: invalid size of either input (%u) or output display array\n",
-                     cDisplaysIn, cDisplaysOutMax);
+        VBClLogError("unable to validate screen layout: invalid size of either input (%u) or output display array\n",
+                         cDisplaysIn, cDisplaysOutMax);
         return VERR_INVALID_PARAMETER;
     }
@@ -276,5 +342,5 @@
     {
         uint32_t idDisplay = aDisplaysIn[i].idDisplay;
-        if (idDisplay < VMW_MAX_HEADS)
+        if (idDisplay < VBOX_DRMIPC_MONITORS_MAX)
         {
             aVmMonitorsCache[idDisplay].idDisplay = idDisplay;
@@ -288,5 +354,5 @@
         else
         {
-            VBClLogError("VBoxDRMClient: received display ID (0x%x, position %u) is invalid\n", idDisplay, i);
+            VBClLogError("received display ID (0x%x, position %u) is invalid\n", idDisplay, i);
             /* If monitor configuration cannot be placed into cache, consider entire cache is invalid. */
             fValid = false;
@@ -295,5 +361,5 @@
 
     /* Now, go though complete cache and check if it is valid. */
-    for (uint32_t i = 0; i < VMW_MAX_HEADS; i++)
+    for (uint32_t i = 0; i < VBOX_DRMIPC_MONITORS_MAX; i++)
     {
         if (i == 0)
@@ -301,5 +367,5 @@
             if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
             {
-                VBClLogError("VBoxDRMClient: unable to validate screen layout: first monitor is not allowed to be disabled");
+                VBClLogError("unable to validate screen layout: first monitor is not allowed to be disabled\n");
                 fValid = false;
             }
@@ -309,20 +375,17 @@
         else
         {
-            /* Check if there is no hole in between monitors (i.e., if current monitor is enabled, but privious one does not). */
+            /* Check if there is no hole in between monitors (i.e., if current monitor is enabled, but previous one does not). */
             if (   !(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
                 && aVmMonitorsCache[i - 1].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
             {
-                VBClLogError("VBoxDRMClient: unable to validate screen layout: there is a hole in displays layout config, "
-                             "monitor (%u) is ENABLED while (%u) does not\n", i, i - 1);
+                VBClLogError("unable to validate screen layout: there is a hole in displays layout config, "
+                                 "monitor (%u) is ENABLED while (%u) does not\n", i, i - 1);
                 fValid = false;
             }
             else
             {
-                /* Align displays next to each other (if needed) and check if there is no holes in between monitors. */
-                if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
-                {
-                    aVmMonitorsCache[i].xOrigin = aVmMonitorsCache[i - 1].xOrigin + aVmMonitorsCache[i - 1].cx;
-                    aVmMonitorsCache[i].yOrigin = aVmMonitorsCache[i - 1].yOrigin;
-                }
+                /* Always align screens since unaligned layout will result in disaster. */
+                aVmMonitorsCache[i].xOrigin = aVmMonitorsCache[i - 1].xOrigin + aVmMonitorsCache[i - 1].cx;
+                aVmMonitorsCache[i].yOrigin = aVmMonitorsCache[i - 1].yOrigin;
 
                 /* Only count enabled monitors. */
@@ -336,4 +399,7 @@
     if (fValid)
     {
+        /* Start with invalid display ID. */
+        uint32_t u32PrimaryDisplay = VBOX_DRMIPC_MONITORS_MAX;
+
         for (uint32_t i = 0; i < cDisplaysOut; i++)
         {
@@ -343,11 +409,17 @@
             aDisplaysOut[i].h = aVmMonitorsCache[i].cy;
 
-            VBClLogInfo("VBoxDRMClient: update monitor %u parameters: %dx%d, (%d, %d)\n",
-                        i,
-                        aDisplaysOut[i].w, aDisplaysOut[i].h,
-                        aDisplaysOut[i].x, aDisplaysOut[i].y);
-
-        }
-
+            if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_PRIMARY)
+            {
+                /* Make sure display layout has only one primary display
+                 * set (for display 0, host side sets primary flag, so exclude it). */
+                Assert(u32PrimaryDisplay == 0 || u32PrimaryDisplay == VBOX_DRMIPC_MONITORS_MAX);
+                u32PrimaryDisplay = i;
+            }
+
+            VBClLogVerbose(1, "update monitor %u parameters: %dx%d, (%d, %d)\n",
+                               i, aDisplaysOut[i].w, aDisplaysOut[i].h, aDisplaysOut[i].x, aDisplaysOut[i].y);
+        }
+
+        *pu32PrimaryDisplay = u32PrimaryDisplay;
         *pcActualDisplays = cDisplaysOut;
     }
@@ -360,9 +432,9 @@
  *
  * @return  VINF_SUCCESS on success, IPRT error code otherwise.
- * @param   hDevice             Handle to opened DRM device.
- * @param   paRects             Array of screen configuration data.
- * @param   cRects              Number of elements in screen configuration array.
- */
-static int drmSendHints(RTFILE hDevice, struct DRMVMWRECT *paRects, uint32_t cRects)
+ * @param   hDevice     Handle to opened DRM device.
+ * @param   paRects     Array of screen configuration data.
+ * @param   cRects      Number of elements in screen configuration array.
+ */
+static int vbDrmSendHints(RTFILE hDevice, struct VBOX_DRMIPC_VMWRECT *paRects, uint32_t cRects)
 {
     int rc = 0;
@@ -372,5 +444,5 @@
     curuid = getuid();
 
-    /* Chenge effective user id. */
+    /* Change effective user id. */
     if (setreuid(0, 0) == 0)
     {
@@ -386,5 +458,5 @@
         if (setreuid(curuid, 0) != 0)
         {
-            VBClLogError("VBoxDRMClient: reset of setreuid failed after drm ioctl");
+            VBClLogError("reset of setreuid failed after drm ioctl");
             rc = VERR_ACCESS_DENIED;
         }
@@ -392,5 +464,5 @@
     else
     {
-        VBClLogError("VBoxDRMClient: setreuid failed during drm ioctl\n");
+        VBClLogError("setreuid failed during drm ioctl\n");
         rc = VERR_ACCESS_DENIED;
     }
@@ -400,17 +472,44 @@
 
 /**
- * This function converts vmwgfx monitors layout data into an array of monitors offsets
+ * Send monitor positions to host (thread safe).
+ *
+ * This function is accessed from DRMResize thread and from IPC Client thread.
+ *
+ * @return  IPRT status code.
+ * @param   cDisplays   Number of displays (elements in pDisplays).
+ * @param   pDisplays   Displays parameters as it was sent to vmwgfx driver.
+ */
+static int vbDrmSendMonitorPositionsSync(uint32_t cDisplays, struct RTPOINT *pDisplays)
+{
+    int rc;
+
+    rc = RTCritSectEnter(&g_monitorPositionsCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        rc = VbglR3SeamlessSendMonitorPositions(cDisplays, pDisplays);
+        int rc2 = RTCritSectLeave(&g_monitorPositionsCritSect);
+        if (RT_FAILURE(rc2))
+            VBClLogError("vbDrmSendMonitorPositionsSync: unable to leave critical section, rc=%Rrc\n", rc);
+    }
+    else
+        VBClLogError("vbDrmSendMonitorPositionsSync: unable to enter critical section, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * This function converts vmwgfx monitors layout data into an array of monitor offsets
  * and sends it back to the host in order to ensure that host and guest have the same
  * monitors layout representation.
  *
- * @return IPRT status code.
- * @param cDisplays     Number of displays (elements in pDisplays).
- * @param pDisplays     Displays parameters as it was sent to vmwgfx driver.
- */
-static int drmSendMonitorPositions(uint32_t cDisplays, struct DRMVMWRECT *pDisplays)
-{
-    static RTPOINT aPositions[VMW_MAX_HEADS];
-
-    if (!pDisplays || !cDisplays || cDisplays > VMW_MAX_HEADS)
+ * @return  IPRT status code.
+ * @param   cDisplays   Number of displays (elements in pDisplays).
+ * @param   pDisplays   Displays parameters as it was sent to vmwgfx driver.
+ */
+static int drmSendMonitorPositions(uint32_t cDisplays, struct VBOX_DRMIPC_VMWRECT *pDisplays)
+{
+    static RTPOINT aPositions[VBOX_DRMIPC_MONITORS_MAX];
+
+    if (!pDisplays || !cDisplays || cDisplays > VBOX_DRMIPC_MONITORS_MAX)
     {
         return VERR_INVALID_PARAMETER;
@@ -424,11 +523,11 @@
     }
 
-    return VbglR3SeamlessSendMonitorPositions(cDisplays, aPositions);
+    return vbDrmSendMonitorPositionsSync(cDisplays, aPositions);
 }
 
 /** Worker thread for resize task. */
-static DECLCALLBACK(int) drmResizeWorker(RTTHREAD ThreadSelf, void *pvUser)
-{
-    int rc;
+static DECLCALLBACK(int) vbDrmResizeWorker(RTTHREAD ThreadSelf, void *pvUser)
+{
+    int rc = VERR_GENERAL_FAILURE;
     RTFILE hDevice = (RTFILE)pvUser;
 
@@ -445,8 +544,8 @@
         uint32_t events;
 
-        VMMDevDisplayDef aDisplaysIn[VMW_MAX_HEADS];
+        VMMDevDisplayDef aDisplaysIn[VBOX_DRMIPC_MONITORS_MAX];
         uint32_t cDisplaysIn = 0;
 
-        struct DRMVMWRECT aDisplaysOut[VMW_MAX_HEADS];
+        struct VBOX_DRMIPC_VMWRECT aDisplaysOut[VBOX_DRMIPC_MONITORS_MAX];
         uint32_t cDisplaysOut = 0;
 
@@ -456,19 +555,18 @@
         /* Query the first size without waiting.  This lets us e.g. pick up
          * the last event before a guest reboot when we start again after. */
-        rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysIn, aDisplaysIn, fAck);
+        rc = VbglR3GetDisplayChangeRequestMulti(VBOX_DRMIPC_MONITORS_MAX, &cDisplaysIn, aDisplaysIn, fAck);
         fAck = true;
-        if (RT_FAILURE(rc))
-        {
-            VBClLogError("Failed to get display change request, rc=%Rrc\n", rc);
-        }
-        else
-        {
+        if (RT_SUCCESS(rc))
+        {
+            uint32_t u32PrimaryDisplay = VBOX_DRMIPC_MONITORS_MAX;
+            static uint32_t u32PrimaryDisplayLast = VBOX_DRMIPC_MONITORS_MAX;
+
             /* Validate displays layout and push it to DRM stack if valid. */
-            rc = drmValidateLayout(aDisplaysIn, cDisplaysIn, aDisplaysOut, sizeof(aDisplaysOut), &cDisplaysOut);
+            rc = vbDrmValidateLayout(aDisplaysIn, cDisplaysIn, aDisplaysOut, &u32PrimaryDisplay, sizeof(aDisplaysOut), &cDisplaysOut);
             if (RT_SUCCESS(rc))
             {
-                rc = drmSendHints(hDevice, aDisplaysOut, cDisplaysOut);
-                VBClLogInfo("VBoxDRMClient: push screen layout data of %u display(s) to DRM stack has %s (%Rrc)\n",
-                            cDisplaysOut, RT_SUCCESS(rc) ? "succeeded" : "failed", rc);
+                rc = vbDrmSendHints(hDevice, aDisplaysOut, cDisplaysOut);
+                VBClLogInfo("push screen layout data of %u display(s) to DRM stack has %s (%Rrc)\n",
+                                cDisplaysOut, RT_SUCCESS(rc) ? "succeeded" : "failed", rc);
                 /* In addition, notify host that configuration was successfully applied to the guest vmwgfx driver. */
                 if (RT_SUCCESS(rc))
@@ -476,38 +574,393 @@
                     rc = drmSendMonitorPositions(cDisplaysOut, aDisplaysOut);
                     if (RT_FAILURE(rc))
+                        VBClLogError("cannot send host notification: %Rrc\n", rc);
+
+                    /* If information about primary display is present in display layout, send it to DE over IPC. */
+                    if (u32PrimaryDisplay != VBOX_DRMIPC_MONITORS_MAX
+                        && u32PrimaryDisplayLast != u32PrimaryDisplay)
                     {
-                        VBClLogError("VBoxDRMClient: cannot send host notification: %Rrc\n", rc);
+                        rc = vbDrmIpcBroadcastPrimaryDisplay(u32PrimaryDisplay);
+
+                        /* Cache last value in order to avoid sending duplicate data over IPC. */
+                        u32PrimaryDisplayLast = u32PrimaryDisplay;
+
+                        VBClLogVerbose(2, "DE was notified that display %u is now primary, rc=%Rrc\n", u32PrimaryDisplay, rc);
                     }
+                    else
+                        VBClLogVerbose(2, "do not notify DE that display %u is now primary, rc=%Rrc\n", u32PrimaryDisplay, rc);
                 }
             }
+            else if (rc == VERR_DUPLICATE)
+                VBClLogVerbose(2, "do not notify DRM stack about monitors layout change, rc=%Rrc\n", rc);
             else
-            {
-                VBClLogError("VBoxDRMClient: displays layout is invalid, will not notify guest driver, rc=%Rrc\n", rc);
-            }
-        }
+                VBClLogError("displays layout is invalid, will not notify guest driver, rc=%Rrc\n", rc);
+        }
+        else
+            VBClLogError("Failed to get display change request, rc=%Rrc\n", rc);
 
         do
         {
-            rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, DRM_HOST_EVENT_RX_TIMEOUT_MS, &events);
+            rc = VbglR3WaitEvent(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, VBOX_DRMIPC_RX_TIMEOUT_MS, &events);
         } while (rc == VERR_TIMEOUT && !ASMAtomicReadBool(&g_fShutdown));
 
         if (ASMAtomicReadBool(&g_fShutdown))
         {
-            VBClLogInfo("VBoxDRMClient: exitting resize thread: shutdown requested\n");
+            VBClLogInfo("exiting resize thread: shutdown requested\n");
+            /* This is a case when we should return positive status. */
+            rc = (rc == VERR_TIMEOUT) ? VINF_SUCCESS : rc;
             break;
         }
         else if (RT_FAILURE(rc))
-        {
-            VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
-        }
-    }
-
-    return 0;
-}
-
-static void drmRequestShutdown(int sig)
-{
-    RT_NOREF1(sig);
-
+            VBClLogFatalError("VBoxDRMClient: resize thread: failure waiting for event, rc=%Rrc\n", rc);
+    }
+
+    return rc;
+}
+
+/**
+ * Go over all existing IPC client connection and put set-primary-screen request
+ * data into TX queue of each of them .
+ *
+ * @return  IPRT status code.
+ * @param   u32PrimaryDisplay   Primary display ID.
+ */
+static int vbDrmIpcBroadcastPrimaryDisplay(uint32_t u32PrimaryDisplay)
+{
+    int rc;
+
+    rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        if (!RTListIsEmpty(&g_ipcClientConnectionsList.Node))
+        {
+            PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pEntry;
+            RTListForEach(&g_ipcClientConnectionsList.Node, pEntry, VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE, Node)
+            {
+                AssertReturn(pEntry, VERR_INVALID_PARAMETER);
+                AssertReturn(pEntry->pClient, VERR_INVALID_PARAMETER);
+                AssertReturn(pEntry->pClient->hThread, VERR_INVALID_PARAMETER);
+
+                rc = vbDrmIpcSetPrimaryDisplay(pEntry->pClient, u32PrimaryDisplay);
+
+                VBClLogInfo("thread %s notified IPC Client that display %u is now primary, rc=%Rrc\n",
+                                RTThreadGetName(pEntry->pClient->hThread), u32PrimaryDisplay, rc);
+            }
+        }
+
+        int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
+        if (RT_FAILURE(rc2))
+            VBClLogError("notify DE: unable to leave critical section, rc=%Rrc\n", rc2);
+    }
+    else
+        VBClLogError("notify DE: unable to enter critical section, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Main loop for IPC client connection handling.
+ *
+ * @return  IPRT status code.
+ * @param   pClient     Pointer to IPC client data.
+ */
+static int vbDrmIpcConnectionProc(PVBOX_DRMIPC_CLIENT pClient)
+{
+    int rc = VERR_GENERAL_FAILURE;
+
+    AssertReturn(pClient, VERR_INVALID_PARAMETER);
+
+    /* This loop handles incoming messages. */
+    for (;;)
+    {
+        rc = vbDrmIpcConnectionHandler(pClient);
+
+        /* Try to detect if we should shutdown as early as we can. */
+        if (ASMAtomicReadBool(&g_fShutdown))
+            break;
+
+        /* Normal case. No data received within short interval. */
+        if (rc == VERR_TIMEOUT)
+        {
+            continue;
+        }
+        else if (RT_FAILURE(rc))
+        {
+            /* Terminate connection handling in case of error. */
+            VBClLogError("unable to handle IPC session, rc=%Rrc\n", rc);
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Add IPC client connection data into list of connections.
+ *
+ * List size is limited indirectly by DRM_IPC_SERVER_CONNECTIONS_MAX value.
+ * This function should only be invoked from client thread context
+ * (from vbDrmIpcClientWorker() in particular).
+ *
+ * @return  IPRT status code.
+ * @param   pClient     Client connection information to add to the list.
+ */
+static int vbDrmIpcClientsListAdd(PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pClientNode)
+{
+    int rc;
+
+    AssertReturn(pClientNode, VERR_INVALID_PARAMETER);
+
+    rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
+    if (RT_SUCCESS(rc))
+    {
+        RTListAppend(&g_ipcClientConnectionsList.Node, &pClientNode->Node);
+
+        int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
+        if (RT_FAILURE(rc2))
+            VBClLogError("add client connection: unable to leave critical section, rc=%Rrc\n", rc2);
+    }
+    else
+        VBClLogError("add client connection: unable to enter critical section, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Remove IPC client connection data from list of connections.
+ *
+ * This function should only be invoked from client thread context
+ * (from vbDrmIpcClientWorker() in particular).
+ *
+ * @return  IPRT status code.
+ * @param   pClient     Client connection information to remove from the list.
+ */
+static int vbDrmIpcClientsListRemove(PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pClientNode)
+{
+    int rc;
+    PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE pEntry, pNextEntry, pFound = NULL;
+
+    AssertReturn(pClientNode, VERR_INVALID_PARAMETER);
+
+    rc = RTCritSectEnter(&g_ipcClientConnectionsListCritSect);
+    if (RT_SUCCESS(rc))
+    {
+
+        if (!RTListIsEmpty(&g_ipcClientConnectionsList.Node))
+        {
+            RTListForEachSafe(&g_ipcClientConnectionsList.Node, pEntry, pNextEntry, VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE, Node)
+            {
+                if (pEntry == pClientNode)
+                    pFound = (PVBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE)RTListNodeRemoveRet(&pEntry->Node);
+            }
+        }
+        else
+            VBClLogError("remove client connection: connections list empty, node %p not there\n", pClientNode);
+
+        int rc2 = RTCritSectLeave(&g_ipcClientConnectionsListCritSect);
+        if (RT_FAILURE(rc2))
+            VBClLogError("remove client connection: unable to leave critical section, rc=%Rrc\n", rc2);
+    }
+    else
+        VBClLogError("remove client connection: unable to enter critical section, rc=%Rrc\n", rc);
+
+    if (!pFound)
+        VBClLogError("remove client connection: node not found\n");
+
+    return !rc && pFound ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
+}
+
+/**
+ * @interface_method_impl{VBOX_DRMIPC_CLIENT,pfnRxCb}
+ */
+static int vbDrmIpcClientRxCallBack(uint8_t idCmd, void *pvData, uint32_t cbData)
+{
+    int rc = VERR_INVALID_PARAMETER;
+
+    AssertReturn(pvData, VERR_INVALID_PARAMETER);
+    AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+    switch (idCmd)
+    {
+        case VBOXDRMIPCSRVCMD_REPORT_DISPLAY_OFFSETS:
+        {
+            PVBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS pCmd = (PVBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS)pvData;
+            AssertReturn(cbData == sizeof(VBOX_DRMIPC_COMMAND_REPORT_DISPLAY_OFFSETS), VERR_INVALID_PARAMETER);
+            rc = vbDrmSendMonitorPositionsSync(pCmd->cOffsets, pCmd->paOffsets);
+            break;
+        }
+
+        default:
+        {
+            VBClLogError("received unknown IPC command 0x%x\n", idCmd);
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/** Worker thread for IPC client task. */
+static DECLCALLBACK(int) vbDrmIpcClientWorker(RTTHREAD ThreadSelf, void *pvUser)
+{
+    VBOX_DRMIPC_CLIENT        hClient  = VBOX_DRMIPC_CLIENT_INITIALIZER;
+    RTLOCALIPCSESSION   hSession = (RTLOCALIPCSESSION)pvUser;
+    int                 rc;
+
+    AssertReturn(RT_VALID_PTR(hSession), VERR_INVALID_PARAMETER);
+
+    /* Initialize client session resources. */
+    rc = vbDrmIpcClientInit(&hClient, ThreadSelf, hSession, VBOX_DRMIPC_TX_QUEUE_SIZE, vbDrmIpcClientRxCallBack);
+    if (RT_SUCCESS(rc))
+    {
+        /* Add IPC client connection data into clients list. */
+        VBOX_DRMIPC_CLIENT_CONNECTION_LIST_NODE hClientNode = { { 0, 0 } , &hClient };
+
+        rc = vbDrmIpcClientsListAdd(&hClientNode);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTThreadUserSignal(ThreadSelf);
+            if (RT_SUCCESS(rc))
+            {
+                /* Start spinning the connection. */
+                VBClLogInfo("IPC client connection started\n", rc);
+                rc = vbDrmIpcConnectionProc(&hClient);
+                VBClLogInfo("IPC client connection ended, rc=%Rrc\n", rc);
+            }
+            else
+                VBClLogError("unable to report IPC client connection handler start, rc=%Rrc\n", rc);
+
+            /* Remove IPC client connection data from clients list. */
+            rc = vbDrmIpcClientsListRemove(&hClientNode);
+            if (RT_FAILURE(rc))
+                VBClLogError("unable to remove IPC client session from list of connections, rc=%Rrc\n", rc);
+        }
+        else
+            VBClLogError("unable to add IPC client connection to the list, rc=%Rrc\n");
+
+        /* Disconnect remote peer if still connected. */
+        if (RT_VALID_PTR(hSession))
+        {
+            rc = RTLocalIpcSessionClose(hSession);
+            VBClLogInfo("IPC session closed, rc=%Rrc\n", rc);
+        }
+
+        /* Connection handler loop has ended, release session resources. */
+        rc = vbDrmIpcClientReleaseResources(&hClient);
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to release IPC client session, rc=%Rrc\n", rc);
+
+        ASMAtomicDecU32(&g_cDrmIpcConnections);
+    }
+    else
+        VBClLogError("unable to initialize IPC client session, rc=%Rrc\n", rc);
+
+    VBClLogInfo("closing IPC client session, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Start processing thread for IPC client requests handling.
+ *
+ * @returns IPRT status code.
+ * @param   hSession    IPC client connection handle.
+ */
+static int vbDrmIpcClientStart(RTLOCALIPCSESSION hSession)
+{
+    int         rc;
+    RTTHREAD    hThread = 0;
+    RTPROCESS   hProcess = 0;
+
+    rc = RTLocalIpcSessionQueryProcess(hSession, &hProcess);
+    if (RT_SUCCESS(rc))
+    {
+        char pszThreadName[DRM_IPC_THREAD_NAME_MAX];
+        RT_ZERO(pszThreadName);
+
+        RTStrPrintf2(pszThreadName, DRM_IPC_THREAD_NAME_MAX, DRM_IPC_CLIENT_THREAD_NAME_PTR, hProcess);
+
+        /* Attempt to start IPC client connection handler task. */
+        rc = RTThreadCreate(&hThread, vbDrmIpcClientWorker, (void *)hSession, 0,
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, pszThreadName);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTThreadUserWait(hThread, RT_MS_5SEC);
+        }
+    }
+
+    return rc;
+}
+
+/** Worker thread for IPC server task. */
+static DECLCALLBACK(int) vbDrmIpcServerWorker(RTTHREAD ThreadSelf, void *pvUser)
+{
+    int rc = VERR_GENERAL_FAILURE;
+    RTLOCALIPCSERVER hIpcServer = (RTLOCALIPCSERVER)pvUser;
+
+    RT_NOREF1(ThreadSelf);
+
+    AssertReturn(hIpcServer, VERR_INVALID_PARAMETER);
+
+    /* This loop accepts incoming connections. */
+    for (;;)
+    {
+        RTLOCALIPCSESSION hClientSession;
+
+        /* Wait for incoming connection. */
+        rc = RTLocalIpcServerListen(hIpcServer, &hClientSession);
+        if (RT_SUCCESS(rc))
+        {
+            VBClLogVerbose(2, "new IPC session\n");
+
+            if (ASMAtomicIncU32(&g_cDrmIpcConnections) <= DRM_IPC_SERVER_CONNECTIONS_MAX)
+            {
+                /* Authenticate remote peer. */
+                rc = vbDrmIpcAuth(hClientSession);
+                if (RT_SUCCESS(rc))
+                {
+                    /* Start incoming connection handler thread. */
+                    rc = vbDrmIpcClientStart(hClientSession);
+                    VBClLogVerbose(2, "connection processing ended, rc=%Rrc\n", rc);
+                }
+                else
+                    VBClLogError("IPC authentication failed, rc=%Rrc\n", rc);
+            }
+            else
+                rc = VERR_RESOURCE_BUSY;
+
+            /* Release resources in case of error. */
+            if (RT_FAILURE(rc))
+            {
+                VBClLogError("maximum amount of IPC client connections reached, dropping connection\n");
+
+                int rc2 = RTLocalIpcSessionClose(hClientSession);
+                if (RT_FAILURE(rc2))
+                    VBClLogError("unable to close IPC session, rc=%Rrc\n", rc2);
+
+                ASMAtomicDecU32(&g_cDrmIpcConnections);
+            }
+        }
+        else
+            VBClLogError("IPC authentication failed, rc=%Rrc\n", rc);
+
+        /* Check shutdown was requested. */
+        if (ASMAtomicReadBool(&g_fShutdown))
+        {
+            VBClLogInfo("exiting IPC thread: shutdown requested\n");
+            break;
+        }
+
+        /* Wait a bit before spinning a loop if something went wrong. */
+        if (RT_FAILURE(rc))
+            RTThreadSleep(VBOX_DRMIPC_RX_RELAX_MS);
+    }
+
+    return rc;
+}
+
+/** A signal handler. */
+static void vbDrmRequestShutdown(int sig)
+{
+    RT_NOREF(sig);
     ASMAtomicWriteBool(&g_fShutdown, true);
 }
@@ -515,11 +968,24 @@
 int main(int argc, char *argv[])
 {
+    /** Custom log prefix to be used for logger instance of this process. */
+    static const char *pszLogPrefix = "VBoxDRMClient:";
+
+    static const RTGETOPTDEF s_aOptions[] = { { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, };
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    int ch;
+
     RTFILE hDevice = NIL_RTFILE;
     RTFILE hPidFile;
 
+    RTLOCALIPCSERVER hIpcServer;
+    RTTHREAD vbDrmIpcThread;
+    int rcDrmIpcThread = 0;
+
     RTTHREAD drmResizeThread;
     int rcDrmResizeThread = 0;
-
-    int rc = RTR3InitExe(argc, &argv, 0);
+    int rc, rc2 = 0;
+
+    rc = RTR3InitExe(argc, &argv, 0);
     if (RT_FAILURE(rc))
         return RTMsgInitFailure(rc);
@@ -529,19 +995,34 @@
         VBClLogFatalError("VBoxDRMClient: VbglR3InitUser failed: %Rrc", rc);
 
+    /* Process command line options. */
+    rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
+    if (RT_FAILURE(rc))
+        VBClLogFatalError("VBoxDRMClient: unable to process command line options, rc=%Rrc\n", rc);
+    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+    {
+        switch (ch)
+        {
+            case 'v':
+            {
+                g_cVerbosity++;
+                break;
+            }
+
+            case VERR_GETOPT_UNKNOWN_OPTION:
+            {
+                VBClLogFatalError("unknown command line option '%s'\n", ValueUnion.psz);
+                return RTEXITCODE_SYNTAX;
+
+            }
+
+            default:
+                break;
+        }
+    }
+
     rc = VBClLogCreate("");
     if (RT_FAILURE(rc))
         VBClLogFatalError("VBoxDRMClient: failed to setup logging, rc=%Rrc\n", rc);
-
-    PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
-    if (pReleaseLog)
-    {
-        rc = RTLogDestinations(pReleaseLog, "stdout");
-        if (RT_FAILURE(rc))
-            VBClLogFatalError("VBoxDRMClient: failed to redirert error output, rc=%Rrc", rc);
-    }
-    else
-    {
-        VBClLogFatalError("VBoxDRMClient: failed to get logger instance");
-    }
+    VBClLogSetLogPrefix(pszLogPrefix);
 
     /* Check PID file before attempting to initialize anything. */
@@ -549,14 +1030,14 @@
     if (rc == VERR_FILE_LOCK_VIOLATION)
     {
-        VBClLogInfo("VBoxDRMClient: already running, exiting\n");
+        VBClLogInfo("already running, exiting\n");
         return RTEXITCODE_SUCCESS;
     }
     if (RT_FAILURE(rc))
     {
-        VBClLogError("VBoxDRMClient: unable to lock PID file (%Rrc), exiting\n", rc);
+        VBClLogError("unable to lock PID file (%Rrc), exiting\n", rc);
         return RTEXITCODE_FAILURE;
     }
 
-    hDevice = drmOpenVmwgfx();
+    hDevice = vbDrmOpenVmwgfx();
     if (hDevice == NIL_RTFILE)
         return RTEXITCODE_FAILURE;
@@ -569,8 +1050,4 @@
     }
     rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
-    if (rc == VERR_RESOURCE_BUSY)  /* Someone else has already acquired it. */
-    {
-        return RTEXITCODE_FAILURE;
-    }
     if (RT_FAILURE(rc))
     {
@@ -580,26 +1057,100 @@
 
     /* Setup signals: gracefully terminate on SIGINT, SIGTERM. */
-    if (   signal(SIGINT, drmRequestShutdown) == SIG_ERR
-        || signal(SIGTERM, drmRequestShutdown) == SIG_ERR)
-    {
-        VBClLogError("VBoxDRMClient: unable to setup signals\n");
+    if (   signal(SIGINT, vbDrmRequestShutdown) == SIG_ERR
+        || signal(SIGTERM, vbDrmRequestShutdown) == SIG_ERR)
+    {
+        VBClLogError("unable to setup signals\n");
         return RTEXITCODE_FAILURE;
     }
 
+    /* Init IPC client connection list. */
+    RTListInit(&g_ipcClientConnectionsList.Node);
+    rc = RTCritSectInit(&g_ipcClientConnectionsListCritSect);
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to initialize IPC client connection list critical section\n");
+        return RTEXITCODE_FAILURE;
+    }
+
+    /* Init critical section which is used for reporting monitors offset back to host. */
+    rc = RTCritSectInit(&g_monitorPositionsCritSect);
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to initialize monitors position critical section\n");
+        return RTEXITCODE_FAILURE;
+    }
+
+    /* Instantiate IPC server for VBoxClient service communication. */
+    rc = RTLocalIpcServerCreate(&hIpcServer, VBOX_DRMIPC_SERVER_NAME, 0);
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to setup IPC server, rc=%Rrc\n", rc);
+        return RTEXITCODE_FAILURE;
+    }
+
+    struct group *pGrp;
+    pGrp = getgrnam(VBOX_DRMIPC_USER_GROUP);
+    if (pGrp)
+    {
+        rc = RTLocalIpcServerGrantGroupAccess(hIpcServer, pGrp->gr_gid);
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to grant IPC server socket access to '" VBOX_DRMIPC_USER_GROUP "', rc=%Rrc\n", rc);
+    }
+    else
+        VBClLogError("unable to grant IPC server socket access to '" VBOX_DRMIPC_USER_GROUP "', group does not exist\n");
+
     /* Attempt to start DRM resize task. */
-    rc = RTThreadCreate(&drmResizeThread, drmResizeWorker, (void *)hDevice, 0,
+    rc = RTThreadCreate(&drmResizeThread, vbDrmResizeWorker, (void *)hDevice, 0,
                         RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, DRM_RESIZE_THREAD_NAME);
     if (RT_SUCCESS(rc))
     {
-        rc = RTThreadWait(drmResizeThread, RT_INDEFINITE_WAIT, &rcDrmResizeThread);
-        VBClLogInfo("VBoxDRMClient: %s thread exitted with status %Rrc\n", DRM_RESIZE_THREAD_NAME, rcDrmResizeThread);
-        rc |= rcDrmResizeThread;
-    }
+        /* Attempt to start IPC task. */
+        rc = RTThreadCreate(&vbDrmIpcThread, vbDrmIpcServerWorker, (void *)hIpcServer, 0,
+                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, DRM_IPC_SERVER_THREAD_NAME);
+        if (RT_SUCCESS(rc))
+        {
+            /* HACK ALERT!
+             * The sequence of RTThreadWait(drmResizeThread) -> RTLocalIpcServerDestroy() -> RTThreadWait(vbDrmIpcThread)
+             * is intentional! Once process received a signal, it will pull g_fShutdown flag, which in turn will cause
+             * drmResizeThread to quit. The vbDrmIpcThread might hang on accept() call, so we terminate IPC server to
+             * release it and then wait for its termination. */
+
+            rc = RTThreadWait(drmResizeThread, RT_INDEFINITE_WAIT, &rcDrmResizeThread);
+            VBClLogInfo("%s thread exited with status, rc=%Rrc\n", DRM_RESIZE_THREAD_NAME, rcDrmResizeThread);
+
+            rc = RTLocalIpcServerCancel(hIpcServer);
+            if (RT_FAILURE(rc))
+                VBClLogError("unable to notify IPC server about shutdown, rc=%Rrc\n", rc);
+
+            /* Wait for threads to terminate gracefully. */
+            rc = RTThreadWait(vbDrmIpcThread, RT_INDEFINITE_WAIT, &rcDrmIpcThread);
+            VBClLogInfo("%s thread exited with status, rc=%Rrc\n", DRM_IPC_SERVER_THREAD_NAME, rcDrmResizeThread);
+
+        }
+        else
+            VBClLogError("unable to start IPC thread, rc=%Rrc\n", rc);
+    }
+    else
+        VBClLogError("unable to start resize thread, rc=%Rrc\n", rc);
+
+    rc = RTLocalIpcServerDestroy(hIpcServer);
+    if (RT_FAILURE(rc))
+        VBClLogError("unable to stop IPC server,  rc=%Rrc\n", rc);
+
+    rc2 = RTCritSectDelete(&g_monitorPositionsCritSect);
+    if (RT_FAILURE(rc2))
+        VBClLogError("unable to destroy g_monitorPositionsCritSect critsect, rc=%Rrc\n", rc2);
+
+    rc2 = RTCritSectDelete(&g_ipcClientConnectionsListCritSect);
+    if (RT_FAILURE(rc2))
+        VBClLogError("unable to destroy g_ipcClientConnectionsListCritSect critsect, rc=%Rrc\n", rc2);
 
     RTFileClose(hDevice);
 
-    VBClLogInfo("VBoxDRMClient: releasing PID file lock\n");
+    VBClLogInfo("releasing PID file lock\n");
     VbglR3ClosePidFile(g_pszPidFile, hPidFile);
 
+    VBClLogDestroy();
+
     return rc == 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
 }
Index: trunk/src/VBox/Additions/x11/VBoxClient/display-svga-session.cpp
===================================================================
--- trunk/src/VBox/Additions/x11/VBoxClient/display-svga-session.cpp	(revision 93371)
+++ trunk/src/VBox/Additions/x11/VBoxClient/display-svga-session.cpp	(revision 93371)
@@ -0,0 +1,527 @@
+/* $Id$ */
+/** @file
+ * Desktop Environment specific guest screen assistant for
+ * VMSVGA graphics adapter.
+ *
+ * This service connects to VBoxDRMClient IPC server, listens for
+ * its commands and reports current display offsets to it. If IPC
+ * server is not available, it forks legacy 'VBoxClient --vmsvga
+ * service and terminates.
+ */
+
+/*
+ * Copyright (C) 2017-2022 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*
+ * This service is an IPC client for VBoxDRMClient daemon. It is also
+ * a proxy bridge to a Desktop Environment specific code (so called
+ * Desktop Environment helpers).
+ *
+ * Once started, it will try to enumerate and probe all the registered
+ * helpers and if appropriate helper found, it will forward incoming IPC
+ * commands to it as well as send helper's commands back to VBoxDRMClient.
+ * Generic helper is a special one. It will be used by default if all the
+ * other helpers are failed on probe. Moreover, generic helper provides
+ * helper functions that can be used by other helpers as well. For example,
+ * once Gnome3 Desktop Environment is running on X11, it will be also use
+ * display offsets change notification monitor of a generic helper.
+ *
+ * Multiple instances of this daemon are allowed to run in parallel
+ * with the following limitations (see also vbclSVGASessionPidFileLock()).
+ * A single user cannot run multiple daemon instances per single TTY device,
+ * however, multiple instances are allowed for the user on different
+ * TTY devices (i.e. in case if user runs multiple X servers on different
+ * terminals). On multiple TTY devices multiple users can run multiple
+ * daemon instances (i.e. in case of "switch user" DE configuration when
+ * multiple X/Wayland servers are running on separate TTY devices).
+ */
+
+#include "VBoxClient.h"
+#include "display-ipc.h"
+#include "display-helper.h"
+
+#include <VBox/VBoxGuestLib.h>
+
+#include <iprt/localipc.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+#include <iprt/linux/sysfs.h>
+
+/** Lock file handle. */
+static RTFILE g_hPidFile;
+/** Full path to PID lock file. */
+static char g_szPidFilePath[RTPATH_MAX];
+
+/** Handle to IPC client connection. */
+VBOX_DRMIPC_CLIENT g_hClient = VBOX_DRMIPC_CLIENT_INITIALIZER;
+
+/** IPC client handle critical section. */
+static RTCRITSECT g_hClientCritSect;
+
+/** List of available Desktop Environment specific display helpers. */
+static const VBCLDISPLAYHELPER *g_apDisplayHelpers[] =
+{
+    &g_DisplayHelperGnome3,  /* GNOME3 helper. */
+    &g_DisplayHelperGeneric, /* Generic helper. */
+    NULL,                    /* Terminate list. */
+};
+
+/** Selected Desktop Environment specific display helper. */
+static const VBCLDISPLAYHELPER *g_pDisplayHelper = NULL;
+
+/** IPC connection session handle. */
+static RTLOCALIPCSESSION g_hSession = 0;
+
+/**
+ * Callback for display offsets change events provided by Desktop Environment specific display helper.
+ *
+ * @returns IPRT status code.
+ * @param   cDisplays   Number of displays which have changed offset.
+ * @param   pDisplays   Display data.
+ */
+static int vbclSVGASessionDisplayOffsetChanged(uint32_t cOffsets, RTPOINT *paOffsets)
+{
+    int rc = RTCritSectEnter(&g_hClientCritSect);
+
+    if (RT_SUCCESS(rc))
+    {
+        rc = vbDrmIpcReportDisplayOffsets(&g_hClient, cOffsets, paOffsets);
+        int rc2 = RTCritSectLeave(&g_hClientCritSect);
+        if (RT_FAILURE(rc2))
+            VBClLogError("vbclSVGASessionDisplayOffsetChanged: unable to leave critical session, rc=%Rrc\n", rc2);
+    }
+    else
+        VBClLogError("vbclSVGASessionDisplayOffsetChanged: unable to enter critical session, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Prevent multiple instances of the service from start.
+ *
+ * @returns IPRT status code.
+ */
+static int vbclSVGASessionPidFileLock(void)
+{
+    int rc;
+
+    /* Allow parallel running instances of the service for processes
+     * which are running in separate X11/Wayland sessions. Compose
+     * custom PID file name based on currently active TTY device. */
+
+    char *pszPidFileName = RTStrAlloc(RTPATH_MAX);
+    if (pszPidFileName)
+    {
+        rc = RTPathUserHome(g_szPidFilePath, sizeof(g_szPidFilePath));
+        if (RT_SUCCESS(rc))
+        {
+            char pszActiveTTY[128];
+            size_t cchRead;
+
+            RT_ZERO(pszActiveTTY);
+
+            RTStrAAppend(&pszPidFileName, ".vboxclient-vmsvga-session");
+
+            rc = RTLinuxSysFsReadStrFile(pszActiveTTY, sizeof(pszActiveTTY) - 1 /* reserve last byte for string termination */,
+                                         &cchRead, "class/tty/tty0/active");
+            if (RT_SUCCESS(rc))
+            {
+                RTStrAAppend(&pszPidFileName, "-");
+                RTStrAAppend(&pszPidFileName, pszActiveTTY);
+            }
+            else
+                VBClLogInfo("cannot detect currently active tty device, "
+                            "multiple service instances for a single user will not be allowed, rc=%Rrc", rc);
+
+            RTStrAAppend(&pszPidFileName, ".pid");
+
+            RTPathAppend(g_szPidFilePath, sizeof(g_szPidFilePath), pszPidFileName);
+
+            VBClLogVerbose(1, "lock file path: %s\n", g_szPidFilePath);
+            rc = VbglR3PidFile(g_szPidFilePath, &g_hPidFile);
+        }
+        else
+            VBClLogError("unable to get user home directory, rc=%Rrc\n", rc);
+
+        RTStrFree(pszPidFileName);
+    }
+    else
+        rc = VERR_NO_MEMORY;
+
+    return rc;
+}
+
+/**
+ * Release lock file.
+ */
+static void vbclSVGASessionPidFileRelease(void)
+{
+    VbglR3ClosePidFile(g_szPidFilePath, g_hPidFile);
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnInit}
+ */
+static DECLCALLBACK(int) vbclSVGASessionInit(void)
+{
+    int rc;
+    RTLOCALIPCSESSION hSession;
+    int idxDisplayHelper = 0;
+
+    /** Custom log prefix to be used for logger instance of this process. */
+    static const char *pszLogPrefix = "VBoxClient VMSVGA:";
+
+    VBClLogSetLogPrefix(pszLogPrefix);
+
+    rc = vbclSVGASessionPidFileLock();
+    if (RT_FAILURE(rc))
+    {
+        VBClLogVerbose(1, "cannot acquire pid lock, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    rc = RTCritSectInit(&g_hClientCritSect);
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to init locking, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    /* Go through list of available Desktop Environment specific helpers and try to pick up one. */
+    while (g_apDisplayHelpers[idxDisplayHelper])
+    {
+        if (g_apDisplayHelpers[idxDisplayHelper]->pfnProbe)
+        {
+            VBClLogInfo("probing Desktop Environment helper '%s'\n",
+                         g_apDisplayHelpers[idxDisplayHelper]->pszName);
+
+            rc = g_apDisplayHelpers[idxDisplayHelper]->pfnProbe();
+
+            /* Found compatible helper. */
+            if (RT_SUCCESS(rc))
+            {
+                /* Initialize it. */
+                if (g_apDisplayHelpers[idxDisplayHelper]->pfnInit)
+                {
+                    rc = g_apDisplayHelpers[idxDisplayHelper]->pfnInit();
+                }
+
+                /* Some helpers might have no .pfnInit(), that's ok. */
+                if (RT_SUCCESS(rc))
+                {
+                    /* Subscribe to display offsets change event. */
+                    if (g_apDisplayHelpers[idxDisplayHelper]->pfnSubscribeDisplayOffsetChangeNotification)
+                    {
+                        g_apDisplayHelpers[idxDisplayHelper]->
+                            pfnSubscribeDisplayOffsetChangeNotification(
+                                vbclSVGASessionDisplayOffsetChanged);
+                    }
+
+                    g_pDisplayHelper = g_apDisplayHelpers[idxDisplayHelper];
+                    break;
+                }
+                else
+                    VBClLogError("compatible Desktop Environment "
+                                 "helper has been found, but it cannot be initialized, rc=%Rrc\n", rc);
+            }
+        }
+
+        idxDisplayHelper++;
+    }
+
+    /* Make sure we found compatible Desktop Environment specific helper. */
+    if (g_pDisplayHelper)
+    {
+        VBClLogInfo("using Desktop Environment specific display helper '%s'\n",
+                    g_pDisplayHelper->pszName);
+    }
+    else
+    {
+        VBClLogError("unable to find Desktop Environment specific display helper\n");
+        return VERR_NOT_IMPLEMENTED;
+    }
+
+    /* Attempt to connect to VBoxDRMClient IPC server. */
+    rc = RTLocalIpcSessionConnect(&hSession, VBOX_DRMIPC_SERVER_NAME, 0);
+    if (RT_SUCCESS(rc))
+    {
+        g_hSession = hSession;
+    }
+    else
+        VBClLogError("unable to connect to IPC server, rc=%Rrc\n", rc);
+
+    /* We cannot initialize ourselves, start legacy service and terminate. */
+    if (RT_FAILURE(rc))
+    {
+        /* Free helper resources. */
+        if (g_pDisplayHelper->pfnUnsubscribeDisplayOffsetChangeNotification)
+            g_pDisplayHelper->pfnUnsubscribeDisplayOffsetChangeNotification();
+
+        if (g_pDisplayHelper->pfnTerm)
+        {
+            rc = g_pDisplayHelper->pfnTerm();
+            VBClLogInfo("helper service terminated, rc=%Rrc\n", rc);
+        }
+
+        rc = VbglR3DrmLegacyClientStart();
+        VBClLogInfo("starting legacy service, rc=%Rrc\n", rc);
+        /* Force return status, so parent thread wont be trying to start worker thread. */
+        rc = VERR_NOT_AVAILABLE;
+    }
+
+    return rc;
+}
+
+/**
+ * A callback function which is triggered on IPC data receive.
+ *
+ * @returns IPRT status code.
+ * @param   idCmd   DRM IPC command ID.
+ * @param   pvData  DRM IPC command payload.
+ * @param   cbData  Size of DRM IPC command payload.
+ */
+static int vbclSVGASessionRxCallBack(uint8_t idCmd, void *pvData, uint32_t cbData)
+{
+    VBOXDRMIPCCLTCMD enmCmd =
+        (idCmd > VBOXDRMIPCCLTCMD_INVALID && idCmd < VBOXDRMIPCCLTCMD_MAX) ?
+            (VBOXDRMIPCCLTCMD)idCmd : VBOXDRMIPCCLTCMD_INVALID;
+
+    int rc = VERR_INVALID_PARAMETER;
+
+    AssertReturn(pvData,            VERR_INVALID_PARAMETER);
+    AssertReturn(cbData,            VERR_INVALID_PARAMETER);
+    AssertReturn(g_pDisplayHelper,  VERR_INVALID_PARAMETER);
+
+    switch (enmCmd)
+    {
+        case VBOXDRMIPCCLTCMD_SET_PRIMARY_DISPLAY:
+        {
+            if (g_pDisplayHelper->pfnSetPrimaryDisplay)
+            {
+                PVBOX_DRMIPC_COMMAND_SET_PRIMARY_DISPLAY pCmd = (PVBOX_DRMIPC_COMMAND_SET_PRIMARY_DISPLAY)pvData;
+                static uint32_t idPrimaryDisplayCached = VBOX_DRMIPC_MONITORS_MAX;
+
+                if (   pCmd->idDisplay < VBOX_DRMIPC_MONITORS_MAX
+                    && idPrimaryDisplayCached != pCmd->idDisplay)
+                {
+                    rc = g_pDisplayHelper->pfnSetPrimaryDisplay(pCmd->idDisplay);
+                    /* Update cache. */
+                    idPrimaryDisplayCached = pCmd->idDisplay;
+                }
+                else
+                    VBClLogVerbose(1, "do not set %u as a primary display\n", pCmd->idDisplay);
+            }
+
+            break;
+        }
+        default:
+        {
+            VBClLogError("received unknown IPC command 0x%x\n", idCmd);
+            break;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Reconnect to DRM IPC server.
+ */
+static int vbclSVGASessionReconnect(void)
+{
+    int rc = VERR_GENERAL_FAILURE;
+
+    rc = RTCritSectEnter(&g_hClientCritSect);
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to enter critical section on reconnect, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    /* Check if session was not closed before. */
+    if (RT_VALID_PTR(g_hSession))
+    {
+        rc = RTLocalIpcSessionClose(g_hSession);
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to release IPC connection on reconnect, rc=%Rrc\n", rc);
+
+        rc = vbDrmIpcClientReleaseResources(&g_hClient);
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to release IPC session resources, rc=%Rrc\n", rc);
+    }
+
+    rc = RTLocalIpcSessionConnect(&g_hSession, VBOX_DRMIPC_SERVER_NAME, 0);
+    if (RT_SUCCESS(rc))
+    {
+        rc = vbDrmIpcClientInit(&g_hClient, RTThreadSelf(), g_hSession, VBOX_DRMIPC_TX_QUEUE_SIZE, vbclSVGASessionRxCallBack);
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to re-initialize IPC session, rc=%Rrc\n", rc);
+    }
+    else
+        VBClLogError("unable to reconnect to IPC server, rc=%Rrc\n", rc);
+
+    int rc2 = RTCritSectLeave(&g_hClientCritSect);
+    if (RT_FAILURE(rc2))
+        VBClLogError("unable to leave critical section on reconnect, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnWorker}
+ */
+static DECLCALLBACK(int) vbclSVGASessionWorker(bool volatile *pfShutdown)
+{
+    int             rc = VINF_SUCCESS;
+
+    /* Notify parent thread that we started successfully. */
+    rc = RTThreadUserSignal(RTThreadSelf());
+    if (RT_FAILURE(rc))
+        VBClLogError("unable to notify parent thread about successful start\n");
+
+    rc = RTCritSectEnter(&g_hClientCritSect);
+
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("unable to enter critical section on worker start, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    rc = vbDrmIpcClientInit(&g_hClient, RTThreadSelf(), g_hSession, VBOX_DRMIPC_TX_QUEUE_SIZE, vbclSVGASessionRxCallBack);
+    int rc2 = RTCritSectLeave(&g_hClientCritSect);
+    if (RT_FAILURE(rc2))
+        VBClLogError("unable to leave critical section on worker start, rc=%Rrc\n", rc);
+
+    if (RT_FAILURE(rc))
+    {
+        VBClLogError("cannot initialize IPC session, rc=%Rrc\n", rc);
+        return rc;
+    }
+
+    for (;;)
+    {
+        rc = vbDrmIpcConnectionHandler(&g_hClient);
+
+        /* Try to shutdown thread as soon as possible. */
+        if (ASMAtomicReadBool(pfShutdown))
+        {
+            /* Shutdown requested. */
+            break;
+        }
+
+        /* Normal case, there was no incoming messages for a while. */
+        if (rc == VERR_TIMEOUT)
+        {
+            continue;
+        }
+        else if (RT_FAILURE(rc))
+        {
+            VBClLogError("unable to handle IPC connection, rc=%Rrc\n", rc);
+
+            /* Relax a bit before spinning the loop. */
+            RTThreadSleep(VBOX_DRMIPC_RX_RELAX_MS);
+            /* Try to reconnect to server. */
+            rc = vbclSVGASessionReconnect();
+        }
+    }
+
+    /* Check if session was not closed before. */
+    if (RT_VALID_PTR(g_hSession))
+    {
+        rc2 = RTCritSectEnter(&g_hClientCritSect);
+        if (RT_SUCCESS(rc2))
+        {
+            rc2 = vbDrmIpcClientReleaseResources(&g_hClient);
+            if (RT_FAILURE(rc2))
+                VBClLogError("cannot release IPC session resources, rc=%Rrc\n", rc2);
+
+            rc2 = RTCritSectLeave(&g_hClientCritSect);
+            if (RT_FAILURE(rc2))
+                VBClLogError("unable to leave critical section on worker end, rc=%Rrc\n", rc);
+        }
+        else
+            VBClLogError("unable to enter critical section on worker end, rc=%Rrc\n", rc);
+    }
+
+    return rc;
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnStop}
+ */
+static DECLCALLBACK(void) vbclSVGASessionStop(void)
+{
+    int rc;
+
+    /* Check if session was not closed before. */
+    if (!RT_VALID_PTR(g_hSession))
+        return;
+
+    /* Attempt to release any waiting syscall related to RTLocalIpcSessionXXX(). */
+    rc = RTLocalIpcSessionFlush(g_hSession);
+    if (RT_FAILURE(rc))
+        VBClLogError("unable to flush data to IPC connection, rc=%Rrc\n", rc);
+
+    rc = RTLocalIpcSessionCancel(g_hSession);
+    if (RT_FAILURE(rc))
+        VBClLogError("unable to cancel IPC session, rc=%Rrc\n", rc);
+}
+
+/**
+ * @interface_method_impl{VBCLSERVICE,pfnTerm}
+ */
+static DECLCALLBACK(int) vbclSVGASessionTerm(void)
+{
+    int rc = VINF_SUCCESS;
+
+    if (g_hSession)
+    {
+        rc = RTLocalIpcSessionClose(g_hSession);
+        g_hSession = 0;
+
+        if (RT_FAILURE(rc))
+            VBClLogError("unable to close IPC connection, rc=%Rrc\n", rc);
+    }
+
+    if (g_pDisplayHelper)
+    {
+        if (g_pDisplayHelper->pfnUnsubscribeDisplayOffsetChangeNotification)
+            g_pDisplayHelper->pfnUnsubscribeDisplayOffsetChangeNotification();
+
+        if (g_pDisplayHelper->pfnTerm)
+        {
+            rc = g_pDisplayHelper->pfnTerm();
+            if (RT_FAILURE(rc))
+                VBClLogError("unable to terminate Desktop Environment helper '%s', rc=%Rrc\n",
+                             rc, g_pDisplayHelper->pszName);
+        }
+    }
+
+    vbclSVGASessionPidFileRelease();
+
+    return VINF_SUCCESS;
+}
+
+VBCLSERVICE g_SvcDisplaySVGASession =
+{
+    "vmsvga-session",                   /* szName */
+    "VMSVGA display assistant",         /* pszDescription */
+    NULL,                               /* pszPidFilePath (no pid file lock) */
+    NULL,                               /* pszUsage */
+    NULL,                               /* pszOptions */
+    NULL,                               /* pfnOption */
+    vbclSVGASessionInit,                /* pfnInit */
+    vbclSVGASessionWorker,              /* pfnWorker */
+    vbclSVGASessionStop,                /* pfnStop */
+    vbclSVGASessionTerm,                /* pfnTerm */
+};
Index: trunk/src/VBox/Additions/x11/VBoxClient/main.cpp
===================================================================
--- trunk/src/VBox/Additions/x11/VBoxClient/main.cpp	(revision 93370)
+++ trunk/src/VBox/Additions/x11/VBoxClient/main.cpp	(revision 93371)
@@ -49,4 +49,5 @@
 #define VBOXCLIENT_OPT_SEAMLESS             VBOXCLIENT_OPT_SERVICES + 3
 #define VBOXCLIENT_OPT_VMSVGA               VBOXCLIENT_OPT_SERVICES + 4
+#define VBOXCLIENT_OPT_VMSVGA_SESSION       VBOXCLIENT_OPT_SERVICES + 5
 
 
@@ -256,5 +257,6 @@
 #endif
 #ifdef VBOX_WITH_VMSVGA
-             "--vmsvga"
+             "--vmsvga|"
+             "--vmsvga-session"
 #endif
              "\n[-d|--nodaemon]\n", pcszFileName);
@@ -275,4 +277,6 @@
 #ifdef VBOX_WITH_VMSVGA
     RTPrintf("  --vmsvga             starts VMSVGA dynamic resizing for X11/Wayland guests\n");
+    RTPrintf("  --vmsvga-session     starts Desktop Environment specific screen assistant for X11/Wayland guests\n"
+             "                       (VMSVGA graphics adapter only)\n");
 #endif
     RTPrintf("  -f, --foreground     run in the foreground (no daemonizing)\n");
@@ -374,4 +378,5 @@
 #ifdef VBOX_WITH_VMSVGA
         { "--vmsvga",                       VBOXCLIENT_OPT_VMSVGA,                    RTGETOPT_REQ_NOTHING },
+        { "--vmsvga-session",               VBOXCLIENT_OPT_VMSVGA_SESSION,            RTGETOPT_REQ_NOTHING },
 #endif
     };
@@ -482,4 +487,11 @@
                     return vbclSyntaxOnlyOneService();
                 g_Service.pDesc = &g_SvcDisplaySVGA;
+                break;
+            }
+            case VBOXCLIENT_OPT_VMSVGA_SESSION:
+            {
+                if (g_Service.pDesc)
+                    return vbclSyntaxOnlyOneService();
+                g_Service.pDesc = &g_SvcDisplaySVGASession;
                 break;
             }
@@ -520,5 +532,5 @@
         return RTMsgErrorExitFailure("VbglR3InitUser failed: %Rrc", rc);
 
-    rc = VBClLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
+    rc = VBClLogCreate(g_szLogFile[0] ? g_szLogFile : "");
     if (RT_FAILURE(rc))
         return RTMsgErrorExitFailure("Failed to create release log '%s', rc=%Rrc\n",
@@ -551,10 +563,14 @@
         if (RT_FAILURE(rc))
             VBClLogFatalError("Creating PID file path failed: %Rrc\n", rc);
-        if (fDaemonise)
-            rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn);
-        if (RT_FAILURE(rc))
-            VBClLogFatalError("Daemonizing service failed: %Rrc\n", rc);
-        if (g_szPidFile[0])
-            rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
+    }
+
+    if (fDaemonise)
+        rc = VbglR3Daemonize(false /* fNoChDir */, false /* fNoClose */, fRespawn, &g_cRespawn);
+    if (RT_FAILURE(rc))
+        VBClLogFatalError("Daemonizing service failed: %Rrc\n", rc);
+
+    if (g_szPidFile[0])
+    {
+        rc = VbglR3PidFile(g_szPidFile, &g_hPidFile);
         if (rc == VERR_FILE_LOCK_VIOLATION)  /* Already running. */
             return RTEXITCODE_SUCCESS;
