1 /*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2011 Aaron Plattner
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission. The copyright holders make no representations
12 * about the suitability of this software for any purpose. It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24 #ifdef HAVE_XORG_CONFIG_H
25 #include <xorg-config.h>
26 #endif
27
28 #include <stddef.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include "mi.h"
32 #include "xf86.h"
33 #include "xf86DDC.h"
34 #include "windowstr.h"
35 #include "xf86Crtc.h"
36 #include "xf86Modes.h"
37 #include "xf86RandR12.h"
38 #include "X11/extensions/render.h"
39 #include "X11/extensions/dpmsconst.h"
40 #include "X11/Xatom.h"
41
42 static void
xf86RotateCrtcRedisplay(xf86CrtcPtr crtc,RegionPtr region)43 xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
44 {
45 ScrnInfoPtr scrn = crtc->scrn;
46 ScreenPtr screen = scrn->pScreen;
47 WindowPtr root = screen->root;
48 PixmapPtr dst_pixmap = crtc->rotatedPixmap;
49 PictFormatPtr format = PictureWindowFormat(screen->root);
50 int error;
51 PicturePtr src, dst;
52 int n = RegionNumRects(region);
53 BoxPtr b = RegionRects(region);
54 XID include_inferiors = IncludeInferiors;
55
56 if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput)
57 return;
58
59 src = CreatePicture(None,
60 &root->drawable,
61 format,
62 CPSubwindowMode,
63 &include_inferiors, serverClient, &error);
64 if (!src)
65 return;
66
67 dst = CreatePicture(None,
68 &dst_pixmap->drawable,
69 format, 0L, NULL, serverClient, &error);
70 if (!dst)
71 return;
72
73 error = SetPictureTransform(src, &crtc->crtc_to_framebuffer);
74 if (error)
75 return;
76 if (crtc->transform_in_use && crtc->filter)
77 SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);
78
79 if (crtc->shadowClear) {
80 CompositePicture(PictOpSrc,
81 src, NULL, dst,
82 0, 0, 0, 0, 0, 0,
83 crtc->mode.HDisplay, crtc->mode.VDisplay);
84 crtc->shadowClear = FALSE;
85 }
86 else {
87 while (n--) {
88 BoxRec dst_box;
89
90 dst_box = *b;
91 dst_box.x1 -= crtc->filter_width >> 1;
92 dst_box.x2 += crtc->filter_width >> 1;
93 dst_box.y1 -= crtc->filter_height >> 1;
94 dst_box.y2 += crtc->filter_height >> 1;
95 pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &dst_box);
96 CompositePicture(PictOpSrc,
97 src, NULL, dst,
98 dst_box.x1, dst_box.y1, 0, 0, dst_box.x1,
99 dst_box.y1, dst_box.x2 - dst_box.x1,
100 dst_box.y2 - dst_box.y1);
101 b++;
102 }
103 }
104 FreePicture(src, None);
105 FreePicture(dst, None);
106 }
107
108 static void
xf86CrtcDamageShadow(xf86CrtcPtr crtc)109 xf86CrtcDamageShadow(xf86CrtcPtr crtc)
110 {
111 ScrnInfoPtr pScrn = crtc->scrn;
112 BoxRec damage_box;
113 RegionRec damage_region;
114 ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
115
116 damage_box.x1 = 0;
117 damage_box.x2 = crtc->mode.HDisplay;
118 damage_box.y1 = 0;
119 damage_box.y2 = crtc->mode.VDisplay;
120 if (!pixman_transform_bounds(&crtc->crtc_to_framebuffer, &damage_box)) {
121 damage_box.x1 = 0;
122 damage_box.y1 = 0;
123 damage_box.x2 = pScreen->width;
124 damage_box.y2 = pScreen->height;
125 }
126 if (damage_box.x1 < 0)
127 damage_box.x1 = 0;
128 if (damage_box.y1 < 0)
129 damage_box.y1 = 0;
130 if (damage_box.x2 > pScreen->width)
131 damage_box.x2 = pScreen->width;
132 if (damage_box.y2 > pScreen->height)
133 damage_box.y2 = pScreen->height;
134 RegionInit(&damage_region, &damage_box, 1);
135 DamageDamageRegion(&(*pScreen->GetScreenPixmap) (pScreen)->drawable,
136 &damage_region);
137 RegionUninit(&damage_region);
138 crtc->shadowClear = TRUE;
139 }
140
141 static void
xf86RotatePrepare(ScreenPtr pScreen)142 xf86RotatePrepare(ScreenPtr pScreen)
143 {
144 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
145 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
146 int c;
147
148 for (c = 0; c < xf86_config->num_crtc; c++) {
149 xf86CrtcPtr crtc = xf86_config->crtc[c];
150
151 if (crtc->rotatedData && !crtc->rotatedPixmap) {
152 crtc->rotatedPixmap = crtc->funcs->shadow_create(crtc,
153 crtc->rotatedData,
154 crtc->mode.
155 HDisplay,
156 crtc->mode.
157 VDisplay);
158 if (!xf86_config->rotation_damage_registered) {
159 /* Hook damage to screen pixmap */
160 DamageRegister(&pScreen->root->drawable,
161 xf86_config->rotation_damage);
162 xf86_config->rotation_damage_registered = TRUE;
163 EnableLimitedSchedulingLatency();
164 }
165
166 xf86CrtcDamageShadow(crtc);
167 }
168 }
169 }
170
171 static Bool
xf86RotateRedisplay(ScreenPtr pScreen)172 xf86RotateRedisplay(ScreenPtr pScreen)
173 {
174 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
175 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
176 DamagePtr damage = xf86_config->rotation_damage;
177 RegionPtr region;
178
179 if (!damage || !pScreen->root)
180 return FALSE;
181 xf86RotatePrepare(pScreen);
182 region = DamageRegion(damage);
183 if (RegionNotEmpty(region)) {
184 int c;
185 SourceValidateProcPtr SourceValidate;
186
187 /*
188 * SourceValidate is used by the software cursor code
189 * to pull the cursor off of the screen when reading
190 * bits from the frame buffer. Bypassing this function
191 * leaves the software cursor in place
192 */
193 SourceValidate = pScreen->SourceValidate;
194 pScreen->SourceValidate = miSourceValidate;
195
196 for (c = 0; c < xf86_config->num_crtc; c++) {
197 xf86CrtcPtr crtc = xf86_config->crtc[c];
198
199 if (crtc->transform_in_use && crtc->enabled) {
200 RegionRec crtc_damage;
201
202 /* compute portion of damage that overlaps crtc */
203 RegionInit(&crtc_damage, &crtc->bounds, 1);
204 RegionIntersect(&crtc_damage, &crtc_damage, region);
205
206 /* update damaged region */
207 if (RegionNotEmpty(&crtc_damage))
208 xf86RotateCrtcRedisplay(crtc, &crtc_damage);
209
210 RegionUninit(&crtc_damage);
211 }
212 }
213 pScreen->SourceValidate = SourceValidate;
214 DamageEmpty(damage);
215 }
216 return TRUE;
217 }
218
219 static void
xf86RotateBlockHandler(ScreenPtr pScreen,void * pTimeout)220 xf86RotateBlockHandler(ScreenPtr pScreen, void *pTimeout)
221 {
222 ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
223 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
224
225 /* Unwrap before redisplay in case the software
226 * cursor layer wants to add its block handler to the
227 * chain
228 */
229 pScreen->BlockHandler = xf86_config->BlockHandler;
230
231 xf86RotateRedisplay(pScreen);
232
233 (*pScreen->BlockHandler) (pScreen, pTimeout);
234
235 /* Re-wrap if we still need this hook */
236 if (xf86_config->rotation_damage != NULL) {
237 xf86_config->BlockHandler = pScreen->BlockHandler;
238 pScreen->BlockHandler = xf86RotateBlockHandler;
239 } else
240 xf86_config->BlockHandler = NULL;
241 }
242
243 void
xf86RotateDestroy(xf86CrtcPtr crtc)244 xf86RotateDestroy(xf86CrtcPtr crtc)
245 {
246 ScrnInfoPtr pScrn = crtc->scrn;
247 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
248 int c;
249
250 /* Free memory from rotation */
251 if (crtc->rotatedPixmap || crtc->rotatedData) {
252 crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
253 crtc->rotatedData);
254 crtc->rotatedPixmap = NULL;
255 crtc->rotatedData = NULL;
256 }
257
258 for (c = 0; c < xf86_config->num_crtc; c++)
259 if (xf86_config->crtc[c]->rotatedData)
260 return;
261
262 /*
263 * Clean up damage structures when no crtcs are rotated
264 */
265 if (xf86_config->rotation_damage) {
266 /* Free damage structure */
267 if (xf86_config->rotation_damage_registered) {
268 xf86_config->rotation_damage_registered = FALSE;
269 DisableLimitedSchedulingLatency();
270 }
271 DamageDestroy(xf86_config->rotation_damage);
272 xf86_config->rotation_damage = NULL;
273 }
274 }
275
276 void
xf86RotateFreeShadow(ScrnInfoPtr pScrn)277 xf86RotateFreeShadow(ScrnInfoPtr pScrn)
278 {
279 xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
280 int c;
281
282 for (c = 0; c < config->num_crtc; c++) {
283 xf86CrtcPtr crtc = config->crtc[c];
284
285 if (crtc->rotatedPixmap || crtc->rotatedData) {
286 crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
287 crtc->rotatedData);
288 crtc->rotatedPixmap = NULL;
289 crtc->rotatedData = NULL;
290 }
291 }
292 }
293
294 void
xf86RotateCloseScreen(ScreenPtr screen)295 xf86RotateCloseScreen(ScreenPtr screen)
296 {
297 ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
298 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
299 int c;
300
301 /* This has already been destroyed when the root window was destroyed */
302 xf86_config->rotation_damage = NULL;
303 for (c = 0; c < xf86_config->num_crtc; c++)
304 xf86RotateDestroy(xf86_config->crtc[c]);
305 }
306
307 static Bool
xf86CrtcFitsScreen(xf86CrtcPtr crtc,struct pict_f_transform * crtc_to_fb)308 xf86CrtcFitsScreen(xf86CrtcPtr crtc, struct pict_f_transform *crtc_to_fb)
309 {
310 ScrnInfoPtr pScrn = crtc->scrn;
311 BoxRec b;
312
313 /* When called before PreInit, the driver is
314 * presumably doing load detect
315 */
316 if (pScrn->is_gpu) {
317 ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
318 if (pScreen->current_master)
319 pScrn = xf86ScreenToScrn(pScreen->current_master);
320 }
321
322 if (pScrn->virtualX == 0 || pScrn->virtualY == 0)
323 return TRUE;
324
325 b.x1 = 0;
326 b.y1 = 0;
327 b.x2 = crtc->mode.HDisplay;
328 b.y2 = crtc->mode.VDisplay;
329 if (crtc_to_fb)
330 pixman_f_transform_bounds(crtc_to_fb, &b);
331 else {
332 b.x1 += crtc->x;
333 b.y1 += crtc->y;
334 b.x2 += crtc->x;
335 b.y2 += crtc->y;
336 }
337
338 return (0 <= b.x1 && b.x2 <= pScrn->virtualX &&
339 0 <= b.y1 && b.y2 <= pScrn->virtualY);
340 }
341
342 Bool
xf86CrtcRotate(xf86CrtcPtr crtc)343 xf86CrtcRotate(xf86CrtcPtr crtc)
344 {
345 ScrnInfoPtr pScrn = crtc->scrn;
346 xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
347 ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
348 PictTransform crtc_to_fb;
349 struct pict_f_transform f_crtc_to_fb, f_fb_to_crtc;
350 xFixed *new_params = NULL;
351 int new_nparams = 0;
352 PictFilterPtr new_filter = NULL;
353 int new_width = 0;
354 int new_height = 0;
355 RRTransformPtr transform = NULL;
356 Bool damage = FALSE;
357
358 if (pScreen->isGPU)
359 return TRUE;
360 if (crtc->transformPresent)
361 transform = &crtc->transform;
362
363 if (!RRTransformCompute(crtc->x, crtc->y,
364 crtc->mode.HDisplay, crtc->mode.VDisplay,
365 crtc->rotation,
366 transform,
367 &crtc_to_fb,
368 &f_crtc_to_fb,
369 &f_fb_to_crtc) &&
370 xf86CrtcFitsScreen(crtc, &f_crtc_to_fb)) {
371 /*
372 * If the untranslated transformation is the identity,
373 * disable the shadow buffer
374 */
375 xf86RotateDestroy(crtc);
376 crtc->transform_in_use = FALSE;
377 free(new_params);
378 new_params = NULL;
379 new_nparams = 0;
380 new_filter = NULL;
381 new_width = 0;
382 new_height = 0;
383 }
384 else {
385 if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput) {
386 xf86RotateDestroy(crtc);
387 }
388 else {
389 /*
390 * these are the size of the shadow pixmap, which
391 * matches the mode, not the pre-rotated copy in the
392 * frame buffer
393 */
394 int width = crtc->mode.HDisplay;
395 int height = crtc->mode.VDisplay;
396 void *shadowData = crtc->rotatedData;
397 PixmapPtr shadow = crtc->rotatedPixmap;
398 int old_width = shadow ? shadow->drawable.width : 0;
399 int old_height = shadow ? shadow->drawable.height : 0;
400
401 /* Allocate memory for rotation */
402 if (old_width != width || old_height != height) {
403 if (shadow || shadowData) {
404 crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
405 crtc->rotatedPixmap = NULL;
406 crtc->rotatedData = NULL;
407 }
408 shadowData = crtc->funcs->shadow_allocate(crtc, width, height);
409 if (!shadowData)
410 goto bail1;
411 crtc->rotatedData = shadowData;
412 /* shadow will be damaged in xf86RotatePrepare */
413 }
414 else {
415 /* mark shadowed area as damaged so it will be repainted */
416 damage = TRUE;
417 }
418
419 if (!xf86_config->rotation_damage) {
420 /* Create damage structure */
421 xf86_config->rotation_damage = DamageCreate(NULL, NULL,
422 DamageReportNone,
423 TRUE, pScreen,
424 pScreen);
425 if (!xf86_config->rotation_damage)
426 goto bail2;
427
428 /* Wrap block handler */
429 if (!xf86_config->BlockHandler) {
430 xf86_config->BlockHandler = pScreen->BlockHandler;
431 pScreen->BlockHandler = xf86RotateBlockHandler;
432 }
433 }
434
435 if (0) {
436 bail2:
437 if (shadow || shadowData) {
438 crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
439 crtc->rotatedPixmap = NULL;
440 crtc->rotatedData = NULL;
441 }
442 bail1:
443 if (old_width && old_height)
444 crtc->rotatedPixmap =
445 crtc->funcs->shadow_create(crtc, NULL, old_width,
446 old_height);
447 return FALSE;
448 }
449 }
450 #ifdef RANDR_12_INTERFACE
451 if (transform) {
452 if (transform->nparams) {
453 new_params = malloc(transform->nparams * sizeof(xFixed));
454 if (new_params) {
455 memcpy(new_params, transform->params,
456 transform->nparams * sizeof(xFixed));
457 new_nparams = transform->nparams;
458 new_filter = transform->filter;
459 }
460 }
461 else
462 new_filter = transform->filter;
463 if (new_filter) {
464 new_width = new_filter->width;
465 new_height = new_filter->height;
466 }
467 }
468 #endif
469 crtc->transform_in_use = TRUE;
470 }
471 crtc->crtc_to_framebuffer = crtc_to_fb;
472 crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
473 crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
474 free(crtc->params);
475 crtc->params = new_params;
476 crtc->nparams = new_nparams;
477 crtc->filter = new_filter;
478 crtc->filter_width = new_width;
479 crtc->filter_height = new_height;
480 crtc->bounds.x1 = 0;
481 crtc->bounds.x2 = crtc->mode.HDisplay;
482 crtc->bounds.y1 = 0;
483 crtc->bounds.y2 = crtc->mode.VDisplay;
484 pixman_f_transform_bounds(&f_crtc_to_fb, &crtc->bounds);
485
486 if (damage)
487 xf86CrtcDamageShadow(crtc);
488 else if (crtc->rotatedData && !crtc->rotatedPixmap)
489 /* Make sure the new rotate buffer has valid transformed contents */
490 xf86RotateRedisplay(pScreen);
491
492 /* All done */
493 return TRUE;
494 }
495