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