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