/************************************************************************** * 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 #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 */