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  */
49 struct 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  */
61 struct 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  */
75 static void
vmwgfx_layout_debug(ScrnInfoPtr pScrn,const struct vmwgfx_layout * l1)76 vmwgfx_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  */
96 static Bool
vmwgfx_layouts_equal(const struct vmwgfx_layout * l1,const struct vmwgfx_layout * l2)97 vmwgfx_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  */
118 struct vmwgfx_layout *
vmwgfx_layout_from_kms(ScrnInfoPtr pScrn)119 vmwgfx_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  */
180 void
vmwgfx_layout_configuration(ScrnInfoPtr pScrn,struct vmwgfx_layout * layout)181 vmwgfx_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  */
232 void
vmwgfx_layout_handler(ScrnInfoPtr pScrn)233 vmwgfx_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