xf86Rotate.c 16 KB
Newer Older
1 2
/*
 * Copyright © 2006 Keith Packard
3
 * Copyright © 2011 Aaron Plattner
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <stddef.h>
#include <string.h>
#include <stdio.h>

#include "xf86.h"
#include "xf86DDC.h"
#include "windowstr.h"
#include "xf86Crtc.h"
#include "xf86Modes.h"
#include "xf86RandR12.h"
#include "X11/extensions/render.h"
Peter Hutterer's avatar
Peter Hutterer committed
39
#include "X11/extensions/dpmsconst.h"
40 41 42
#include "X11/Xatom.h"

static void
43
xf86RotateCrtcRedisplay(xf86CrtcPtr crtc, RegionPtr region)
44
{
45 46 47 48
    ScrnInfoPtr scrn = crtc->scrn;
    ScreenPtr screen = scrn->pScreen;
    WindowPtr root = screen->root;
    PixmapPtr dst_pixmap = crtc->rotatedPixmap;
49
    PictFormatPtr format = PictureWindowFormat(screen->root);
50 51 52 53 54
    int error;
    PicturePtr src, dst;
    int n = RegionNumRects(region);
    BoxPtr b = RegionRects(region);
    XID include_inferiors = IncludeInferiors;
55

56
    if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput)
57 58 59 60 61 62 63
        return;

    src = CreatePicture(None,
                        &root->drawable,
                        format,
                        CPSubwindowMode,
                        &include_inferiors, serverClient, &error);
64
    if (!src)
65 66 67 68 69
        return;

    dst = CreatePicture(None,
                        &dst_pixmap->drawable,
                        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 78 79 80 81 82 83 84
        SetPicturePictFilter(src, crtc->filter, crtc->params, crtc->nparams);

    if (crtc->shadowClear) {
        CompositePicture(PictOpSrc,
                         src, NULL, dst,
                         0, 0, 0, 0, 0, 0,
                         crtc->mode.HDisplay, crtc->mode.VDisplay);
        crtc->shadowClear = FALSE;
85
    }
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    else {
        while (n--) {
            BoxRec dst_box;

            dst_box = *b;
            dst_box.x1 -= crtc->filter_width >> 1;
            dst_box.x2 += crtc->filter_width >> 1;
            dst_box.y1 -= crtc->filter_height >> 1;
            dst_box.y2 += crtc->filter_height >> 1;
            pixman_f_transform_bounds(&crtc->f_framebuffer_to_crtc, &dst_box);
            CompositePicture(PictOpSrc,
                             src, NULL, dst,
                             dst_box.x1, dst_box.y1, 0, 0, dst_box.x1,
                             dst_box.y1, dst_box.x2 - dst_box.x1,
                             dst_box.y2 - dst_box.y1);
            b++;
        }
103
    }
104 105
    FreePicture(src, None);
    FreePicture(dst, None);
106 107
}

108
static void
109
xf86CrtcDamageShadow(xf86CrtcPtr crtc)
110
{
111 112 113
    ScrnInfoPtr pScrn = crtc->scrn;
    BoxRec damage_box;
    RegionRec damage_region;
114
    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
115 116 117 118 119

    damage_box.x1 = 0;
    damage_box.x2 = crtc->mode.HDisplay;
    damage_box.y1 = 0;
    damage_box.y2 = crtc->mode.VDisplay;
120 121 122 123 124
    if (!pixman_transform_bounds(&crtc->crtc_to_framebuffer, &damage_box)) {
        damage_box.x1 = 0;
        damage_box.y1 = 0;
        damage_box.x2 = pScreen->width;
        damage_box.y2 = pScreen->height;
125
    }
126 127 128 129 130 131 132 133
    if (damage_box.x1 < 0)
        damage_box.x1 = 0;
    if (damage_box.y1 < 0)
        damage_box.y1 = 0;
    if (damage_box.x2 > pScreen->width)
        damage_box.x2 = pScreen->width;
    if (damage_box.y2 > pScreen->height)
        damage_box.y2 = pScreen->height;
134
    RegionInit(&damage_region, &damage_box, 1);
135 136
    DamageDamageRegion(&(*pScreen->GetScreenPixmap) (pScreen)->drawable,
                       &damage_region);
137
    RegionUninit(&damage_region);
138
    crtc->shadowClear = TRUE;
139 140
}

141
static void
142
xf86RotatePrepare(ScreenPtr pScreen)
143
{
144
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
145 146
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    int c;
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
    for (c = 0; c < xf86_config->num_crtc; c++) {
        xf86CrtcPtr crtc = xf86_config->crtc[c];

        if (crtc->rotatedData && !crtc->rotatedPixmap) {
            crtc->rotatedPixmap = crtc->funcs->shadow_create(crtc,
                                                             crtc->rotatedData,
                                                             crtc->mode.
                                                             HDisplay,
                                                             crtc->mode.
                                                             VDisplay);
            if (!xf86_config->rotation_damage_registered) {
                /* Hook damage to screen pixmap */
                DamageRegister(&pScreen->root->drawable,
                               xf86_config->rotation_damage);
                xf86_config->rotation_damage_registered = TRUE;
                EnableLimitedSchedulingLatency();
            }

            xf86CrtcDamageShadow(crtc);
        }
168 169 170
    }
}

171
static Bool
172 173
xf86RotateRedisplay(ScreenPtr pScreen)
{
174
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
175 176 177
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    DamagePtr damage = xf86_config->rotation_damage;
    RegionPtr region;
178 179

    if (!damage)
180 181
        return FALSE;
    xf86RotatePrepare(pScreen);
182
    region = DamageRegion(damage);
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    if (RegionNotEmpty(region)) {
        int c;
        SourceValidateProcPtr SourceValidate;

        /*
         * SourceValidate is used by the software cursor code
         * to pull the cursor off of the screen when reading
         * bits from the frame buffer. Bypassing this function
         * leaves the software cursor in place
         */
        SourceValidate = pScreen->SourceValidate;
        pScreen->SourceValidate = NULL;

        for (c = 0; c < xf86_config->num_crtc; c++) {
            xf86CrtcPtr crtc = xf86_config->crtc[c];

            if (crtc->transform_in_use && crtc->enabled) {
                RegionRec crtc_damage;

                /* compute portion of damage that overlaps crtc */
                RegionInit(&crtc_damage, &crtc->bounds, 1);
                RegionIntersect(&crtc_damage, &crtc_damage, region);

                /* update damaged region */
                if (RegionNotEmpty(&crtc_damage))
                    xf86RotateCrtcRedisplay(crtc, &crtc_damage);

                RegionUninit(&crtc_damage);
            }
        }
        pScreen->SourceValidate = SourceValidate;
        DamageEmpty(damage);
215
    }
216
    return TRUE;
217 218 219
}

static void
220
xf86RotateBlockHandler(ScreenPtr pScreen, void *pTimeout)
221
{
222
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
223
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
224

225 226 227 228
    /* Unwrap before redisplay in case the software
     * cursor layer wants to add its block handler to the
     * chain
     */
229
    pScreen->BlockHandler = xf86_config->BlockHandler;
230 231 232

    xf86RotateRedisplay(pScreen);

233
    (*pScreen->BlockHandler) (pScreen, pTimeout);
234 235 236 237 238 239 240

    /* Re-wrap if we still need this hook */
    if (xf86_config->rotation_damage != NULL) {
        xf86_config->BlockHandler = pScreen->BlockHandler;
        pScreen->BlockHandler = xf86RotateBlockHandler;
    } else
        xf86_config->BlockHandler = NULL;
241 242
}

243
void
244
xf86RotateDestroy(xf86CrtcPtr crtc)
245
{
246 247 248 249
    ScrnInfoPtr pScrn = crtc->scrn;
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
    int c;

250
    /* Free memory from rotation */
251 252 253 254 255
    if (crtc->rotatedPixmap || crtc->rotatedData) {
        crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
                                    crtc->rotatedData);
        crtc->rotatedPixmap = NULL;
        crtc->rotatedData = NULL;
256 257 258
    }

    for (c = 0; c < xf86_config->num_crtc; c++)
259 260
        if (xf86_config->crtc[c]->rotatedData)
            return;
261

262 263 264
    /*
     * Clean up damage structures when no crtcs are rotated
     */
265
    if (xf86_config->rotation_damage) {
266 267 268 269 270
        /* Free damage structure */
        if (xf86_config->rotation_damage_registered) {
            xf86_config->rotation_damage_registered = FALSE;
            DisableLimitedSchedulingLatency();
        }
271
        DamageDestroy(xf86_config->rotation_damage);
272
        xf86_config->rotation_damage = NULL;
273 274 275
    }
}

276
void
277 278 279 280 281
xf86RotateFreeShadow(ScrnInfoPtr pScrn)
{
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
    int c;

282 283
    for (c = 0; c < config->num_crtc; c++) {
        xf86CrtcPtr crtc = config->crtc[c];
284

285 286 287 288 289 290 291
        if (crtc->rotatedPixmap || crtc->rotatedData) {
            crtc->funcs->shadow_destroy(crtc, crtc->rotatedPixmap,
                                        crtc->rotatedData);
            crtc->rotatedPixmap = NULL;
            crtc->rotatedData = NULL;
        }
    }
292 293
}

294
void
295
xf86RotateCloseScreen(ScreenPtr screen)
296
{
297
    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
298 299
    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
    int c;
300

301 302
    /* This has already been destroyed when the root window was destroyed */
    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
308
xf86CrtcFitsScreen(xf86CrtcPtr crtc, struct pict_f_transform *crtc_to_fb)
309
{
310 311
    ScrnInfoPtr pScrn = crtc->scrn;
    BoxRec b;
312

313 314 315
    /* When called before PreInit, the driver is
     * presumably doing load detect
     */
316 317 318 319 320 321
    if (pScrn->is_gpu) {
	ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
	if (pScreen->current_master)
	    pScrn = xf86ScreenToScrn(pScreen->current_master);
    }

322
    if (pScrn->virtualX == 0 || pScrn->virtualY == 0)
323
        return TRUE;
324

325 326 327 328 329
    b.x1 = 0;
    b.y1 = 0;
    b.x2 = crtc->mode.HDisplay;
    b.y2 = crtc->mode.VDisplay;
    if (crtc_to_fb)
330
        pixman_f_transform_bounds(crtc_to_fb, &b);
331
    else {
332 333 334 335
        b.x1 += crtc->x;
        b.y1 += crtc->y;
        b.x2 += crtc->x;
        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
343
xf86CrtcRotate(xf86CrtcPtr crtc)
344
{
345 346
    ScrnInfoPtr pScrn = crtc->scrn;
    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 351 352 353 354 355 356
    xFixed *new_params = NULL;
    int new_nparams = 0;
    PictFilterPtr new_filter = NULL;
    int new_width = 0;
    int new_height = 0;
    RRTransformPtr transform = NULL;
    Bool damage = FALSE;
357

358 359
    if (pScreen->isGPU)
        return TRUE;
360
    if (crtc->transformPresent)
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
        transform = &crtc->transform;

    if (!RRTransformCompute(crtc->x, crtc->y,
                            crtc->mode.HDisplay, crtc->mode.VDisplay,
                            crtc->rotation,
                            transform,
                            &crtc_to_fb,
                            &f_crtc_to_fb,
                            &f_fb_to_crtc) &&
        xf86CrtcFitsScreen(crtc, &f_crtc_to_fb)) {
        /*
         * If the untranslated transformation is the identity,
         * disable the shadow buffer
         */
        xf86RotateDestroy(crtc);
        crtc->transform_in_use = FALSE;
        free(new_params);
        new_params = NULL;
        new_nparams = 0;
        new_filter = NULL;
        new_width = 0;
        new_height = 0;
383
    }
384
    else {
385
        if (crtc->driverIsPerformingTransform & XF86DriverTransformOutput) {
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
            xf86RotateDestroy(crtc);
        }
        else {
            /*
             * these are the size of the shadow pixmap, which
             * matches the mode, not the pre-rotated copy in the
             * frame buffer
             */
            int width = crtc->mode.HDisplay;
            int height = crtc->mode.VDisplay;
            void *shadowData = crtc->rotatedData;
            PixmapPtr shadow = crtc->rotatedPixmap;
            int old_width = shadow ? shadow->drawable.width : 0;
            int old_height = shadow ? shadow->drawable.height : 0;

            /* Allocate memory for rotation */
            if (old_width != width || old_height != height) {
                if (shadow || shadowData) {
                    crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
                    crtc->rotatedPixmap = NULL;
                    crtc->rotatedData = NULL;
                }
                shadowData = crtc->funcs->shadow_allocate(crtc, width, height);
                if (!shadowData)
                    goto bail1;
                crtc->rotatedData = shadowData;
                /* shadow will be damaged in xf86RotatePrepare */
            }
            else {
                /* mark shadowed area as damaged so it will be repainted */
                damage = TRUE;
            }

            if (!xf86_config->rotation_damage) {
                /* Create damage structure */
                xf86_config->rotation_damage = DamageCreate(NULL, NULL,
                                                            DamageReportNone,
                                                            TRUE, pScreen,
                                                            pScreen);
                if (!xf86_config->rotation_damage)
                    goto bail2;

                /* Wrap block handler */
                if (!xf86_config->BlockHandler) {
                    xf86_config->BlockHandler = pScreen->BlockHandler;
                    pScreen->BlockHandler = xf86RotateBlockHandler;
                }
            }

            if (0) {
 bail2:
                if (shadow || shadowData) {
                    crtc->funcs->shadow_destroy(crtc, shadow, shadowData);
                    crtc->rotatedPixmap = NULL;
                    crtc->rotatedData = NULL;
                }
 bail1:
                if (old_width && old_height)
                    crtc->rotatedPixmap =
                        crtc->funcs->shadow_create(crtc, NULL, old_width,
                                                   old_height);
                return FALSE;
            }
        }
450
#ifdef RANDR_12_INTERFACE
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
        if (transform) {
            if (transform->nparams) {
                new_params = malloc(transform->nparams * sizeof(xFixed));
                if (new_params) {
                    memcpy(new_params, transform->params,
                           transform->nparams * sizeof(xFixed));
                    new_nparams = transform->nparams;
                    new_filter = transform->filter;
                }
            }
            else
                new_filter = transform->filter;
            if (new_filter) {
                new_width = new_filter->width;
                new_height = new_filter->height;
            }
        }
468
#endif
469
        crtc->transform_in_use = TRUE;
470
    }
471
    crtc->crtc_to_framebuffer = crtc_to_fb;
472 473
    crtc->f_crtc_to_framebuffer = f_crtc_to_fb;
    crtc->f_framebuffer_to_crtc = f_fb_to_crtc;
474
    free(crtc->params);
475 476 477 478 479 480 481 482 483
    crtc->params = new_params;
    crtc->nparams = new_nparams;
    crtc->filter = new_filter;
    crtc->filter_width = new_width;
    crtc->filter_height = new_height;
    crtc->bounds.x1 = 0;
    crtc->bounds.x2 = crtc->mode.HDisplay;
    crtc->bounds.y1 = 0;
    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 490 491
    /* All done */
    return TRUE;
}