diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2016-01-20 13:01:21 +0100 |
---|---|---|
committer | Thomas Hellstrom <thellstrom@vmware.com> | 2016-02-11 09:14:05 +0100 |
commit | 5978597da92898a424837ee89e66f66a8120480c (patch) | |
tree | 889a6c2ab2ecd7b3b3ca0fc56c1fb98025d8aca5 | |
parent | 885e360b16fd88b48b40930c6277637615aab188 (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>
-rw-r--r-- | vmwgfx/Makefile.am | 1 | ||||
-rw-r--r-- | vmwgfx/vmwgfx_driver.h | 20 | ||||
-rw-r--r-- | vmwgfx/vmwgfx_layout.c | 265 | ||||
-rw-r--r-- | vmwgfx/vmwgfx_output.c | 100 | ||||
-rw-r--r-- | vmwgfx/vmwgfx_rr_inlines.h | 92 |
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 |