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