xenocara/driver/xf86-video-vmware/vmwgfx/vmwgfx_layout.c

271 lines
7.6 KiB
C
Raw Permalink Normal View History

/**************************************************************************
* 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"
#include "../src/common_compat.h"
#ifndef X_DEBUG
#define X_DEBUG X_NOTICE
#endif
/**
* 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 */