xf86RandR12.c 64.8 KB
Newer Older
1
/*
2
 * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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>
25 26 27 28
#else
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
29 30 31 32 33
#endif

#include "xf86.h"
#include "os.h"
#include "globals.h"
34
#include "xf86Modes.h"
35 36 37 38
#include "xf86Priv.h"
#include "xf86DDC.h"
#include "mipointer.h"
#include "windowstr.h"
39
#include "inputstr.h"
40 41 42
#include <randrstr.h>
#include <X11/extensions/render.h>

43
#include "xf86cmap.h"
44 45 46 47
#include "xf86Crtc.h"
#include "xf86RandR12.h"

typedef struct _xf86RandR12Info {
48 49 50 51 52 53 54 55 56 57
    int virtualX;
    int virtualY;
    int mmWidth;
    int mmHeight;
    int maxX;
    int maxY;
    int pointerX;
    int pointerY;
    Rotation rotation;          /* current mode */
    Rotation supported_rotations;       /* driver supported */
58

59 60 61 62 63 64 65
    /* Compatibility with colormaps and XF86VidMode's gamma */
    int palette_red_size;
    int palette_green_size;
    int palette_blue_size;
    int palette_size;
    LOCO *palette;

66 67 68 69 70 71
    /* Used to wrap EnterVT so we can re-probe the outputs when a laptop unsuspends
     * (actually, any time that we switch back into our VT).
     *
     * See https://bugs.freedesktop.org/show_bug.cgi?id=21554
     */
    xf86EnterVTProc *orig_EnterVT;
72 73 74

    Bool                         panning;
    ConstrainCursorHarderProcPtr orig_ConstrainCursorHarder;
75 76 77
} XF86RandRInfoRec, *XF86RandRInfoPtr;

#ifdef RANDR_12_INTERFACE
78 79
static Bool xf86RandR12Init12(ScreenPtr pScreen);
static Bool xf86RandR12CreateScreenResources12(ScreenPtr pScreen);
80 81
#endif

82
static int xf86RandR12Generation;
83

84
static DevPrivateKeyRec xf86RandR12KeyRec;
85
static DevPrivateKey xf86RandR12Key;
86

87 88
#define XF86RANDRINFO(p) ((XF86RandRInfoPtr) \
    dixLookupPrivate(&(p)->devPrivates, xf86RandR12Key))
89 90

static int
91
xf86RandR12ModeRefresh(DisplayModePtr mode)
92 93
{
    if (mode->VRefresh)
94
        return (int) (mode->VRefresh + 0.5);
95
    else
96
        return (int) (mode->Clock * 1000.0 / mode->HTotal / mode->VTotal + 0.5);
97 98
}

99
/* Adapt panning area; return TRUE if panning area was valid without adaption */
Matthias Hopf's avatar
Matthias Hopf committed
100
static int
101 102
xf86RandR13VerifyPanningArea(xf86CrtcPtr crtc, int screenWidth,
                             int screenHeight)
Matthias Hopf's avatar
Matthias Hopf committed
103
{
104 105
    int ret = TRUE;

Matthias Hopf's avatar
Matthias Hopf committed
106
    if (crtc->version < 2)
107
        return FALSE;
Matthias Hopf's avatar
Matthias Hopf committed
108

109
    if (crtc->panningTotalArea.x2 <= crtc->panningTotalArea.x1) {
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
        /* Panning in X is disabled */
        if (crtc->panningTotalArea.x1 || crtc->panningTotalArea.x2)
            /* Illegal configuration -> fail/disable */
            ret = FALSE;
        crtc->panningTotalArea.x1 = crtc->panningTotalArea.x2 = 0;
        crtc->panningTrackingArea.x1 = crtc->panningTrackingArea.x2 = 0;
        crtc->panningBorder[0] = crtc->panningBorder[2] = 0;
    }
    else {
        /* Panning in X is enabled */
        if (crtc->panningTotalArea.x1 < 0) {
            /* Panning region outside screen -> move inside */
            crtc->panningTotalArea.x2 -= crtc->panningTotalArea.x1;
            crtc->panningTotalArea.x1 = 0;
            ret = FALSE;
        }
        if (crtc->panningTotalArea.x2 <
            crtc->panningTotalArea.x1 + crtc->mode.HDisplay) {
            /* Panning region smaller than displayed area -> crop to displayed area */
            crtc->panningTotalArea.x2 =
                crtc->panningTotalArea.x1 + crtc->mode.HDisplay;
            ret = FALSE;
        }
        if (crtc->panningTotalArea.x2 > screenWidth) {
            /* Panning region larger than screen -> move inside, then crop to screen */
            crtc->panningTotalArea.x1 -=
                crtc->panningTotalArea.x2 - screenWidth;
            crtc->panningTotalArea.x2 = screenWidth;
            ret = FALSE;
            if (crtc->panningTotalArea.x1 < 0)
                crtc->panningTotalArea.x1 = 0;
        }
        if (crtc->panningBorder[0] + crtc->panningBorder[2] >
            crtc->mode.HDisplay) {
            /* Borders too large -> set to 0 */
            crtc->panningBorder[0] = crtc->panningBorder[2] = 0;
            ret = FALSE;
        }
Matthias Hopf's avatar
Matthias Hopf committed
148 149
    }

150
    if (crtc->panningTotalArea.y2 <= crtc->panningTotalArea.y1) {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        /* Panning in Y is disabled */
        if (crtc->panningTotalArea.y1 || crtc->panningTotalArea.y2)
            /* Illegal configuration -> fail/disable */
            ret = FALSE;
        crtc->panningTotalArea.y1 = crtc->panningTotalArea.y2 = 0;
        crtc->panningTrackingArea.y1 = crtc->panningTrackingArea.y2 = 0;
        crtc->panningBorder[1] = crtc->panningBorder[3] = 0;
    }
    else {
        /* Panning in Y is enabled */
        if (crtc->panningTotalArea.y1 < 0) {
            /* Panning region outside screen -> move inside */
            crtc->panningTotalArea.y2 -= crtc->panningTotalArea.y1;
            crtc->panningTotalArea.y1 = 0;
            ret = FALSE;
        }
        if (crtc->panningTotalArea.y2 <
            crtc->panningTotalArea.y1 + crtc->mode.VDisplay) {
            /* Panning region smaller than displayed area -> crop to displayed area */
            crtc->panningTotalArea.y2 =
                crtc->panningTotalArea.y1 + crtc->mode.VDisplay;
            ret = FALSE;
        }
        if (crtc->panningTotalArea.y2 > screenHeight) {
            /* Panning region larger than screen -> move inside, then crop to screen */
            crtc->panningTotalArea.y1 -=
                crtc->panningTotalArea.y2 - screenHeight;
            crtc->panningTotalArea.y2 = screenHeight;
            ret = FALSE;
            if (crtc->panningTotalArea.y1 < 0)
                crtc->panningTotalArea.y1 = 0;
        }
        if (crtc->panningBorder[1] + crtc->panningBorder[3] >
            crtc->mode.VDisplay) {
            /* Borders too large -> set to 0 */
            crtc->panningBorder[1] = crtc->panningBorder[3] = 0;
            ret = FALSE;
        }
Matthias Hopf's avatar
Matthias Hopf committed
189
    }
190 191

    return ret;
Matthias Hopf's avatar
Matthias Hopf committed
192 193
}

194 195 196 197 198 199 200 201 202 203 204
/*
 * The heart of the panning operation:
 *
 * Given a frame buffer position (fb_x, fb_y),
 * and a crtc position (crtc_x, crtc_y),
 * and a transform matrix which maps frame buffer to crtc,
 * compute a panning position (pan_x, pan_y) that
 * makes the resulting transform line those two up
 */

static void
205 206 207 208 209
xf86ComputeCrtcPan(Bool transform_in_use,
                   struct pixman_f_transform *m,
                   double screen_x, double screen_y,
                   double crtc_x, double crtc_y,
                   int old_pan_x, int old_pan_y, int *new_pan_x, int *new_pan_y)
210
{
211
    if (transform_in_use) {
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
        /*
         * Given the current transform, M, the current position
         * on the Screen, S, and the desired position on the CRTC,
         * C, compute a translation, T, such that:
         *
         * M T S = C
         *
         * where T is of the form
         *
         * | 1 0 dx |
         * | 0 1 dy |
         * | 0 0 1  |
         *
         * M T S =
         *   | M00 Sx + M01 Sy + M00 dx + M01 dy + M02 |   | Cx F |
         *   | M10 Sx + M11 Sy + M10 dx + M11 dy + M12 | = | Cy F |
         *   | M20 Sx + M21 Sy + M20 dx + M21 dy + M22 |   |  F   |
         *
         * R = M S
         *
         *   Cx F = M00 dx + M01 dy + R0
         *   Cy F = M10 dx + M11 dy + R1
         *      F = M20 dx + M21 dy + R2
         *
         * Zero out dx, then dy
         *
         * F (Cx M10 - Cy M00) =
         *          (M10 M01 - M00 M11) dy + M10 R0 - M00 R1
         * F (M10 - Cy M20) =
         *          (M10 M21 - M20 M11) dy + M10 R2 - M20 R1
         *
         * F (Cx M11 - Cy M01) =
         *          (M11 M00 - M01 M10) dx + M11 R0 - M01 R1
         * F (M11 - Cy M21) =
         *          (M11 M20 - M21 M10) dx + M11 R2 - M21 R1
         *
         * Make some temporaries
         *
         * T = | Cx M10 - Cy M00 |
         *     | Cx M11 - Cy M01 |
         *
         * U = | M10 M01 - M00 M11 |
         *     | M11 M00 - M01 M10 |
         *
         * Q = | M10 R0 - M00 R1 |
         *     | M11 R0 - M01 R1 |
         *
         * P = | M10 - Cy M20 |
         *     | M11 - Cy M21 |
         *
         * W = | M10 M21 - M20 M11 |
         *     | M11 M20 - M21 M10 |
         *
         * V = | M10 R2 - M20 R1 |
         *         | M11 R2 - M21 R1 |
         *
         * Rewrite:
         *
         * F T0 = U0 dy + Q0
         * F P0 = W0 dy + V0
         * F T1 = U1 dx + Q1
         * F P1 = W1 dx + V1
         *
         * Solve for F (two ways)
         *
         * F (W0 T0 - U0 P0)  = W0 Q0 - U0 V0
         *
         *     W0 Q0 - U0 V0
         * F = -------------
         *     W0 T0 - U0 P0
         *
         * F (W1 T1 - U1 P1) = W1 Q1 - U1 V1
         *
         *     W1 Q1 - U1 V1
         * F = -------------
         *     W1 T1 - U1 P1
         *
         * We'll use which ever solution works (denominator != 0)
         *
         * Finally, solve for dx and dy:
         *
         * dx = (F T1 - Q1) / U1
         * dx = (F P1 - V1) / W1
         *
         * dy = (F T0 - Q0) / U0
         * dy = (F P0 - V0) / W0
         */
        double r[3];
        double q[2], u[2], t[2], v[2], w[2], p[2];
        double f;
        struct pict_f_vector d;
        int i;

        /* Get the un-normalized crtc coordinates again */
        for (i = 0; i < 3; i++)
            r[i] = m->m[i][0] * screen_x + m->m[i][1] * screen_y + m->m[i][2];

        /* Combine values into temporaries */
        for (i = 0; i < 2; i++) {
            q[i] = m->m[1][i] * r[0] - m->m[0][i] * r[1];
            u[i] = m->m[1][i] * m->m[0][1 - i] - m->m[0][i] * m->m[1][1 - i];
            t[i] = m->m[1][i] * crtc_x - m->m[0][i] * crtc_y;

            v[i] = m->m[1][i] * r[2] - m->m[2][i] * r[1];
            w[i] = m->m[1][i] * m->m[2][1 - i] - m->m[2][i] * m->m[1][1 - i];
            p[i] = m->m[1][i] - m->m[2][i] * crtc_y;
        }

        /* Find a way to compute f */
        f = 0;
        for (i = 0; i < 2; i++) {
            double a = w[i] * q[i] - u[i] * v[i];
            double b = w[i] * t[i] - u[i] * p[i];

            if (b != 0) {
                f = a / b;
                break;
            }
        }

        /* Solve for the resulting transform vector */
        for (i = 0; i < 2; i++) {
            if (u[i])
                d.v[1 - i] = (t[i] * f - q[i]) / u[i];
            else if (w[1])
                d.v[1 - i] = (p[i] * f - v[i]) / w[i];
            else
                d.v[1 - i] = 0;
        }
        *new_pan_x = old_pan_x - floor(d.v[0] + 0.5);
        *new_pan_y = old_pan_y - floor(d.v[1] + 0.5);
    }
    else {
        *new_pan_x = screen_x - crtc_x;
        *new_pan_y = screen_y - crtc_y;
347 348 349
    }
}

Matthias Hopf's avatar
Matthias Hopf committed
350
static void
351
xf86RandR13Pan(xf86CrtcPtr crtc, int x, int y)
Matthias Hopf's avatar
Matthias Hopf committed
352 353 354
{
    int newX, newY;
    int width, height;
355
    Bool panned = FALSE;
Matthias Hopf's avatar
Matthias Hopf committed
356 357

    if (crtc->version < 2)
358
        return;
Matthias Hopf's avatar
Matthias Hopf committed
359

360 361 362 363
    if (!crtc->enabled ||
        (crtc->panningTotalArea.x2 <= crtc->panningTotalArea.x1 &&
         crtc->panningTotalArea.y2 <= crtc->panningTotalArea.y1))
        return;
Matthias Hopf's avatar
Matthias Hopf committed
364

365 366 367
    newX = crtc->x;
    newY = crtc->y;
    width = crtc->mode.HDisplay;
Matthias Hopf's avatar
Matthias Hopf committed
368 369
    height = crtc->mode.VDisplay;

370
    if ((crtc->panningTrackingArea.x2 <= crtc->panningTrackingArea.x1 ||
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 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
         (x >= crtc->panningTrackingArea.x1 &&
          x < crtc->panningTrackingArea.x2)) &&
        (crtc->panningTrackingArea.y2 <= crtc->panningTrackingArea.y1 ||
         (y >= crtc->panningTrackingArea.y1 &&
          y < crtc->panningTrackingArea.y2))) {
        struct pict_f_vector c;

        /*
         * Pre-clip the mouse position to the panning area so that we don't
         * push the crtc outside. This doesn't deal with changes to the
         * panning values, only mouse position changes.
         */
        if (crtc->panningTotalArea.x2 > crtc->panningTotalArea.x1) {
            if (x < crtc->panningTotalArea.x1)
                x = crtc->panningTotalArea.x1;
            if (x >= crtc->panningTotalArea.x2)
                x = crtc->panningTotalArea.x2 - 1;
        }
        if (crtc->panningTotalArea.y2 > crtc->panningTotalArea.y1) {
            if (y < crtc->panningTotalArea.y1)
                y = crtc->panningTotalArea.y1;
            if (y >= crtc->panningTotalArea.y2)
                y = crtc->panningTotalArea.y2 - 1;
        }

        c.v[0] = x;
        c.v[1] = y;
        c.v[2] = 1.0;
        if (crtc->transform_in_use) {
            pixman_f_transform_point(&crtc->f_framebuffer_to_crtc, &c);
        }
        else {
            c.v[0] -= crtc->x;
            c.v[1] -= crtc->y;
        }

        if (crtc->panningTotalArea.x2 > crtc->panningTotalArea.x1) {
            if (c.v[0] < crtc->panningBorder[0]) {
                c.v[0] = crtc->panningBorder[0];
                panned = TRUE;
            }
            if (c.v[0] >= width - crtc->panningBorder[2]) {
                c.v[0] = width - crtc->panningBorder[2] - 1;
                panned = TRUE;
            }
        }
        if (crtc->panningTotalArea.y2 > crtc->panningTotalArea.y1) {
            if (c.v[1] < crtc->panningBorder[1]) {
                c.v[1] = crtc->panningBorder[1];
                panned = TRUE;
            }
            if (c.v[1] >= height - crtc->panningBorder[3]) {
                c.v[1] = height - crtc->panningBorder[3] - 1;
                panned = TRUE;
            }
        }
        if (panned)
            xf86ComputeCrtcPan(crtc->transform_in_use,
                               &crtc->f_framebuffer_to_crtc,
                               x, y, c.v[0], c.v[1], newX, newY, &newX, &newY);
Matthias Hopf's avatar
Matthias Hopf committed
431
    }
432 433 434 435 436 437 438

    /*
     * Ensure that the crtc is within the panning region.
     *
     * XXX This computation only works when we do not have a transform
     * in use.
     */
439 440 441 442 443 444 445 446 447 448 449 450 451 452
    if (!crtc->transform_in_use) {
        /* Validate against [xy]1 after [xy]2, to be sure that results are > 0 for [xy]1 > 0 */
        if (crtc->panningTotalArea.x2 > crtc->panningTotalArea.x1) {
            if (newX > crtc->panningTotalArea.x2 - width)
                newX = crtc->panningTotalArea.x2 - width;
            if (newX < crtc->panningTotalArea.x1)
                newX = crtc->panningTotalArea.x1;
        }
        if (crtc->panningTotalArea.y2 > crtc->panningTotalArea.y1) {
            if (newY > crtc->panningTotalArea.y2 - height)
                newY = crtc->panningTotalArea.y2 - height;
            if (newY < crtc->panningTotalArea.y1)
                newY = crtc->panningTotalArea.y1;
        }
453
    }
Matthias Hopf's avatar
Matthias Hopf committed
454
    if (newX != crtc->x || newY != crtc->y)
455
        xf86CrtcSetOrigin(crtc, newX, newY);
Matthias Hopf's avatar
Matthias Hopf committed
456 457
}

458
static Bool
459
xf86RandR12GetInfo(ScreenPtr pScreen, Rotation * rotations)
460
{
461
    RRScreenSizePtr pSize;
462
    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
463 464 465
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
    DisplayModePtr mode;
    int maxX = 0, maxY = 0;
466 467 468

    *rotations = randrp->supported_rotations;

469 470 471
    if (randrp->virtualX == -1 || randrp->virtualY == -1) {
        randrp->virtualX = scrp->virtualX;
        randrp->virtualY = scrp->virtualY;
472 473 474
    }

    /* Re-probe the outputs for new monitors or modes */
475 476 477
    if (scrp->vtSema) {
        xf86ProbeOutputModes(scrp, 0, 0);
        xf86SetScrnInfoModes(scrp);
478
    }
479

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
    for (mode = scrp->modes;; mode = mode->next) {
        int refresh = xf86RandR12ModeRefresh(mode);

        if (randrp->maxX == 0 || randrp->maxY == 0) {
            if (maxX < mode->HDisplay)
                maxX = mode->HDisplay;
            if (maxY < mode->VDisplay)
                maxY = mode->VDisplay;
        }
        pSize = RRRegisterSize(pScreen,
                               mode->HDisplay, mode->VDisplay,
                               randrp->mmWidth, randrp->mmHeight);
        if (!pSize)
            return FALSE;
        RRRegisterRate(pScreen, pSize, refresh);

        if (xf86ModesEqual(mode, scrp->currentMode)) {
            RRSetCurrentConfig(pScreen, randrp->rotation, refresh, pSize);
        }
        if (mode->next == scrp->modes)
            break;
501 502
    }

503 504 505
    if (randrp->maxX == 0 || randrp->maxY == 0) {
        randrp->maxX = maxX;
        randrp->maxY = maxY;
506 507 508 509 510 511
    }

    return TRUE;
}

static Bool
512 513 514
xf86RandR12SetMode(ScreenPtr pScreen,
                   DisplayModePtr mode,
                   Bool useVirtual, int mmWidth, int mmHeight)
515
{
516
    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
517 518 519 520 521 522 523 524
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
    int oldWidth = pScreen->width;
    int oldHeight = pScreen->height;
    int oldmmWidth = pScreen->mmWidth;
    int oldmmHeight = pScreen->mmHeight;
    WindowPtr pRoot = pScreen->root;
    DisplayModePtr currentMode = NULL;
    Bool ret = TRUE;
525 526

    if (pRoot)
527
        (*scrp->EnableDisableFBAccess) (scrp, FALSE);
528 529 530
    if (useVirtual) {
        scrp->virtualX = randrp->virtualX;
        scrp->virtualY = randrp->virtualY;
531
    }
532 533 534
    else {
        scrp->virtualX = mode->HDisplay;
        scrp->virtualY = mode->VDisplay;
535 536
    }

537 538 539 540 541 542
    if (randrp->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
        /* If the screen is rotated 90 or 270 degrees, swap the sizes. */
        pScreen->width = scrp->virtualY;
        pScreen->height = scrp->virtualX;
        pScreen->mmWidth = mmHeight;
        pScreen->mmHeight = mmWidth;
543
    }
544 545 546 547 548
    else {
        pScreen->width = scrp->virtualX;
        pScreen->height = scrp->virtualY;
        pScreen->mmWidth = mmWidth;
        pScreen->mmHeight = mmHeight;
549 550 551 552 553 554 555 556 557 558 559
    }
    if (scrp->currentMode == mode) {
        /* Save current mode */
        currentMode = scrp->currentMode;
        /* Reset, just so we ensure the drivers SwitchMode is called */
        scrp->currentMode = NULL;
    }
    /*
     * We know that if the driver failed to SwitchMode to the rotated
     * version, then it should revert back to it's prior mode.
     */
560
    if (!xf86SwitchMode(pScreen, mode)) {
561
        ret = FALSE;
562 563 564 565
        scrp->virtualX = pScreen->width = oldWidth;
        scrp->virtualY = pScreen->height = oldHeight;
        pScreen->mmWidth = oldmmWidth;
        pScreen->mmHeight = oldmmHeight;
566 567 568 569 570 571 572 573 574 575 576
        scrp->currentMode = currentMode;
    }

    /*
     * Make sure the layout is correct
     */
    xf86ReconfigureLayout();

    /*
     * Make sure the whole screen is visible
     */
577 578
    xf86SetViewport(pScreen, pScreen->width, pScreen->height);
    xf86SetViewport(pScreen, 0, 0);
579
    if (pRoot)
580
        (*scrp->EnableDisableFBAccess) (scrp, TRUE);
581 582 583
    return ret;
}

584
Bool
585 586
xf86RandR12SetConfig(ScreenPtr pScreen,
                     Rotation rotation, int rate, RRScreenSizePtr pSize)
587
{
588
    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
589 590 591 592 593 594 595 596
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
    DisplayModePtr mode;
    int pos[MAXDEVICES][2];
    Bool useVirtual = FALSE;
    int maxX = 0, maxY = 0;
    Rotation oldRotation = randrp->rotation;
    DeviceIntPtr dev;
    Bool view_adjusted = FALSE;
597 598 599

    randrp->rotation = rotation;

600 601 602
    if (randrp->virtualX == -1 || randrp->virtualY == -1) {
        randrp->virtualX = scrp->virtualX;
        randrp->virtualY = scrp->virtualY;
603 604
    }

605 606 607
    for (dev = inputInfo.devices; dev; dev = dev->next) {
        if (!IsMaster(dev) && !IsFloating(dev))
            continue;
608

609
        miPointerGetPosition(dev, &pos[dev->id][0], &pos[dev->id][1]);
610 611
    }

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    for (mode = scrp->modes;; mode = mode->next) {
        if (randrp->maxX == 0 || randrp->maxY == 0) {
            if (maxX < mode->HDisplay)
                maxX = mode->HDisplay;
            if (maxY < mode->VDisplay)
                maxY = mode->VDisplay;
        }
        if (mode->HDisplay == pSize->width &&
            mode->VDisplay == pSize->height &&
            (rate == 0 || xf86RandR12ModeRefresh(mode) == rate))
            break;
        if (mode->next == scrp->modes) {
            if (pSize->width == randrp->virtualX &&
                pSize->height == randrp->virtualY) {
                mode = scrp->modes;
                useVirtual = TRUE;
                break;
            }
            if (randrp->maxX == 0 || randrp->maxY == 0) {
                randrp->maxX = maxX;
                randrp->maxY = maxY;
            }
            return FALSE;
        }
636 637
    }

638 639 640
    if (randrp->maxX == 0 || randrp->maxY == 0) {
        randrp->maxX = maxX;
        randrp->maxY = maxY;
641 642
    }

643 644
    if (!xf86RandR12SetMode(pScreen, mode, useVirtual, pSize->mmWidth,
                            pSize->mmHeight)) {
645
        randrp->rotation = oldRotation;
646
        return FALSE;
647 648 649 650
    }

    /*
     * Move the cursor back where it belongs; SwitchMode repositions it
651
     * FIXME: duplicated code, see modes/xf86RandR12.c
652
     */
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
    for (dev = inputInfo.devices; dev; dev = dev->next) {
        if (!IsMaster(dev) && !IsFloating(dev))
            continue;

        if (pScreen == miPointerGetScreen(dev)) {
            int px = pos[dev->id][0];
            int py = pos[dev->id][1];

            px = (px >= pScreen->width ? (pScreen->width - 1) : px);
            py = (py >= pScreen->height ? (pScreen->height - 1) : py);

            /* Setting the viewpoint makes only sense on one device */
            if (!view_adjusted && IsMaster(dev)) {
                xf86SetViewport(pScreen, px, py);
                view_adjusted = TRUE;
            }

            (*pScreen->SetCursorPosition) (dev, pScreen, px, py, FALSE);
        }
672 673 674 675 676
    }

    return TRUE;
}

677 678 679 680
#define PANNING_ENABLED(crtc)                                           \
    ((crtc)->panningTotalArea.x2 > (crtc)->panningTotalArea.x1 ||       \
     (crtc)->panningTotalArea.y2 > (crtc)->panningTotalArea.y1)

681
static Bool
682 683 684
xf86RandR12ScreenSetSize(ScreenPtr pScreen,
                         CARD16 width,
                         CARD16 height, CARD32 mmWidth, CARD32 mmHeight)
685
{
686
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
687
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
688 689 690 691 692
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
    WindowPtr pRoot = pScreen->root;
    PixmapPtr pScrnPix;
    Bool ret = FALSE;
    int c;
693

694 695 696
    if (randrp->virtualX == -1 || randrp->virtualY == -1) {
        randrp->virtualX = pScrn->virtualX;
        randrp->virtualY = pScrn->virtualY;
697
    }
698
    if (pRoot && pScrn->vtSema)
699
        (*pScrn->EnableDisableFBAccess) (pScrn, FALSE);
700

701
    /* Let the driver update virtualX and virtualY */
702 703
    if (!(*config->funcs->resize) (pScrn, width, height))
        goto finish;
704 705

    ret = TRUE;
706 707
    /* Update panning information */
    for (c = 0; c < config->num_crtc; c++) {
708 709
        xf86CrtcPtr crtc = config->crtc[c];

710
	if (PANNING_ENABLED (crtc)) {
711 712 713 714 715 716 717 718 719 720 721
            if (crtc->panningTotalArea.x2 > crtc->panningTrackingArea.x1)
                crtc->panningTotalArea.x2 += width - pScreen->width;
            if (crtc->panningTotalArea.y2 > crtc->panningTrackingArea.y1)
                crtc->panningTotalArea.y2 += height - pScreen->height;
            if (crtc->panningTrackingArea.x2 > crtc->panningTrackingArea.x1)
                crtc->panningTrackingArea.x2 += width - pScreen->width;
            if (crtc->panningTrackingArea.y2 > crtc->panningTrackingArea.y1)
                crtc->panningTrackingArea.y2 += height - pScreen->height;
            xf86RandR13VerifyPanningArea(crtc, width, height);
            xf86RandR13Pan(crtc, randrp->pointerX, randrp->pointerY);
        }
Matthias Hopf's avatar
Matthias Hopf committed
722
    }
723

724
    pScrnPix = (*pScreen->GetScreenPixmap) (pScreen);
725 726
    pScreen->width = pScrnPix->drawable.width = width;
    pScreen->height = pScrnPix->drawable.height = height;
727 728
    randrp->mmWidth = pScreen->mmWidth = mmWidth;
    randrp->mmHeight = pScreen->mmHeight = mmHeight;
729

730 731
    xf86SetViewport(pScreen, pScreen->width - 1, pScreen->height - 1);
    xf86SetViewport(pScreen, 0, 0);
732

733
 finish:
734 735
    update_desktop_dimensions();

736
    if (pRoot && pScrn->vtSema)
737
        (*pScrn->EnableDisableFBAccess) (pScrn, TRUE);
738
#if RANDR_12_INTERFACE
739
    if (pScreen->root && ret)
740
        RRScreenSizeNotify(pScreen);
741 742 743 744
#endif
    return ret;
}

745
Rotation
746 747
xf86RandR12GetRotation(ScreenPtr pScreen)
{
748
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
749 750 751 752

    return randrp->rotation;
}

753
Bool
754
xf86RandR12CreateScreenResources(ScreenPtr pScreen)
755
{
756
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
757 758 759 760 761 762
    xf86CrtcConfigPtr config;
    XF86RandRInfoPtr randrp;
    int c;
    int width, height;
    int mmWidth, mmHeight;

763 764 765
#ifdef PANORAMIX
    /* XXX disable RandR when using Xinerama */
    if (!noPanoramiXExtension)
766
        return TRUE;
767 768
#endif

769 770
    config = XF86_CRTC_CONFIG_PTR(pScrn);
    randrp = XF86RANDRINFO(pScreen);
771 772 773
    /*
     * Compute size of screen
     */
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
    width = 0;
    height = 0;
    for (c = 0; c < config->num_crtc; c++) {
        xf86CrtcPtr crtc = config->crtc[c];
        int crtc_width = crtc->x + xf86ModeWidth(&crtc->mode, crtc->rotation);
        int crtc_height = crtc->y + xf86ModeHeight(&crtc->mode, crtc->rotation);

        if (crtc->enabled) {
            if (crtc_width > width)
                width = crtc_width;
            if (crtc_height > height)
                height = crtc_height;
            if (crtc->panningTotalArea.x2 > width)
                width = crtc->panningTotalArea.x2;
            if (crtc->panningTotalArea.y2 > height)
                height = crtc->panningTotalArea.y2;
        }
791
    }
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832

    if (width && height) {
        /*
         * Compute physical size of screen
         */
        if (monitorResolution) {
            mmWidth = width * 25.4 / monitorResolution;
            mmHeight = height * 25.4 / monitorResolution;
        }
        else {
            xf86OutputPtr output = xf86CompatOutput(pScrn);

            if (output &&
                output->conf_monitor &&
                (output->conf_monitor->mon_width > 0 &&
                 output->conf_monitor->mon_height > 0)) {
                /*
                 * Prefer user configured DisplaySize
                 */
                mmWidth = output->conf_monitor->mon_width;
                mmHeight = output->conf_monitor->mon_height;
            }
            else {
                /*
                 * Otherwise, just set the screen to DEFAULT_DPI
                 */
                mmWidth = width * 25.4 / DEFAULT_DPI;
                mmHeight = height * 25.4 / DEFAULT_DPI;
            }
        }
        xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                   "Setting screen physical size to %d x %d\n",
                   mmWidth, mmHeight);
        /*
         * This is the initial setting of the screen size.
         * We have to pre-set it here, otherwise panning would be adapted
         * to the new screen size.
         */
        pScreen->width = width;
        pScreen->height = height;
        xf86RandR12ScreenSetSize(pScreen, width, height, mmWidth, mmHeight);
833 834
    }

835 836 837
    if (randrp->virtualX == -1 || randrp->virtualY == -1) {
        randrp->virtualX = pScrn->virtualX;
        randrp->virtualY = pScrn->virtualY;
838
    }
839
    xf86CrtcSetScreenSubpixelOrder(pScreen);
840
#if RANDR_12_INTERFACE
841 842
    if (xf86RandR12CreateScreenResources12(pScreen))
        return TRUE;
843 844 845 846
#endif
    return TRUE;
}

847
Bool
848
xf86RandR12Init(ScreenPtr pScreen)
849
{
850 851
    rrScrPrivPtr rp;
    XF86RandRInfoPtr randrp;
852 853 854

#ifdef PANORAMIX
    /* XXX disable RandR when using Xinerama */
855
    if (!noPanoramiXExtension) {
856 857 858 859 860
        if (xf86NumScreens == 1)
            noPanoramiXExtension = TRUE;
        else
            return TRUE;
    }
861
#endif
862

863
    if (xf86RandR12Generation != serverGeneration)
864
        xf86RandR12Generation = serverGeneration;
865

866 867
    xf86RandR12Key = &xf86RandR12KeyRec;
    if (!dixRegisterPrivateKey(&xf86RandR12KeyRec, PRIVATE_SCREEN, 0))
868
        return FALSE;
869

870
    randrp = malloc(sizeof(XF86RandRInfoRec));
871
    if (!randrp)
872
        return FALSE;
873

874 875 876
    if (!RRScreenInit(pScreen)) {
        free(randrp);
        return FALSE;
877 878 879 880 881 882 883 884 885 886
    }
    rp = rrGetScrPriv(pScreen);
    rp->rrGetInfo = xf86RandR12GetInfo;
    rp->rrSetConfig = xf86RandR12SetConfig;

    randrp->virtualX = -1;
    randrp->virtualY = -1;
    randrp->mmWidth = pScreen->mmWidth;
    randrp->mmHeight = pScreen->mmHeight;

887
    randrp->rotation = RR_Rotate_0;     /* initial rotated mode */
888 889 890 891 892

    randrp->supported_rotations = RR_Rotate_0;

    randrp->maxX = randrp->maxY = 0;

893 894 895
    randrp->palette_size = 0;
    randrp->palette = NULL;

896
    dixSetPrivate(&pScreen->devPrivates, xf86RandR12Key, randrp);
897 898

#if RANDR_12_INTERFACE
899 900
    if (!xf86RandR12Init12(pScreen))
        return FALSE;
901 902 903 904
#endif
    return TRUE;
}

905
void
906
xf86RandR12CloseScreen(ScreenPtr pScreen)
907
{
908
    XF86RandRInfoPtr randrp;
909 910

    if (xf86RandR12Key == NULL)
911
        return;
912 913 914

    randrp = XF86RANDRINFO(pScreen);
#if RANDR_12_INTERFACE
915
    xf86ScreenToScrn(pScreen)->EnterVT = randrp->orig_EnterVT;
916
    pScreen->ConstrainCursorHarder = randrp->orig_ConstrainCursorHarder;
917 918
#endif

919
    free(randrp->palette);
920 921 922
    free(randrp);
}

923
void
924
xf86RandR12SetRotations(ScreenPtr pScreen, Rotation rotations)
925
{
926 927
    XF86RandRInfoPtr randrp;

928
#if RANDR_12_INTERFACE
929
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
930 931
    int c;
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
932
#endif
933

934
    if (xf86RandR12Key == NULL)
935
        return;
936

937
    randrp = XF86RANDRINFO(pScreen);
938
#if RANDR_12_INTERFACE
939
    for (c = 0; c < config->num_crtc; c++) {
940
        xf86CrtcPtr crtc = config->crtc[c];
941

942
        RRCrtcSetRotations(crtc->randr_crtc, rotations);
943 944
    }
#endif
945
    randrp->supported_rotations = rotations;
946 947
}

948
void
949
xf86RandR12SetTransformSupport(ScreenPtr pScreen, Bool transforms)
950 951
{
#if RANDR_13_INTERFACE
952
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
953 954
    int c;
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
955 956 957
#endif

    if (xf86RandR12Key == NULL)
958
        return;
959 960 961

#if RANDR_13_INTERFACE
    for (c = 0; c < config->num_crtc; c++) {
962
        xf86CrtcPtr crtc = config->crtc[c];
963

964
        RRCrtcSetTransformSupport(crtc->randr_crtc, transforms);
965 966 967 968
    }
#endif
}

969
void
970 971
xf86RandR12GetOriginalVirtualSize(ScrnInfoPtr pScrn, int *x, int *y)
{
972
    ScreenPtr pScreen = xf86ScrnToScreen(pScrn);
973 974

    if (xf86RandR12Generation != serverGeneration ||
975 976 977 978 979 980 981 982 983
        XF86RANDRINFO(pScreen)->virtualX == -1) {
        *x = pScrn->virtualX;
        *y = pScrn->virtualY;
    }
    else {
        XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);

        *x = randrp->virtualX;
        *y = randrp->virtualY;
984 985 986 987
    }
}

#if RANDR_12_INTERFACE
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004

#define FLAG_BITS (RR_HSyncPositive | \
		   RR_HSyncNegative | \
		   RR_VSyncPositive | \
		   RR_VSyncNegative | \
		   RR_Interlace | \
		   RR_DoubleScan | \
		   RR_CSync | \
		   RR_CSyncPositive | \
		   RR_CSyncNegative | \
		   RR_HSkewPresent | \
		   RR_BCast | \
		   RR_PixelMultiplex | \
		   RR_DoubleClock | \
		   RR_ClockDivideBy2)

static Bool
1005
xf86RandRModeMatches(RRModePtr randr_mode, DisplayModePtr mode)
1006 1007
{
#if 0
1008 1009 1010 1011 1012 1013 1014 1015
    if (match_name) {
        /* check for same name */
        int len = strlen(mode->name);

        if (randr_mode->mode.nameLength != len)
            return FALSE;
        if (memcmp(randr_mode->name, mode->name, len) != 0)
            return FALSE;
1016 1017
    }
#endif
1018

1019
    /* check for same timings */
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040
    if (randr_mode->mode.dotClock / 1000 != mode->Clock)
        return FALSE;
    if (randr_mode->mode.width != mode->HDisplay)
        return FALSE;
    if (randr_mode->mode.hSyncStart != mode->HSyncStart)
        return FALSE;
    if (randr_mode->mode.hSyncEnd != mode->HSyncEnd)
        return FALSE;
    if (randr_mode->mode.hTotal != mode->HTotal)
        return FALSE;
    if (randr_mode->mode.hSkew != mode->HSkew)
        return FALSE;
    if (randr_mode->mode.height != mode->VDisplay)
        return FALSE;
    if (randr_mode->mode.vSyncStart != mode->VSyncStart)
        return FALSE;
    if (randr_mode->mode.vSyncEnd != mode->VSyncEnd)
        return FALSE;
    if (randr_mode->mode.vTotal != mode->VTotal)
        return FALSE;

1041 1042
    /* check for same flags (using only the XF86 valid flag bits) */
    if ((randr_mode->mode.modeFlags & FLAG_BITS) != (mode->Flags & FLAG_BITS))
1043 1044
        return FALSE;

1045 1046 1047 1048
    /* everything matches */
    return TRUE;
}

1049
static Bool
1050
xf86RandR12CrtcNotify(RRCrtcPtr randr_crtc)
1051
{
1052
    ScreenPtr pScreen = randr_crtc->pScreen;
1053
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
    RRModePtr randr_mode = NULL;
    int x;
    int y;
    Rotation rotation;
    int numOutputs;
    RROutputPtr *randr_outputs;
    RROutputPtr randr_output;
    xf86CrtcPtr crtc = randr_crtc->devPrivate;
    xf86OutputPtr output;
    int i, j;
    DisplayModePtr mode = &crtc->mode;
    Bool ret;

1068
    randr_outputs = xallocarray(config->num_output, sizeof(RROutputPtr));
1069
    if (!randr_outputs)
1070
        return FALSE;
1071 1072 1073 1074 1075
    x = crtc->x;
    y = crtc->y;
    rotation = crtc->rotation;
    numOutputs = 0;
    randr_mode = NULL;
1076 1077 1078 1079 1080 1081
    for (i = 0; i < config->num_output; i++) {
        output = config->output[i];
        if (output->crtc == crtc) {
            randr_output = output->randr_output;
            randr_outputs[numOutputs++] = randr_output;
            /*
Peter Hutterer's avatar
Peter Hutterer committed
1082
             * We make copies of modes, so pointer equality
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
             * isn't sufficient
             */
            for (j = 0; j < randr_output->numModes + randr_output->numUserModes;
                 j++) {
                RRModePtr m =
                    (j <
                     randr_output->numModes ? randr_output->
                     modes[j] : randr_output->userModes[j -
                                                        randr_output->
                                                        numModes]);

                if (xf86RandRModeMatches(m, mode)) {
                    randr_mode = m;
                    break;
                }
            }
        }
1100
    }
1101 1102 1103 1104
    ret = RRCrtcNotify(randr_crtc, randr_mode, x, y,
                       rotation,
                       crtc->transformPresent ? &crtc->transform : NULL,
                       numOutputs, randr_outputs);
1105
    free(randr_outputs);
1106 1107 1108
    return ret;
}

1109 1110 1111 1112
/*
 * Convert a RandR mode to a DisplayMode
 */
static void
1113 1114
xf86RandRModeConvert(ScrnInfoPtr scrn,
                     RRModePtr randr_mode, DisplayModePtr mode)
1115
{
1116
    memset(mode, 0, sizeof(DisplayModeRec));
1117 1118 1119
    mode->status = MODE_OK;

    mode->Clock = randr_mode->mode.dotClock / 1000;
1120

1121 1122 1123 1124 1125
    mode->HDisplay = randr_mode->mode.width;
    mode->HSyncStart = randr_mode->mode.hSyncStart;
    mode->HSyncEnd = randr_mode->mode.hSyncEnd;
    mode->HTotal = randr_mode->mode.hTotal;
    mode->HSkew = randr_mode->mode.hSkew;
1126

1127 1128 1129 1130 1131 1132 1133 1134
    mode->VDisplay = randr_mode->mode.height;
    mode->VSyncStart = randr_mode->mode.vSyncStart;
    mode->VSyncEnd = randr_mode->mode.vSyncEnd;
    mode->VTotal = randr_mode->mode.vTotal;
    mode->VScan = 0;

    mode->Flags = randr_mode->mode.modeFlags & FLAG_BITS;

1135
    xf86SetModeCrtc(mode, scrn->adjustFlags);
1136 1137
}

1138
static Bool
1139 1140 1141 1142 1143 1144 1145
xf86RandR12CrtcSet(ScreenPtr pScreen,
                   RRCrtcPtr randr_crtc,
                   RRModePtr randr_mode,
                   int x,
                   int y,
                   Rotation rotation,
                   int num_randr_outputs, RROutputPtr * randr_outputs)
1146
{
1147
    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
1148
    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
1149 1150 1151 1152 1153 1154 1155
    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn);
    xf86CrtcPtr crtc = randr_crtc->devPrivate;
    RRTransformPtr transform;
    Bool changed = FALSE;
    int o, ro;
    xf86CrtcPtr *save_crtcs;
    Bool save_enabled = crtc->enabled;
1156

1157
    if (!crtc->scrn->vtSema)
1158
        return FALSE;
1159

1160
    save_crtcs = xallocarray(config->num_output, sizeof(xf86CrtcPtr));
1161
    if ((randr_mode != NULL) != crtc->enabled)
1162 1163 1164 1165
        changed = TRUE;
    else if (randr_mode && !xf86RandRModeMatches(randr_mode, &crtc->mode))
        changed = TRUE;

1166
    if (rotation != crtc->rotation)
1167
        changed = TRUE;
1168

1169 1170
    if (crtc->current_scanout != randr_crtc->scanout_pixmap ||
        crtc->current_scanout_back != randr_crtc->scanout_pixmap_back)
1171 1172
        changed = TRUE;

1173
    transform = RRCrtcGetTransform(randr_crtc);
1174
    if ((transform != NULL) != crtc->transformPresent)
1175 1176 1177 1178 1179
        changed = TRUE;
    else if (transform &&
             memcmp(&transform->transform, &crtc->transform.transform,
                    sizeof(transform->transform)) != 0)
        changed = TRUE;
1180

1181
    if (x != crtc->x || y != crtc->y)