1 /*
2  * Copyright © 2000 Compaq Computer Corporation, Inc.
3  * Copyright © 2002 Hewlett Packard Company, Inc.
4  * Copyright © 2006 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that copyright
9  * notice and this permission notice appear in supporting documentation, and
10  * that the name of the copyright holders not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  The copyright holders make no representations
13  * about the suitability of this software for any purpose.  It is provided "as
14  * is" without express or implied warranty.
15  *
16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22  * OF THIS SOFTWARE.
23  *
24  * Author:  Jim Gettys, HP Labs, HP.
25  * Author:  Keith Packard, Intel Corporation
26  */
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #include <limits.h>
33 #include <stdio.h>
34 #include <X11/Xlib.h>
35 /* we need to be able to manipulate the Display structure on events */
36 #include <X11/Xlibint.h>
37 #include <X11/extensions/render.h>
38 #include <X11/extensions/Xrender.h>
39 #include "Xrandrint.h"
40 
41 static XRRScreenConfiguration *_XRRGetScreenInfo (Display *dpy,
42 						  XExtDisplayInfo *info,
43 						  Window window);
44 
XRRConfigRotations(XRRScreenConfiguration * config,Rotation * current_rotation)45 Rotation XRRConfigRotations(XRRScreenConfiguration *config, Rotation *current_rotation)
46 {
47   *current_rotation = config->current_rotation;
48   return config->rotations;
49 }
50 
XRRConfigSizes(XRRScreenConfiguration * config,int * nsizes)51 XRRScreenSize *XRRConfigSizes(XRRScreenConfiguration *config, int *nsizes)
52 {
53    *nsizes = config->nsizes;
54   return config->sizes;
55 }
56 
XRRConfigRates(XRRScreenConfiguration * config,int sizeID,int * nrates)57 short *XRRConfigRates (XRRScreenConfiguration *config, int sizeID, int *nrates)
58 {
59     short   *r = config->rates;
60     int	    nents = config->nrates;
61 
62     /* Skip over the intervening rate lists */
63     while (sizeID > 0 && nents > 0)
64     {
65 	int i = (*r + 1);
66 	r += i;
67 	nents -= i;
68 	sizeID--;
69     }
70     if (!nents)
71     {
72 	*nrates = 0;
73 	return NULL;
74     }
75     *nrates = (int) *r;
76     return r + 1;
77 }
78 
XRRConfigTimes(XRRScreenConfiguration * config,Time * config_timestamp)79 Time XRRConfigTimes (XRRScreenConfiguration *config, Time *config_timestamp)
80 {
81     *config_timestamp = config->config_timestamp;
82     return config->timestamp;
83 }
84 
85 
XRRConfigCurrentConfiguration(XRRScreenConfiguration * config,Rotation * rotation)86 SizeID XRRConfigCurrentConfiguration (XRRScreenConfiguration *config,
87 			      Rotation *rotation)
88 {
89     *rotation = (Rotation) config->current_rotation;
90     return (SizeID) config->current_size;
91 }
92 
XRRConfigCurrentRate(XRRScreenConfiguration * config)93 short XRRConfigCurrentRate (XRRScreenConfiguration *config)
94 {
95     return config->current_rate;
96 }
97 
98 /*
99  * Go get the screen configuration data and salt it away for future use;
100  * returns NULL if extension not supported
101  */
_XRRValidateCache(Display * dpy,XExtDisplayInfo * info,int screen)102 static XRRScreenConfiguration *_XRRValidateCache (Display *dpy,
103 						  XExtDisplayInfo *info,
104 						  int screen)
105 {
106     XRRScreenConfiguration **configs;
107     XRandRInfo *xrri;
108 
109     if ((screen >= 0) && (screen < ScreenCount(dpy)) && XextHasExtension(info)) {
110 	xrri = (XRandRInfo *) info->data;
111 	configs = xrri->config;
112 
113 	if (configs[screen] == NULL)
114 	    configs[screen] = _XRRGetScreenInfo (dpy, info, RootWindow(dpy, screen));
115 	return configs[screen];
116     } else {
117 	return NULL;
118     }
119 }
120 
121 /* given a screen, return the information from the (possibly) cached data */
XRRRotations(Display * dpy,int screen,Rotation * current_rotation)122 Rotation XRRRotations(Display *dpy, int screen, Rotation *current_rotation)
123 {
124   XRRScreenConfiguration *config;
125   XExtDisplayInfo *info = XRRFindDisplay(dpy);
126   Rotation cr;
127   LockDisplay(dpy);
128   if ((config = _XRRValidateCache(dpy, info, screen))) {
129     *current_rotation = config->current_rotation;
130     cr = config->rotations;
131     UnlockDisplay(dpy);
132     return cr;
133   }
134   else {
135     UnlockDisplay(dpy);
136     *current_rotation = RR_Rotate_0;
137     return 0;	/* no rotations supported */
138   }
139 }
140 
141 /* given a screen, return the information from the (possibly) cached data */
XRRSizes(Display * dpy,int screen,int * nsizes)142 XRRScreenSize *XRRSizes(Display *dpy, int screen, int *nsizes)
143 {
144   XRRScreenConfiguration *config;
145   XExtDisplayInfo *info = XRRFindDisplay(dpy);
146   XRRScreenSize *sizes;
147 
148   LockDisplay(dpy);
149   if ((config = _XRRValidateCache(dpy, info, screen))) {
150     *nsizes = config->nsizes;
151     sizes = config->sizes;
152     UnlockDisplay(dpy);
153     return sizes;
154     }
155   else {
156     UnlockDisplay(dpy);
157     *nsizes = 0;
158     return NULL;
159   }
160 }
161 
XRRRates(Display * dpy,int screen,int sizeID,int * nrates)162 short *XRRRates (Display *dpy, int screen, int sizeID, int *nrates)
163 {
164   XRRScreenConfiguration *config;
165   XExtDisplayInfo *info = XRRFindDisplay(dpy);
166   short *rates;
167 
168   LockDisplay(dpy);
169   if ((config = _XRRValidateCache(dpy, info, screen))) {
170     rates = XRRConfigRates (config, sizeID, nrates);
171     UnlockDisplay(dpy);
172     return rates;
173     }
174   else {
175     UnlockDisplay(dpy);
176     *nrates = 0;
177     return NULL;
178   }
179 }
180 
181 /* given a screen, return the information from the (possibly) cached data */
XRRTimes(Display * dpy,int screen,Time * config_timestamp)182 Time XRRTimes (Display *dpy, int screen, Time *config_timestamp)
183 {
184   XRRScreenConfiguration *config;
185   XExtDisplayInfo *info = XRRFindDisplay(dpy);
186   Time ts;
187 
188   LockDisplay(dpy);
189   if ((config = _XRRValidateCache(dpy, info, screen))) {
190       *config_timestamp = config->config_timestamp;
191       ts = config->timestamp;
192       UnlockDisplay(dpy);
193       return ts;
194     } else {
195       UnlockDisplay(dpy);
196 	return CurrentTime;
197     }
198 }
199 
200 /* need a version that does not hold the display lock */
_XRRGetScreenInfo(Display * dpy,XExtDisplayInfo * info,Window window)201 static XRRScreenConfiguration *_XRRGetScreenInfo (Display *dpy,
202 						  XExtDisplayInfo *info,
203 						  Window window)
204 {
205     xRRGetScreenInfoReply   rep;
206     xRRGetScreenInfoReq	    *req;
207     _XAsyncHandler 	    async;
208     _XRRVersionState	    async_state;
209     int			    nbytes, nbytesRead, rbytes;
210     int			    i;
211     xScreenSizes	    size;
212     struct _XRRScreenConfiguration  *scp;
213     XRRScreenSize	    *ssp;
214     short    		    *rates;
215     xRRQueryVersionReq      *vreq;
216     XRandRInfo		    *xrri;
217     Bool		    getting_version = False;
218 
219     xrri = (XRandRInfo *) info->data;
220     if (!xrri)
221 	return NULL;
222 
223     if (xrri->major_version == -1)
224     {
225 	/* hide a version query in the request */
226 	GetReq (RRQueryVersion, vreq);
227 	vreq->reqType = info->codes->major_opcode;
228 	vreq->randrReqType = X_RRQueryVersion;
229 	vreq->majorVersion = RANDR_MAJOR;
230 	vreq->minorVersion = RANDR_MINOR;
231 
232 	async_state.version_seq = dpy->request;
233 	async_state.error = False;
234 	async.next = dpy->async_handlers;
235 	async.handler = _XRRVersionHandler;
236 	async.data = (XPointer) &async_state;
237 	dpy->async_handlers = &async;
238 
239 	getting_version = True;
240     }
241 
242     GetReq (RRGetScreenInfo, req);
243     req->reqType = info->codes->major_opcode;
244     req->randrReqType = X_RRGetScreenInfo;
245     req->window = window;
246 
247     if (!_XReply (dpy, (xReply *) &rep, 0, xFalse))
248     {
249 	if (getting_version)
250 	    DeqAsyncHandler (dpy, &async);
251 	SyncHandle ();
252 	return NULL;
253     }
254     if (getting_version)
255     {
256 	DeqAsyncHandler (dpy, &async);
257 	if (async_state.error)
258 	{
259 	    UnlockDisplay (dpy);
260 	    SyncHandle();
261 	    LockDisplay (dpy);
262 	}
263 	xrri->major_version = async_state.major_version;
264 	xrri->minor_version = async_state.minor_version;
265 	xrri->has_rates = _XRRHasRates (xrri->minor_version, xrri->major_version);
266     }
267 
268     /*
269      * Make the reply compatible with v1.1
270      */
271     if (!xrri->has_rates)
272     {
273 	rep.rate = 0;
274 	rep.nrateEnts = 0;
275     }
276     if (rep.length < INT_MAX >> 2) {
277 	nbytes = (long) rep.length << 2;
278 
279 	nbytesRead = (long) (rep.nSizes * SIZEOF (xScreenSizes) +
280 			    ((rep.nrateEnts + 1)& ~1) * 2 /* SIZEOF(CARD16) */);
281 
282 	/*
283 	 * first we must compute how much space to allocate for
284 	 * randr library's use; we'll allocate the structures in a single
285 	 * allocation, on cleanlyness grounds.
286 	 */
287 
288 	rbytes = sizeof (XRRScreenConfiguration) +
289 	  (rep.nSizes * sizeof (XRRScreenSize) +
290 	   rep.nrateEnts * sizeof (int));
291 
292 	scp = (struct _XRRScreenConfiguration *) Xmalloc(rbytes);
293     } else {
294 	nbytes = 0;
295 	nbytesRead = 0;
296 	rbytes = 0;
297 	scp = NULL;
298     }
299 
300     if (scp == NULL) {
301 	_XEatData (dpy, (unsigned long) nbytes);
302 	return NULL;
303     }
304 
305 
306     ssp = (XRRScreenSize *)(scp + 1);
307     rates = (short *) (ssp + rep.nSizes);
308 
309     /* set up the screen configuration structure */
310     scp->screen =
311       ScreenOfDisplay (dpy, XRRRootToScreen(dpy, rep.root));
312 
313     scp->sizes = ssp;
314     scp->rates = rates;
315     scp->rotations = rep.setOfRotations;
316     scp->current_size = rep.sizeID;
317     scp->current_rate = rep.rate;
318     scp->current_rotation = rep.rotation;
319     scp->timestamp = rep.timestamp;
320     scp->config_timestamp = rep.configTimestamp;
321     scp->nsizes = rep.nSizes;
322     scp->nrates = rep.nrateEnts;
323 
324     /*
325      * Time to unpack the data from the server.
326      */
327 
328     /*
329      * First the size information
330      */
331     for (i = 0; i < rep.nSizes; i++)  {
332 	_XReadPad (dpy, (char *) &size, SIZEOF (xScreenSizes));
333 
334         ssp[i].width = size.widthInPixels;
335 	ssp[i].height = size.heightInPixels;
336 	ssp[i].mwidth = size.widthInMillimeters;
337 	ssp[i].mheight = size.heightInMillimeters;
338     }
339     /*
340      * And the rates
341      */
342     _XRead16Pad (dpy, rates, 2 /* SIZEOF (CARD16) */ * rep.nrateEnts);
343 
344     /*
345      * Skip any extra data
346      */
347     if (nbytes > nbytesRead)
348 	_XEatData (dpy, (unsigned long) (nbytes - nbytesRead));
349 
350     return (XRRScreenConfiguration *)(scp);
351 }
352 
XRRGetScreenInfo(Display * dpy,Window window)353 XRRScreenConfiguration *XRRGetScreenInfo (Display *dpy, Window window)
354 {
355     XRRScreenConfiguration *config;
356     XExtDisplayInfo *info = XRRFindDisplay(dpy);
357     XRRFindDisplay(dpy);
358     LockDisplay (dpy);
359     config = _XRRGetScreenInfo(dpy, info, window);
360     UnlockDisplay (dpy);
361     SyncHandle ();
362     return config;
363 }
364 
365 
XRRFreeScreenConfigInfo(XRRScreenConfiguration * config)366 void XRRFreeScreenConfigInfo (XRRScreenConfiguration *config)
367 {
368     Xfree (config);
369 }
370 
XRRSetScreenConfigAndRate(Display * dpy,XRRScreenConfiguration * config,Drawable draw,int size_index,Rotation rotation,short rate,Time timestamp)371 Status XRRSetScreenConfigAndRate (Display *dpy,
372 				  XRRScreenConfiguration *config,
373 				  Drawable draw,
374 				  int size_index,
375 				  Rotation rotation,
376 				  short rate,
377 				  Time timestamp)
378 {
379     XExtDisplayInfo *info = XRRFindDisplay (dpy);
380     xRRSetScreenConfigReply rep;
381     XRandRInfo *xrri;
382     int major, minor;
383 
384     RRCheckExtension (dpy, info, 0);
385 
386     /* Make sure has_rates is set */
387     if (!XRRQueryVersion (dpy, &major, &minor))
388 	return 0;
389 
390     LockDisplay (dpy);
391     xrri = (XRandRInfo *) info->data;
392     if (xrri->has_rates)
393     {
394 	xRRSetScreenConfigReq  *req;
395 	GetReq (RRSetScreenConfig, req);
396 	req->reqType = info->codes->major_opcode;
397 	req->randrReqType = X_RRSetScreenConfig;
398 	req->drawable = draw;
399 	req->sizeID = size_index;
400 	req->rotation = rotation;
401 	req->timestamp = timestamp;
402 	req->configTimestamp = config->config_timestamp;
403 	req->rate = rate;
404     }
405     else
406     {
407 	xRR1_0SetScreenConfigReq  *req;
408 	GetReq (RR1_0SetScreenConfig, req);
409 	req->reqType = info->codes->major_opcode;
410 	req->randrReqType = X_RRSetScreenConfig;
411 	req->drawable = draw;
412 	req->sizeID = size_index;
413 	req->rotation = rotation;
414 	req->timestamp = timestamp;
415 	req->configTimestamp = config->config_timestamp;
416     }
417 
418     (void) _XReply (dpy, (xReply *) &rep, 0, xTrue);
419 
420     /* actually .errorCode in struct xError */
421     if (rep.status == RRSetConfigSuccess) {
422       /* if we succeed, set our view of reality to what we set it to */
423       config->config_timestamp = rep.newConfigTimestamp;
424       config->timestamp = rep.newTimestamp;
425       config->screen = ScreenOfDisplay (dpy, XRRRootToScreen(dpy, rep.root));
426       config->current_size = size_index;
427       config->current_rotation = rotation;
428     }
429     UnlockDisplay (dpy);
430     SyncHandle ();
431     return(rep.status);
432 }
433 
XRRSetScreenConfig(Display * dpy,XRRScreenConfiguration * config,Drawable draw,int size_index,Rotation rotation,Time timestamp)434 Status XRRSetScreenConfig (Display *dpy,
435 			   XRRScreenConfiguration *config,
436 			   Drawable draw,
437 			   int size_index,
438 			   Rotation rotation, Time timestamp)
439 {
440     return XRRSetScreenConfigAndRate (dpy, config, draw, size_index,
441 				      rotation, 0, timestamp);
442 }
443