xf86Rotate.c revision 25da500f
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
42static void
43xf86RotateCrtcRedisplay(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
108static void
109xf86CrtcDamageShadow(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
141static void
142xf86RotatePrepare(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
171static Bool
172xf86RotateRedisplay(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)
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
219static void
220xf86RotateBlockHandler(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
243void
244xf86RotateDestroy(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
276void
277xf86RotateFreeShadow(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
294void
295xf86RotateCloseScreen(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
307static Bool
308xf86CrtcFitsScreen(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
342Bool
343xf86CrtcRotate(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
489    /* All done */
490    return TRUE;
491}
492