1/*
2 *
3 * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
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
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Keith Packard not be used in
10 * advertising or publicity pertaining to distribution of the software without
11 * specific, written prior permission.  Keith Packard makes no
12 * representations about the suitability of this software for any purpose.  It
13 * is provided "as is" without express or implied warranty.
14 *
15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL KEITH PACKARD 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
21 * PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_XORG_CONFIG_H
25#include <xorg-config.h>
26#endif
27
28#include <X11/X.h>
29#include "os.h"
30#include "globals.h"
31#include "xf86.h"
32#include "xf86str.h"
33#include "xf86Priv.h"
34#include "xf86DDC.h"
35#include "mipointer.h"
36#include <randrstr.h>
37#include "inputstr.h"
38
39typedef struct _xf86RandRInfo {
40    CloseScreenProcPtr CloseScreen;
41    int virtualX;
42    int virtualY;
43    int mmWidth;
44    int mmHeight;
45    Rotation rotation;
46} XF86RandRInfoRec, *XF86RandRInfoPtr;
47
48static DevPrivateKeyRec xf86RandRKeyRec;
49static DevPrivateKey xf86RandRKey;
50
51#define XF86RANDRINFO(p) ((XF86RandRInfoPtr)dixLookupPrivate(&(p)->devPrivates, xf86RandRKey))
52
53static int
54xf86RandRModeRefresh(DisplayModePtr mode)
55{
56    if (mode->VRefresh)
57        return (int) (mode->VRefresh + 0.5);
58    else if (mode->Clock == 0)
59        return 0;
60    else
61        return (int) (mode->Clock * 1000.0 / mode->HTotal / mode->VTotal + 0.5);
62}
63
64static Bool
65xf86RandRGetInfo(ScreenPtr pScreen, Rotation * rotations)
66{
67    RRScreenSizePtr pSize;
68    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
69    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
70    DisplayModePtr mode;
71    int refresh0 = 60;
72    xorgRRModeMM RRModeMM;
73
74    *rotations = RR_Rotate_0;
75
76    for (mode = scrp->modes; mode != NULL; mode = mode->next) {
77        int refresh = xf86RandRModeRefresh(mode);
78
79        if (mode == scrp->modes)
80            refresh0 = refresh;
81
82        RRModeMM.mode = mode;
83        RRModeMM.virtX = randrp->virtualX;
84        RRModeMM.virtY = randrp->virtualY;
85        RRModeMM.mmWidth = randrp->mmWidth;
86        RRModeMM.mmHeight = randrp->mmHeight;
87
88        if (scrp->DriverFunc) {
89            (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM);
90        }
91
92        pSize = RRRegisterSize(pScreen,
93                               mode->HDisplay, mode->VDisplay,
94                               RRModeMM.mmWidth, RRModeMM.mmHeight);
95        if (!pSize)
96            return FALSE;
97        RRRegisterRate(pScreen, pSize, refresh);
98        if (mode == scrp->currentMode &&
99            mode->HDisplay == scrp->virtualX &&
100            mode->VDisplay == scrp->virtualY)
101            RRSetCurrentConfig(pScreen, randrp->rotation, refresh, pSize);
102        if (mode->next == scrp->modes)
103            break;
104    }
105    if (scrp->currentMode->HDisplay != randrp->virtualX ||
106        scrp->currentMode->VDisplay != randrp->virtualY) {
107        mode = scrp->modes;
108
109        RRModeMM.mode = NULL;
110        RRModeMM.virtX = randrp->virtualX;
111        RRModeMM.virtY = randrp->virtualY;
112        RRModeMM.mmWidth = randrp->mmWidth;
113        RRModeMM.mmHeight = randrp->mmHeight;
114
115        if (scrp->DriverFunc) {
116            (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM);
117        }
118
119        pSize = RRRegisterSize(pScreen,
120                               randrp->virtualX, randrp->virtualY,
121                               RRModeMM.mmWidth, RRModeMM.mmHeight);
122        if (!pSize)
123            return FALSE;
124        RRRegisterRate(pScreen, pSize, refresh0);
125        if (scrp->virtualX == randrp->virtualX &&
126            scrp->virtualY == randrp->virtualY) {
127            RRSetCurrentConfig(pScreen, randrp->rotation, refresh0, pSize);
128        }
129    }
130
131    /* If there is driver support for randr, let it set our supported rotations */
132    if (scrp->DriverFunc) {
133        xorgRRRotation RRRotation;
134
135        RRRotation.RRRotations = *rotations;
136        if (!(*scrp->DriverFunc) (scrp, RR_GET_INFO, &RRRotation))
137            return TRUE;
138        *rotations = RRRotation.RRRotations;
139    }
140
141    return TRUE;
142}
143
144static Bool
145xf86RandRSetMode(ScreenPtr pScreen,
146                 DisplayModePtr mode,
147                 Bool useVirtual, int mmWidth, int mmHeight)
148{
149    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
150    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
151    int oldWidth = pScreen->width;
152    int oldHeight = pScreen->height;
153    int oldmmWidth = pScreen->mmWidth;
154    int oldmmHeight = pScreen->mmHeight;
155    int oldVirtualX = scrp->virtualX;
156    int oldVirtualY = scrp->virtualY;
157    WindowPtr pRoot = pScreen->root;
158    Bool ret = TRUE;
159
160    if (pRoot && scrp->vtSema)
161        (*scrp->EnableDisableFBAccess) (scrp, FALSE);
162    if (useVirtual) {
163        scrp->virtualX = randrp->virtualX;
164        scrp->virtualY = randrp->virtualY;
165    }
166    else {
167        scrp->virtualX = mode->HDisplay;
168        scrp->virtualY = mode->VDisplay;
169    }
170
171    /*
172     * The DIX forgets the physical dimensions we passed into RRRegisterSize, so
173     * reconstruct them if possible.
174     */
175    if (scrp->DriverFunc) {
176        xorgRRModeMM RRModeMM;
177
178        RRModeMM.mode = mode;
179        RRModeMM.virtX = scrp->virtualX;
180        RRModeMM.virtY = scrp->virtualY;
181        RRModeMM.mmWidth = mmWidth;
182        RRModeMM.mmHeight = mmHeight;
183
184        (*scrp->DriverFunc) (scrp, RR_GET_MODE_MM, &RRModeMM);
185
186        mmWidth = RRModeMM.mmWidth;
187        mmHeight = RRModeMM.mmHeight;
188    }
189    if (randrp->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
190        /* If the screen is rotated 90 or 270 degrees, swap the sizes. */
191        pScreen->width = scrp->virtualY;
192        pScreen->height = scrp->virtualX;
193        pScreen->mmWidth = mmHeight;
194        pScreen->mmHeight = mmWidth;
195    }
196    else {
197        pScreen->width = scrp->virtualX;
198        pScreen->height = scrp->virtualY;
199        pScreen->mmWidth = mmWidth;
200        pScreen->mmHeight = mmHeight;
201    }
202    if (!xf86SwitchMode(pScreen, mode)) {
203        pScreen->width = oldWidth;
204        pScreen->height = oldHeight;
205        pScreen->mmWidth = oldmmWidth;
206        pScreen->mmHeight = oldmmHeight;
207        scrp->virtualX = oldVirtualX;
208        scrp->virtualY = oldVirtualY;
209        ret = FALSE;
210    }
211    /*
212     * Make sure the layout is correct
213     */
214    xf86ReconfigureLayout();
215
216    if (scrp->vtSema) {
217        /*
218         * Make sure the whole screen is visible
219         */
220        xf86SetViewport (pScreen, pScreen->width, pScreen->height);
221        xf86SetViewport (pScreen, 0, 0);
222        if (pRoot)
223            (*scrp->EnableDisableFBAccess) (scrp, TRUE);
224    }
225    return ret;
226}
227
228static Bool
229xf86RandRSetConfig(ScreenPtr pScreen,
230                   Rotation rotation, int rate, RRScreenSizePtr pSize)
231{
232    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
233    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
234    DisplayModePtr mode;
235    int pos[MAXDEVICES][2];
236    Bool useVirtual = FALSE;
237    Rotation oldRotation = randrp->rotation;
238    DeviceIntPtr dev;
239    Bool view_adjusted = FALSE;
240
241    for (dev = inputInfo.devices; dev; dev = dev->next) {
242        if (!IsMaster(dev) && !IsFloating(dev))
243            continue;
244
245        miPointerGetPosition(dev, &pos[dev->id][0], &pos[dev->id][1]);
246    }
247
248    for (mode = scrp->modes;; mode = mode->next) {
249        if (mode->HDisplay == pSize->width &&
250            mode->VDisplay == pSize->height &&
251            (rate == 0 || xf86RandRModeRefresh(mode) == rate))
252            break;
253        if (mode->next == scrp->modes) {
254            if (pSize->width == randrp->virtualX &&
255                pSize->height == randrp->virtualY) {
256                mode = scrp->modes;
257                useVirtual = TRUE;
258                break;
259            }
260            return FALSE;
261        }
262    }
263
264    if (randrp->rotation != rotation) {
265
266        /* Have the driver do its thing. */
267        if (scrp->DriverFunc) {
268            xorgRRRotation RRRotation;
269
270            RRRotation.RRConfig.rotation = rotation;
271            RRRotation.RRConfig.rate = rate;
272            RRRotation.RRConfig.width = pSize->width;
273            RRRotation.RRConfig.height = pSize->height;
274
275            /*
276             * Currently we need to rely on HW support for rotation.
277             */
278            if (!(*scrp->DriverFunc) (scrp, RR_SET_CONFIG, &RRRotation))
279                return FALSE;
280        }
281        else
282            return FALSE;
283
284        randrp->rotation = rotation;
285    }
286
287    if (!xf86RandRSetMode
288        (pScreen, mode, useVirtual, pSize->mmWidth, pSize->mmHeight)) {
289        if (randrp->rotation != oldRotation) {
290            /* Have the driver undo its thing. */
291            if (scrp->DriverFunc) {
292                xorgRRRotation RRRotation;
293
294                RRRotation.RRConfig.rotation = oldRotation;
295                RRRotation.RRConfig.rate =
296                    xf86RandRModeRefresh(scrp->currentMode);
297                RRRotation.RRConfig.width = scrp->virtualX;
298                RRRotation.RRConfig.height = scrp->virtualY;
299                (*scrp->DriverFunc) (scrp, RR_SET_CONFIG, &RRRotation);
300            }
301
302            randrp->rotation = oldRotation;
303        }
304        return FALSE;
305    }
306
307    update_desktop_dimensions();
308
309    /*
310     * Move the cursor back where it belongs; SwitchMode repositions it
311     * FIXME: duplicated code, see modes/xf86RandR12.c
312     */
313    for (dev = inputInfo.devices; dev; dev = dev->next) {
314        if (!IsMaster(dev) && !IsFloating(dev))
315            continue;
316
317        if (pScreen == miPointerGetScreen(dev)) {
318            int px = pos[dev->id][0];
319            int py = pos[dev->id][1];
320
321            px = (px >= pScreen->width ? (pScreen->width - 1) : px);
322            py = (py >= pScreen->height ? (pScreen->height - 1) : py);
323
324            /* Setting the viewpoint makes only sense on one device */
325            if (!view_adjusted && IsMaster(dev)) {
326                xf86SetViewport(pScreen, px, py);
327                view_adjusted = TRUE;
328            }
329
330            (*pScreen->SetCursorPosition) (dev, pScreen, px, py, FALSE);
331        }
332    }
333
334    return TRUE;
335}
336
337/*
338 * Reset size back to original
339 */
340static Bool
341xf86RandRCloseScreen(ScreenPtr pScreen)
342{
343    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
344    XF86RandRInfoPtr randrp = XF86RANDRINFO(pScreen);
345
346    scrp->virtualX = pScreen->width = randrp->virtualX;
347    scrp->virtualY = pScreen->height = randrp->virtualY;
348    scrp->currentMode = scrp->modes;
349    pScreen->CloseScreen = randrp->CloseScreen;
350    free(randrp);
351    dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, NULL);
352    return (*pScreen->CloseScreen) (pScreen);
353}
354
355Rotation
356xf86GetRotation(ScreenPtr pScreen)
357{
358    if (xf86RandRKey == NULL)
359        return RR_Rotate_0;
360
361    return XF86RANDRINFO(pScreen)->rotation;
362}
363
364/* Function to change RandR's idea of the virtual screen size */
365Bool
366xf86RandRSetNewVirtualAndDimensions(ScreenPtr pScreen,
367                                    int newvirtX, int newvirtY, int newmmWidth,
368                                    int newmmHeight, Bool resetMode)
369{
370    XF86RandRInfoPtr randrp;
371
372    if (xf86RandRKey == NULL)
373        return FALSE;
374
375    randrp = XF86RANDRINFO(pScreen);
376    if (randrp == NULL)
377        return FALSE;
378
379    if (newvirtX > 0)
380        randrp->virtualX = newvirtX;
381
382    if (newvirtY > 0)
383        randrp->virtualY = newvirtY;
384
385    if (newmmWidth > 0)
386        randrp->mmWidth = newmmWidth;
387
388    if (newmmHeight > 0)
389        randrp->mmHeight = newmmHeight;
390
391    /* This is only for during server start */
392    if (resetMode) {
393	ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
394        return (xf86RandRSetMode(pScreen,
395                                 pScrn->currentMode,
396                                 TRUE, pScreen->mmWidth, pScreen->mmHeight));
397    }
398
399    return TRUE;
400}
401
402Bool
403xf86RandRInit(ScreenPtr pScreen)
404{
405    rrScrPrivPtr rp;
406    XF86RandRInfoPtr randrp;
407    ScrnInfoPtr scrp = xf86ScreenToScrn(pScreen);
408
409#ifdef PANORAMIX
410    /* XXX disable RandR when using Xinerama */
411    if (!noPanoramiXExtension)
412        return TRUE;
413#endif
414
415    xf86RandRKey = &xf86RandRKeyRec;
416
417    if (!dixRegisterPrivateKey(&xf86RandRKeyRec, PRIVATE_SCREEN, 0))
418        return FALSE;
419
420    randrp = malloc(sizeof(XF86RandRInfoRec));
421    if (!randrp)
422        return FALSE;
423
424    if (!RRScreenInit(pScreen)) {
425        free(randrp);
426        return FALSE;
427    }
428    rp = rrGetScrPriv(pScreen);
429    rp->rrGetInfo = xf86RandRGetInfo;
430    rp->rrSetConfig = xf86RandRSetConfig;
431
432    randrp->virtualX = scrp->virtualX;
433    randrp->virtualY = scrp->virtualY;
434    randrp->mmWidth = pScreen->mmWidth;
435    randrp->mmHeight = pScreen->mmHeight;
436
437    randrp->CloseScreen = pScreen->CloseScreen;
438    pScreen->CloseScreen = xf86RandRCloseScreen;
439
440    randrp->rotation = RR_Rotate_0;
441
442    dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, randrp);
443    return TRUE;
444}
445