summaryrefslogtreecommitdiff
path: root/vmwgfx
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2016-01-20 13:01:21 +0100
committerThomas Hellstrom <thellstrom@vmware.com>2016-02-11 09:14:05 +0100
commit5978597da92898a424837ee89e66f66a8120480c (patch)
tree889a6c2ab2ecd7b3b3ca0fc56c1fb98025d8aca5 /vmwgfx
parent885e360b16fd88b48b40930c6277637615aab188 (diff)
vmware/vmwgfx: Add a layout handler v2
Add a handler that, on hotplug events, scans for a new GUI layout and tries to set that layout using XRandR similar to what the RandR1.2 part of vmware tools resolutionSet module is doing today. v2: Address review comments - Keep the old layout in case of screen resizing errors - Fix the vmwgfx_layout handler() declaration. Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Reviewed-by: Sinclair Yeh <syeh@vmware.com>
Diffstat (limited to 'vmwgfx')
-rw-r--r--vmwgfx/Makefile.am1
-rw-r--r--vmwgfx/vmwgfx_driver.h20
-rw-r--r--vmwgfx/vmwgfx_layout.c265
-rw-r--r--vmwgfx/vmwgfx_output.c100
-rw-r--r--vmwgfx/vmwgfx_rr_inlines.h92
5 files changed, 478 insertions, 0 deletions
diff --git a/vmwgfx/Makefile.am b/vmwgfx/Makefile.am
index 81b9e08..19533e6 100644
--- a/vmwgfx/Makefile.am
+++ b/vmwgfx/Makefile.am
@@ -30,5 +30,6 @@ libvmwgfx_la_SOURCES = \
vmwgfx_hosted_priv.h \
vmwgfx_xmir.c \
vmwgfx_xwayland.c \
+ vmwgfx_layout.c \
wsbm_util.h
endif
diff --git a/vmwgfx/vmwgfx_driver.h b/vmwgfx/vmwgfx_driver.h
index 080ec8c..8113b22 100644
--- a/vmwgfx/vmwgfx_driver.h
+++ b/vmwgfx/vmwgfx_driver.h
@@ -88,6 +88,7 @@ enum xorg_throttling_reason {
struct vmwgfx_hosted;
struct xf86_platform_device;
+struct vmwgfx_layout;
typedef struct _modesettingRec
{
@@ -149,6 +150,7 @@ typedef struct _modesettingRec
#ifdef HAVE_LIBUDEV
struct udev_monitor *uevent_monitor;
InputHandlerProc uevent_handler;
+ struct vmwgfx_layout *layout;
#endif
} modesettingRec, *modesettingPtr;
@@ -196,6 +198,24 @@ void
vmwgfx_uevent_init(ScrnInfoPtr scrn, modesettingPtr ms);
void
vmwgfx_uevent_fini(ScrnInfoPtr scrn, modesettingPtr ms);
+Bool
+vmwgfx_output_has_origin(xf86OutputPtr output);
+void
+vmwgfx_output_origin(xf86OutputPtr output, int *x, int *y);
+void
+vmwgfx_outputs_off(ScrnInfoPtr pScrn);
+void
+vmwgfx_outputs_on(ScrnInfoPtr pScrn);
+
+/***********************************************************************
+ * vmwgfx_layout.c
+ */
+struct vmwgfx_layout *
+vmwgfx_layout_from_kms(ScrnInfoPtr pScrn);
+void
+vmwgfx_layout_configuration(ScrnInfoPtr pScrn, struct vmwgfx_layout *layout);
+void
+vmwgfx_layout_handler(ScrnInfoPtr pScrn);
/***********************************************************************
* xorg_xv.c
diff --git a/vmwgfx/vmwgfx_layout.c b/vmwgfx/vmwgfx_layout.c
new file mode 100644
index 0000000..2c67dd5
--- /dev/null
+++ b/vmwgfx/vmwgfx_layout.c
@@ -0,0 +1,265 @@
+/**************************************************************************
+ * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIBUDEV
+#include "vmwgfx_driver.h"
+#include <xf86Crtc.h>
+#include "vmwgfx_rr_inlines.h"
+
+/**
+ * struct vmwgfx_layout_box - Struct representing a GUI layout rect
+ *
+ * @x: X value of the origin.
+ * @y: Y value of the origin.
+ * @width: Width of the rect.
+ * @height: Height of the rect.
+ */
+struct vmwgfx_layout_box {
+ int x, y, width, height;
+};
+
+/**
+ * struct vmwgfx_layout - Struct representing a complete GUI layout
+ *
+ * @connected: Number of connected outputs.
+ * @root_width: Width of full desktop.
+ * @root_height: Height of full desktop.
+ * @boxes: Array of GUI layout rects.
+ */
+struct vmwgfx_layout {
+ int connected;
+ int root_width;
+ int root_height;
+ struct vmwgfx_layout_box boxes[];
+};
+
+/**
+ * vmwgfx_layout_debug - Log debug info of a layout struct.
+ *
+ * @pScrn: ScrnInfoPtr: Pointer to the ScrnInfo struct for the screen the
+ * layout should be logged for.
+ * @l1: Pointer to a valid struct vmwgfx_layout.
+ */
+static void
+vmwgfx_layout_debug(ScrnInfoPtr pScrn, const struct vmwgfx_layout *l1)
+{
+ int i;
+
+ xf86DrvMsg(pScrn->scrnIndex, X_DEBUG, "New layout.\n");
+ for (i = 0; i < l1->connected; ++i)
+ xf86DrvMsg(pScrn->scrnIndex, X_DEBUG,
+ "%d: %d %d %d %d\n", i, l1->boxes[i].x,
+ l1->boxes[i].y, l1->boxes[i].width, l1->boxes[i].height);
+ xf86DrvMsg(pScrn->scrnIndex, X_DEBUG, "\n");
+}
+
+/**
+ * vmwgfx_layouts_equal - Determine whether two layouts are equal.
+ *
+ * @l1: Pointer to the first struct vmwgfx_layout.
+ * @l2: Pointer to the second struct vmwgfx_layout.
+ *
+ * Returns: TRUE if the layouts are equal. FALSE otherwise.
+ */
+static Bool
+vmwgfx_layouts_equal(const struct vmwgfx_layout *l1,
+ const struct vmwgfx_layout *l2)
+{
+ if (l1->connected != l2->connected)
+ return FALSE;
+
+ if (!l1->connected)
+ return TRUE;
+
+ return !memcmp(l1->boxes, l2->boxes,
+ l1->connected*sizeof(struct vmwgfx_layout_box));
+}
+
+/**
+ * vmwgfx_layout_from_kms - Construct a struct vmwgfx_layout from KMS info.
+ *
+ * @pScrn: Pointer to a ScrnInfo struct.
+ *
+ * Returns: A pointer to a newly allocated struct vmwgfx_layout if
+ * successful. NULL otherwise.
+ */
+struct vmwgfx_layout *
+vmwgfx_layout_from_kms(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i, connected;
+ struct vmwgfx_layout *layout;
+ size_t size;
+ int min_x = INT_MAX, max_x = INT_MIN, min_y = INT_MAX, max_y = INT_MIN;
+
+ for (i = 0; i < config->num_output; ++i) {
+ xf86OutputPtr output = config->output[i];
+
+ if (!vmwgfx_output_has_origin(output))
+ return NULL;
+
+ if (output->status != XF86OutputStatusConnected)
+ break;
+ }
+ connected = i;
+
+ size = offsetof(struct vmwgfx_layout, boxes) +
+ connected * sizeof(struct vmwgfx_layout_box);
+ layout = calloc(1, size);
+ if (!layout)
+ return NULL;
+
+ layout->connected = connected;
+ for (i = 0; i < connected; ++i) {
+ struct vmwgfx_layout_box *box = &layout->boxes[i];
+ xf86OutputPtr output = config->output[i];
+ DisplayModePtr mode = output->probed_modes;
+
+ if (mode == NULL) {
+ free(layout);
+ return NULL;
+ }
+
+ vmwgfx_output_origin(output, &box->x, &box->y);
+ box->width = output->probed_modes->HDisplay;
+ box->height = output->probed_modes->VDisplay;
+ min_x = min(min_x, box->x);
+ min_y = min(min_y, box->y);
+ max_x = max(max_x, box->x + box->width);
+ max_y = max(max_y, box->y + box->height);
+ }
+
+ layout->root_width = max_x;
+ layout->root_height = max_y;
+
+ return layout;
+}
+
+/**
+ * vmwgfx_layout_configuration - Set up the screen modesetting configuration
+ * from a struct vmwgfx_layout.
+ *
+ * @pScrn: Pointer to a ScrnInfo struct.
+ * @layout: Layout to use for the new configuration.
+ *
+ * Sets up a new modesetting configuration. Note that the configuration needs
+ * to be committed using xf86SetDesiredModes().
+ */
+void
+vmwgfx_layout_configuration(ScrnInfoPtr pScrn, struct vmwgfx_layout *layout)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ struct vmwgfx_layout_box *box;
+ xf86OutputPtr output;
+ xf86CrtcPtr crtc;
+ int i, j;
+
+ for (j = 0; j < config->num_crtc; ++j) {
+ crtc = config->crtc[j];
+ crtc->enabled = FALSE;
+ }
+
+ for (i = 0, box = layout->boxes; i < config->num_output; ++i, ++box) {
+ output = config->output[i];
+ output->crtc = NULL;
+ if (i >= layout->connected)
+ continue;
+
+ for (j = 0; j < config->num_crtc; ++j) {
+ crtc = config->crtc[j];
+ if (!crtc->enabled && (output->possible_crtcs & (1 << j))) {
+ crtc->enabled = TRUE;
+ output->crtc = crtc;
+ break;
+ }
+ }
+
+ if (!output->crtc)
+ continue;
+
+ crtc = output->crtc;
+ xf86SaveModeContents(&crtc->desiredMode, output->probed_modes);
+ crtc->desiredRotation = RR_Rotate_0;
+ crtc->desiredX = box->x;
+ crtc->desiredY = box->y;
+ crtc->desiredTransformPresent = FALSE;
+ }
+}
+
+/**
+ * vmwgfx_layout_handler - Obtain and set a new layout.
+ *
+ * @pScrn: Pointer to a ScrnInfo struct.
+ *
+ * Obtains a new layout from DRM. If the layout differs from the current one,
+ * Try to set the new layout. If that fails, (typically due to root pixmap
+ * resizing issues) try hard to revert to the old layout. Finally
+ * update RandR in a way that tries to block racing display managers
+ * from setting up the layout in a different way.
+ */
+void
+vmwgfx_layout_handler(ScrnInfoPtr pScrn)
+{
+
+ ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
+ modesettingPtr ms = modesettingPTR(pScrn);
+ struct vmwgfx_layout *layout;
+
+ if (!pScreen)
+ return;
+
+ /*
+ * Construct a layout from the new information and determine whether we
+ * need to take action
+ */
+ layout = vmwgfx_layout_from_kms(pScrn);
+ if (layout && (!ms->layout || !vmwgfx_layouts_equal(ms->layout, layout))) {
+ vmwgfx_layout_debug(pScrn, layout);
+ vmwgfx_outputs_off(pScrn);
+ xf86DisableUnusedFunctions(pScrn);
+ if (!vmwgfx_rr_screen_set_size(pScreen, layout->root_width,
+ layout->root_height)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Resizing screen failed.\n");
+ vmwgfx_outputs_on(pScrn);
+ free(layout);
+ } else {
+ vmwgfx_layout_configuration(pScrn, layout);
+ if (ms->layout)
+ free(ms->layout);
+ ms->layout = layout;
+ }
+ xf86SetDesiredModes(pScrn);
+ vmwgfx_notify_rr(pScreen);
+ } else if (layout) {
+ free(layout);
+ }
+}
+
+#endif /* HAVE_LIBUDEV */
diff --git a/vmwgfx/vmwgfx_output.c b/vmwgfx/vmwgfx_output.c
index eadb1a2..ddd8d5c 100644
--- a/vmwgfx/vmwgfx_output.c
+++ b/vmwgfx/vmwgfx_output.c
@@ -74,6 +74,10 @@ struct output_private
struct output_prop *props;
int c;
Bool is_implicit;
+ int suggested_x;
+ int suggested_y;
+ xf86CrtcPtr saved_crtc;
+ Bool saved_crtc_enabled;
};
static const char *output_enum_list[] = {
@@ -96,6 +100,40 @@ static const char *output_enum_list[] = {
};
/**
+ * vmwgfx_output_has_origin - Whether we've detected layout info on the DRM
+ * connector.
+ *
+ * @output: The output to consider.
+ *
+ * Returns: TRUE if the corresponding DRM connector has layout info.
+ * FALSE otherwise.
+ */
+Bool
+vmwgfx_output_has_origin(xf86OutputPtr output)
+{
+ struct output_private *vmwgfx_output = output->driver_private;
+
+ return vmwgfx_output->suggested_x != -1 &&
+ vmwgfx_output->suggested_y != -1;
+}
+
+/**
+ * vmwgfx_output_origin - Get the origin for an output in the GUI layout.
+ *
+ * @output: The output to consider.
+ * @x: Outputs the x coordinate of the origin.
+ * @y: Outputs the y coordinate of the origin.
+ */
+void
+vmwgfx_output_origin(xf86OutputPtr output, int *x, int *y)
+{
+ struct output_private *vmwgfx_output = output->driver_private;
+
+ *x = vmwgfx_output->props[vmwgfx_output->suggested_x].value;
+ *y = vmwgfx_output->props[vmwgfx_output->suggested_y].value;
+}
+
+/**
* output_property_ignore - Function to determine whether to ignore or
* to re-export a drm property.
*
@@ -144,6 +182,10 @@ output_create_resources(xf86OutputPtr output)
vmwgfx_output->props[j].index = i;
vmwgfx_output->props[j].mode_prop = drmmode_prop;
vmwgfx_output->props[j].value = drm_connector->prop_values[i];
+ if (!strcmp(drmmode_prop->name,"suggested X"))
+ vmwgfx_output->suggested_x = j;
+ if (!strcmp(drmmode_prop->name,"suggested Y"))
+ vmwgfx_output->suggested_y = j;
vmwgfx_output->num_props++;
j++;
}
@@ -580,6 +622,8 @@ xorg_output_init(ScrnInfoPtr pScrn)
}
priv->is_implicit = is_implicit;
+ priv->suggested_x = -1;
+ priv->suggested_y = -1;
drm_encoder = drmModeGetEncoder(ms->fd, drm_connector->encoders[0]);
if (drm_encoder) {
@@ -650,6 +694,60 @@ vmwgfx_output_properties_scan(ScrnInfoPtr pScrn)
}
/**
+ * vmwgfx_outputs_off - Mark all crtc / output pairs as disabled and save
+ * their configuration.
+ *
+ * @pScrn: Pointer to a ScrnInfo struct.
+ *
+ * Note that to commit this to the display system, a call to this function
+ * should be followed by a call to xf86DisableUnusedFunctions()
+ */
+void
+vmwgfx_outputs_off(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for (i = 0; i < config->num_output; ++i) {
+ xf86OutputPtr output = config->output[i];
+ struct output_private *vmwgfx_output = output->driver_private;
+
+ vmwgfx_output->saved_crtc = output->crtc;
+ if (output->crtc) {
+ vmwgfx_output->saved_crtc_enabled = output->crtc->enabled;
+ output->crtc->enabled = FALSE;
+ output->crtc = NULL;
+ }
+ }
+}
+
+/**
+ * vmwgfx_outputs_on - Reset crtc / output pairs to a configuation saved
+ * using vmwgfx_output_off.
+ *
+ * @pScrn: Pointer to a ScrnInfo struct.
+ *
+ * Note that to commit the setup to the display system, a call to this
+ * function should be followed by a call to xf86SetDesiredModes().
+ */
+void
+vmwgfx_outputs_on(ScrnInfoPtr pScrn)
+{
+ xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
+ int i;
+
+ for (i = 0; i < config->num_output; ++i) {
+ xf86OutputPtr output = config->output[i];
+ struct output_private *vmwgfx_output = output->driver_private;
+
+ if (vmwgfx_output->saved_crtc) {
+ output->crtc = vmwgfx_output->saved_crtc;
+ output->crtc->enabled = vmwgfx_output->saved_crtc_enabled;
+ }
+ }
+}
+
+/**
* vmwgfx_handle uevent - Property update callback
*
* @fd: File descriptor for the uevent
@@ -673,6 +771,8 @@ vmwgfx_handle_uevents(int fd, void *closure)
if (pScreen)
RRGetInfo(pScreen, TRUE);
+ vmwgfx_layout_handler(scrn);
+
udev_device_unref(dev);
}
#endif /* HAVE_LIBUDEV */
diff --git a/vmwgfx/vmwgfx_rr_inlines.h b/vmwgfx/vmwgfx_rr_inlines.h
new file mode 100644
index 0000000..9608fd2
--- /dev/null
+++ b/vmwgfx/vmwgfx_rr_inlines.h
@@ -0,0 +1,92 @@
+/**************************************************************************
+ * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+/*
+ * This file contains calls into the RandR1.2 code and modification of core
+ * RandR data structures that probably were never intended from drivers.
+ */
+
+#ifndef _VMWGFX_RR_INLINES_H_
+#define _VMWGFX_RR_INLINES_H_
+
+#include <xf86Crtc.h>
+#include <xf86RandR12.h>
+
+#define VMW_DPI 96.
+#define VMW_INCH_TO_MM 25.4
+
+/**
+ * vmwgfx_notify_rr - Notify RandR that our configuration has changed.
+ *
+ * @pScreen: Pointer to the affected screen.
+ *
+ * Normally screen configurations are typically only changed using RandR,
+ * so when we do it in an udev handler, we need to notify RandR that we've
+ * made a change, so that it can be propagated to all RandR clients.
+ */
+static inline void
+vmwgfx_notify_rr(ScreenPtr pScreen)
+{
+ rrScrPriv(pScreen);
+
+
+ /*
+ * We need to update the time-stamps, otherwise X clients that haven't
+ * yet read this config might just overwrite it.
+ * This effectively stops the desktop manager from trying to
+ * outsmart us, since RandR simply doesn't accept requests from
+ * clients that haven't read this config and tag their request with
+ * an earlier timestamp.
+ */
+ pScrPriv->lastSetTime = currentTime;
+ pScrPriv->lastConfigTime = currentTime;
+#ifdef RANDR_12_INTERFACE
+ xf86RandR12TellChanged(pScreen);
+#else
+ RRTellChanged(pScreen);
+#endif
+}
+
+/**
+ * vmwgfx_rr_screen_set_size - Use RandR to change the root pixmap dimensions.
+ *
+ * @pScreen: Pointer to the affected screen.
+ *
+ * Returns: TRUE if successful. False otherwise.
+ */
+static inline Bool
+vmwgfx_rr_screen_set_size(ScreenPtr pScreen, int width, int height)
+{
+ rrScrPriv(pScreen);
+ float mm_width, mm_height;
+
+ mm_width = ((float) width) * VMW_INCH_TO_MM / VMW_DPI + .5;
+ mm_height = ((float) height) * VMW_INCH_TO_MM / VMW_DPI + .5;
+
+ return pScrPriv->rrScreenSetSize(pScreen, width, height,
+ (int) mm_width, (int) mm_height);
+}
+
+#endif