1/************************************************************************** 2 * Copyright © 2016 VMware, Inc., Palo Alto, CA., USA 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sub license, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial portions 15 * of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 **************************************************************************/ 26 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30 31#ifdef HAVE_LIBUDEV 32#include "vmwgfx_driver.h" 33#include <xf86Crtc.h> 34#include "vmwgfx_rr_inlines.h" 35#include "../src/common_compat.h" 36 37#ifndef X_DEBUG 38#define X_DEBUG X_NOTICE 39#endif 40 41/** 42 * struct vmwgfx_layout_box - Struct representing a GUI layout rect 43 * 44 * @x: X value of the origin. 45 * @y: Y value of the origin. 46 * @width: Width of the rect. 47 * @height: Height of the rect. 48 */ 49struct vmwgfx_layout_box { 50 int x, y, width, height; 51}; 52 53/** 54 * struct vmwgfx_layout - Struct representing a complete GUI layout 55 * 56 * @connected: Number of connected outputs. 57 * @root_width: Width of full desktop. 58 * @root_height: Height of full desktop. 59 * @boxes: Array of GUI layout rects. 60 */ 61struct vmwgfx_layout { 62 int connected; 63 int root_width; 64 int root_height; 65 struct vmwgfx_layout_box boxes[]; 66}; 67 68/** 69 * vmwgfx_layout_debug - Log debug info of a layout struct. 70 * 71 * @pScrn: ScrnInfoPtr: Pointer to the ScrnInfo struct for the screen the 72 * layout should be logged for. 73 * @l1: Pointer to a valid struct vmwgfx_layout. 74 */ 75static void 76vmwgfx_layout_debug(ScrnInfoPtr pScrn, const struct vmwgfx_layout *l1) 77{ 78 int i; 79 80 xf86DrvMsg(pScrn->scrnIndex, X_DEBUG, "New layout.\n"); 81 for (i = 0; i < l1->connected; ++i) 82 xf86DrvMsg(pScrn->scrnIndex, X_DEBUG, 83 "%d: %d %d %d %d\n", i, l1->boxes[i].x, 84 l1->boxes[i].y, l1->boxes[i].width, l1->boxes[i].height); 85 xf86DrvMsg(pScrn->scrnIndex, X_DEBUG, "\n"); 86} 87 88/** 89 * vmwgfx_layouts_equal - Determine whether two layouts are equal. 90 * 91 * @l1: Pointer to the first struct vmwgfx_layout. 92 * @l2: Pointer to the second struct vmwgfx_layout. 93 * 94 * Returns: TRUE if the layouts are equal. FALSE otherwise. 95 */ 96static Bool 97vmwgfx_layouts_equal(const struct vmwgfx_layout *l1, 98 const struct vmwgfx_layout *l2) 99{ 100 if (l1->connected != l2->connected) 101 return FALSE; 102 103 if (!l1->connected) 104 return TRUE; 105 106 return !memcmp(l1->boxes, l2->boxes, 107 l1->connected*sizeof(struct vmwgfx_layout_box)); 108} 109 110/** 111 * vmwgfx_layout_from_kms - Construct a struct vmwgfx_layout from KMS info. 112 * 113 * @pScrn: Pointer to a ScrnInfo struct. 114 * 115 * Returns: A pointer to a newly allocated struct vmwgfx_layout if 116 * successful. NULL otherwise. 117 */ 118struct vmwgfx_layout * 119vmwgfx_layout_from_kms(ScrnInfoPtr pScrn) 120{ 121 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 122 int i, connected; 123 struct vmwgfx_layout *layout; 124 size_t size; 125 int min_x = INT_MAX, max_x = INT_MIN, min_y = INT_MAX, max_y = INT_MIN; 126 127 for (i = 0; i < config->num_output; ++i) { 128 xf86OutputPtr output = config->output[i]; 129 130 if (!vmwgfx_output_has_origin(output)) 131 return NULL; 132 133 if (output->status != XF86OutputStatusConnected) 134 break; 135 } 136 connected = i; 137 138 size = offsetof(struct vmwgfx_layout, boxes) + 139 connected * sizeof(struct vmwgfx_layout_box); 140 layout = calloc(1, size); 141 if (!layout) 142 return NULL; 143 144 layout->connected = connected; 145 for (i = 0; i < connected; ++i) { 146 struct vmwgfx_layout_box *box = &layout->boxes[i]; 147 xf86OutputPtr output = config->output[i]; 148 DisplayModePtr mode = output->probed_modes; 149 150 if (mode == NULL) { 151 free(layout); 152 return NULL; 153 } 154 155 vmwgfx_output_origin(output, &box->x, &box->y); 156 box->width = output->probed_modes->HDisplay; 157 box->height = output->probed_modes->VDisplay; 158 min_x = min(min_x, box->x); 159 min_y = min(min_y, box->y); 160 max_x = max(max_x, box->x + box->width); 161 max_y = max(max_y, box->y + box->height); 162 } 163 164 layout->root_width = max_x; 165 layout->root_height = max_y; 166 167 return layout; 168} 169 170/** 171 * vmwgfx_layout_configuration - Set up the screen modesetting configuration 172 * from a struct vmwgfx_layout. 173 * 174 * @pScrn: Pointer to a ScrnInfo struct. 175 * @layout: Layout to use for the new configuration. 176 * 177 * Sets up a new modesetting configuration. Note that the configuration needs 178 * to be committed using xf86SetDesiredModes(). 179 */ 180void 181vmwgfx_layout_configuration(ScrnInfoPtr pScrn, struct vmwgfx_layout *layout) 182{ 183 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); 184 struct vmwgfx_layout_box *box; 185 xf86OutputPtr output; 186 xf86CrtcPtr crtc; 187 int i, j; 188 189 for (j = 0; j < config->num_crtc; ++j) { 190 crtc = config->crtc[j]; 191 crtc->enabled = FALSE; 192 } 193 194 for (i = 0, box = layout->boxes; i < config->num_output; ++i, ++box) { 195 output = config->output[i]; 196 output->crtc = NULL; 197 if (i >= layout->connected) 198 continue; 199 200 for (j = 0; j < config->num_crtc; ++j) { 201 crtc = config->crtc[j]; 202 if (!crtc->enabled && (output->possible_crtcs & (1 << j))) { 203 crtc->enabled = TRUE; 204 output->crtc = crtc; 205 break; 206 } 207 } 208 209 if (!output->crtc) 210 continue; 211 212 crtc = output->crtc; 213 xf86SaveModeContents(&crtc->desiredMode, output->probed_modes); 214 crtc->desiredRotation = RR_Rotate_0; 215 crtc->desiredX = box->x; 216 crtc->desiredY = box->y; 217 crtc->desiredTransformPresent = FALSE; 218 } 219} 220 221/** 222 * vmwgfx_layout_handler - Obtain and set a new layout. 223 * 224 * @pScrn: Pointer to a ScrnInfo struct. 225 * 226 * Obtains a new layout from DRM. If the layout differs from the current one, 227 * Try to set the new layout. If that fails, (typically due to root pixmap 228 * resizing issues) try hard to revert to the old layout. Finally 229 * update RandR in a way that tries to block racing display managers 230 * from setting up the layout in a different way. 231 */ 232void 233vmwgfx_layout_handler(ScrnInfoPtr pScrn) 234{ 235 236 ScreenPtr pScreen = xf86ScrnToScreen(pScrn); 237 modesettingPtr ms = modesettingPTR(pScrn); 238 struct vmwgfx_layout *layout; 239 240 if (!pScreen) 241 return; 242 243 /* 244 * Construct a layout from the new information and determine whether we 245 * need to take action 246 */ 247 layout = vmwgfx_layout_from_kms(pScrn); 248 if (layout && (!ms->layout || !vmwgfx_layouts_equal(ms->layout, layout))) { 249 vmwgfx_layout_debug(pScrn, layout); 250 vmwgfx_outputs_off(pScrn); 251 xf86DisableUnusedFunctions(pScrn); 252 if (!vmwgfx_rr_screen_set_size(pScreen, layout->root_width, 253 layout->root_height)) { 254 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Resizing screen failed.\n"); 255 vmwgfx_outputs_on(pScrn); 256 free(layout); 257 } else { 258 vmwgfx_layout_configuration(pScrn, layout); 259 if (ms->layout) 260 free(ms->layout); 261 ms->layout = layout; 262 } 263 xf86SetDesiredModes(pScrn); 264 vmwgfx_notify_rr(pScreen); 265 } else if (layout) { 266 free(layout); 267 } 268} 269 270#endif /* HAVE_LIBUDEV */ 271