xf86RandR.c revision 6747b715
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    CreateScreenResourcesProcPtr    CreateScreenResources;
41    CloseScreenProcPtr		    CloseScreen;
42    int				    virtualX;
43    int				    virtualY;
44    int				    mmWidth;
45    int				    mmHeight;
46    Rotation			    rotation;
47} XF86RandRInfoRec, *XF86RandRInfoPtr;
48
49static DevPrivateKeyRec xf86RandRKeyRec;
50static DevPrivateKey xf86RandRKey;
51
52#define XF86RANDRINFO(p) ((XF86RandRInfoPtr)dixLookupPrivate(&(p)->devPrivates, xf86RandRKey))
53
54static int
55xf86RandRModeRefresh (DisplayModePtr mode)
56{
57    if (mode->VRefresh)
58	return (int) (mode->VRefresh + 0.5);
59    else if (mode->Clock == 0)
60	return 0;
61    else
62	return (int) (mode->Clock * 1000.0 / mode->HTotal / mode->VTotal + 0.5);
63}
64
65static Bool
66xf86RandRGetInfo (ScreenPtr pScreen, Rotation *rotations)
67{
68    RRScreenSizePtr	    pSize;
69    ScrnInfoPtr		    scrp = XF86SCRNINFO(pScreen);
70    XF86RandRInfoPtr	    randrp = XF86RANDRINFO(pScreen);
71    DisplayModePtr	    mode;
72    int			    refresh0 = 60;
73    xorgRRModeMM	    RRModeMM;
74
75    *rotations = RR_Rotate_0;
76
77    for (mode = scrp->modes; mode != NULL ; mode = mode->next)
78    {
79	int refresh = xf86RandRModeRefresh (mode);
80
81	if (mode == scrp->modes)
82	    refresh0 = refresh;
83
84	RRModeMM.mode = mode;
85	RRModeMM.virtX = randrp->virtualX;
86	RRModeMM.virtY = randrp->virtualY;
87	RRModeMM.mmWidth = randrp->mmWidth;
88	RRModeMM.mmHeight = randrp->mmHeight;
89
90	if(scrp->DriverFunc) {
91	   (*scrp->DriverFunc)(scrp, RR_GET_MODE_MM, &RRModeMM);
92	}
93
94	pSize = RRRegisterSize (pScreen,
95				mode->HDisplay, mode->VDisplay,
96				RRModeMM.mmWidth, RRModeMM.mmHeight);
97	if (!pSize)
98	    return FALSE;
99	RRRegisterRate (pScreen, pSize, refresh);
100	if (mode == scrp->currentMode &&
101	    mode->HDisplay == scrp->virtualX && mode->VDisplay == scrp->virtualY)
102	    RRSetCurrentConfig (pScreen, randrp->rotation, refresh, pSize);
103	if (mode->next == scrp->modes)
104	    break;
105    }
106    if (scrp->currentMode->HDisplay != randrp->virtualX ||
107	scrp->currentMode->VDisplay != randrp->virtualY)
108    {
109	mode = scrp->modes;
110
111	RRModeMM.mode = NULL;
112	RRModeMM.virtX = randrp->virtualX;
113	RRModeMM.virtY = randrp->virtualY;
114	RRModeMM.mmWidth = randrp->mmWidth;
115	RRModeMM.mmHeight = randrp->mmHeight;
116
117	if(scrp->DriverFunc) {
118	   (*scrp->DriverFunc)(scrp, RR_GET_MODE_MM, &RRModeMM);
119	}
120
121	pSize = RRRegisterSize (pScreen,
122				randrp->virtualX, randrp->virtualY,
123				RRModeMM.mmWidth, RRModeMM.mmHeight);
124	if (!pSize)
125	    return FALSE;
126	RRRegisterRate (pScreen, pSize, refresh0);
127	if (scrp->virtualX == randrp->virtualX &&
128	    scrp->virtualY == randrp->virtualY)
129	{
130	    RRSetCurrentConfig (pScreen, randrp->rotation, refresh0, pSize);
131	}
132    }
133
134    /* If there is driver support for randr, let it set our supported rotations */
135    if(scrp->DriverFunc) {
136	xorgRRRotation RRRotation;
137
138	RRRotation.RRRotations = *rotations;
139	if (!(*scrp->DriverFunc)(scrp, RR_GET_INFO, &RRRotation))
140	    return TRUE;
141	*rotations = RRRotation.RRRotations;
142    }
143
144    return TRUE;
145}
146
147static Bool
148xf86RandRSetMode (ScreenPtr	    pScreen,
149		  DisplayModePtr    mode,
150		  Bool		    useVirtual,
151		  int		    mmWidth,
152		  int		    mmHeight)
153{
154    ScrnInfoPtr		scrp = XF86SCRNINFO(pScreen);
155    XF86RandRInfoPtr	randrp = XF86RANDRINFO(pScreen);
156    int			oldWidth = pScreen->width;
157    int			oldHeight = pScreen->height;
158    int			oldmmWidth = pScreen->mmWidth;
159    int			oldmmHeight = pScreen->mmHeight;
160    int			oldVirtualX = scrp->virtualX;
161    int			oldVirtualY = scrp->virtualY;
162    WindowPtr		pRoot = pScreen->root;
163    Bool		ret = TRUE;
164
165    if (pRoot && scrp->vtSema)
166	(*scrp->EnableDisableFBAccess) (pScreen->myNum, FALSE);
167    if (useVirtual)
168    {
169	scrp->virtualX = randrp->virtualX;
170	scrp->virtualY = randrp->virtualY;
171    }
172    else
173    {
174	scrp->virtualX = mode->HDisplay;
175	scrp->virtualY = mode->VDisplay;
176    }
177
178    /*
179     * The DIX forgets the physical dimensions we passed into RRRegisterSize, so
180     * reconstruct them if possible.
181     */
182    if(scrp->DriverFunc) {
183	xorgRRModeMM RRModeMM;
184
185	RRModeMM.mode = mode;
186	RRModeMM.virtX = scrp->virtualX;
187	RRModeMM.virtY = scrp->virtualY;
188	RRModeMM.mmWidth = mmWidth;
189	RRModeMM.mmHeight = mmHeight;
190
191	(*scrp->DriverFunc)(scrp, RR_GET_MODE_MM, &RRModeMM);
192
193	mmWidth = RRModeMM.mmWidth;
194	mmHeight = RRModeMM.mmHeight;
195    }
196    if(randrp->rotation & (RR_Rotate_90 | RR_Rotate_270))
197    {
198	/* If the screen is rotated 90 or 270 degrees, swap the sizes. */
199	pScreen->width = scrp->virtualY;
200	pScreen->height = scrp->virtualX;
201	pScreen->mmWidth = mmHeight;
202	pScreen->mmHeight = mmWidth;
203    }
204    else
205    {
206	pScreen->width = scrp->virtualX;
207	pScreen->height = scrp->virtualY;
208	pScreen->mmWidth = mmWidth;
209	pScreen->mmHeight = mmHeight;
210    }
211    if (!xf86SwitchMode (pScreen, mode))
212    {
213	pScreen->width = oldWidth;
214	pScreen->height = oldHeight;
215	pScreen->mmWidth = oldmmWidth;
216	pScreen->mmHeight = oldmmHeight;
217	scrp->virtualX = oldVirtualX;
218	scrp->virtualY = oldVirtualY;
219	ret = FALSE;
220    }
221    /*
222     * Make sure the layout is correct
223     */
224    xf86ReconfigureLayout();
225
226    /*
227     * Make sure the whole screen is visible
228     */
229    xf86SetViewport (pScreen, pScreen->width, pScreen->height);
230    xf86SetViewport (pScreen, 0, 0);
231    if (pRoot && scrp->vtSema)
232	(*scrp->EnableDisableFBAccess) (pScreen->myNum, TRUE);
233    return ret;
234}
235
236static Bool
237xf86RandRSetConfig (ScreenPtr		pScreen,
238		    Rotation		rotation,
239		    int			rate,
240		    RRScreenSizePtr	pSize)
241{
242    ScrnInfoPtr		    scrp = XF86SCRNINFO(pScreen);
243    XF86RandRInfoPtr	    randrp = XF86RANDRINFO(pScreen);
244    DisplayModePtr	    mode;
245    int			    px, py;
246    Bool		    useVirtual = FALSE;
247    Rotation		    oldRotation = randrp->rotation;
248
249    miPointerGetPosition(inputInfo.pointer, &px, &py);
250    for (mode = scrp->modes; ; mode = mode->next)
251    {
252	if (mode->HDisplay == pSize->width &&
253	    mode->VDisplay == pSize->height &&
254	    (rate == 0 || xf86RandRModeRefresh (mode) == rate))
255	    break;
256	if (mode->next == scrp->modes)
257	{
258	    if (pSize->width == randrp->virtualX &&
259		pSize->height == randrp->virtualY)
260	    {
261		mode = scrp->modes;
262		useVirtual = TRUE;
263		break;
264	    }
265	    return FALSE;
266	}
267    }
268
269    if (randrp->rotation != rotation) {
270
271        /* Have the driver do its thing. */
272	if (scrp->DriverFunc) {
273	    xorgRRRotation RRRotation;
274	    RRRotation.RRConfig.rotation = rotation;
275	    RRRotation.RRConfig.rate = rate;
276	    RRRotation.RRConfig.width = pSize->width;
277	    RRRotation.RRConfig.height = pSize->height;
278
279	    /*
280	     * Currently we need to rely on HW support for rotation.
281	     */
282	    if (!(*scrp->DriverFunc)(scrp, RR_SET_CONFIG, &RRRotation))
283		return FALSE;
284	} else
285	    return FALSE;
286
287	randrp->rotation = rotation;
288    }
289
290    if (!xf86RandRSetMode (pScreen, mode, useVirtual, pSize->mmWidth, pSize->mmHeight)) {
291	if(randrp->rotation != oldRotation) {
292	   /* Have the driver undo its thing. */
293	   if (scrp->DriverFunc) {
294	       xorgRRRotation RRRotation;
295	       RRRotation.RRConfig.rotation = oldRotation;
296	       RRRotation.RRConfig.rate = 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     * Move the cursor back where it belongs; SwitchMode repositions it
308     */
309    if (pScreen == miPointerCurrentScreen ())
310    {
311	px = (px >= pScreen->width ? (pScreen->width - 1) : px);
312	py = (py >= pScreen->height ? (pScreen->height - 1) : py);
313
314        xf86SetViewport(pScreen, px, py);
315
316        (*pScreen->SetCursorPosition) (inputInfo.pointer, pScreen, px, py, FALSE);
317    }
318
319    return TRUE;
320}
321
322/*
323 * Wait until the screen is initialized before whacking the
324 * sizes around; otherwise the screen pixmap will be allocated
325 * at the current mode size rather than the maximum size
326 */
327static Bool
328xf86RandRCreateScreenResources (ScreenPtr pScreen)
329{
330    XF86RandRInfoPtr	    randrp = XF86RANDRINFO(pScreen);
331#if 0
332    ScrnInfoPtr		    scrp = XF86SCRNINFO(pScreen);
333    DisplayModePtr	    mode;
334#endif
335
336    pScreen->CreateScreenResources = randrp->CreateScreenResources;
337    if (!(*pScreen->CreateScreenResources) (pScreen))
338	return FALSE;
339
340#if 0
341    mode = scrp->currentMode;
342    if (mode)
343	xf86RandRSetMode (pScreen, mode, TRUE);
344#endif
345
346    return TRUE;
347}
348
349/*
350 * Reset size back to original
351 */
352static Bool
353xf86RandRCloseScreen (int index, ScreenPtr pScreen)
354{
355    ScrnInfoPtr		    scrp = XF86SCRNINFO(pScreen);
356    XF86RandRInfoPtr	    randrp = XF86RANDRINFO(pScreen);
357
358    scrp->virtualX = pScreen->width = randrp->virtualX;
359    scrp->virtualY = pScreen->height = randrp->virtualY;
360    scrp->currentMode = scrp->modes;
361    pScreen->CloseScreen = randrp->CloseScreen;
362    free(randrp);
363    dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, NULL);
364    return (*pScreen->CloseScreen) (index, pScreen);
365}
366
367Rotation
368xf86GetRotation(ScreenPtr pScreen)
369{
370    if (xf86RandRKey == NULL)
371       return RR_Rotate_0;
372
373    return XF86RANDRINFO(pScreen)->rotation;
374}
375
376/* Function to change RandR's idea of the virtual screen size */
377Bool
378xf86RandRSetNewVirtualAndDimensions(ScreenPtr pScreen,
379	int newvirtX, int newvirtY, int newmmWidth, int newmmHeight,
380	Bool resetMode)
381{
382    XF86RandRInfoPtr randrp;
383
384    if (xf86RandRKey == NULL)
385	return FALSE;
386
387    randrp = XF86RANDRINFO(pScreen);
388    if (randrp == NULL)
389	return FALSE;
390
391    if (newvirtX > 0)
392	randrp->virtualX = newvirtX;
393
394    if (newvirtY > 0)
395	randrp->virtualY = newvirtY;
396
397    if (newmmWidth > 0)
398	randrp->mmWidth = newmmWidth;
399
400    if (newmmHeight > 0)
401	randrp->mmHeight = newmmHeight;
402
403    /* This is only for during server start */
404    if (resetMode) {
405	return (xf86RandRSetMode(pScreen,
406		  XF86SCRNINFO(pScreen)->currentMode,
407		  TRUE,
408		  pScreen->mmWidth, pScreen->mmHeight));
409    }
410
411    return TRUE;
412}
413
414Bool
415xf86RandRInit (ScreenPtr    pScreen)
416{
417    rrScrPrivPtr	rp;
418    XF86RandRInfoPtr	randrp;
419    ScrnInfoPtr		scrp = XF86SCRNINFO(pScreen);
420
421#ifdef PANORAMIX
422    /* XXX disable RandR when using Xinerama */
423    if (!noPanoramiXExtension)
424	return TRUE;
425#endif
426
427    xf86RandRKey = &xf86RandRKeyRec;
428
429    if (!dixRegisterPrivateKey(&xf86RandRKeyRec, PRIVATE_SCREEN, 0))
430	return FALSE;
431
432    randrp = malloc(sizeof (XF86RandRInfoRec));
433    if (!randrp)
434	return FALSE;
435
436    if (!RRScreenInit (pScreen))
437    {
438	free(randrp);
439	return FALSE;
440    }
441    rp = rrGetScrPriv(pScreen);
442    rp->rrGetInfo = xf86RandRGetInfo;
443    rp->rrSetConfig = xf86RandRSetConfig;
444
445    randrp->virtualX = scrp->virtualX;
446    randrp->virtualY = scrp->virtualY;
447    randrp->mmWidth = pScreen->mmWidth;
448    randrp->mmHeight = pScreen->mmHeight;
449
450    randrp->CreateScreenResources = pScreen->CreateScreenResources;
451    pScreen->CreateScreenResources = xf86RandRCreateScreenResources;
452
453    randrp->CloseScreen = pScreen->CloseScreen;
454    pScreen->CloseScreen = xf86RandRCloseScreen;
455
456    randrp->rotation = RR_Rotate_0;
457
458    dixSetPrivate(&pScreen->devPrivates, xf86RandRKey, randrp);
459    return TRUE;
460}
461
462
463