Xrandr.c revision b042e37f
1/*
2 * $XFree86: xc/lib/Xrandr/Xrandr.c,v 1.13tsi Exp $
3 *
4 * Copyright © 2000 Compaq Computer Corporation, Inc.
5 * Copyright © 2002 Hewlett Packard Company, Inc.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of Compaq or HP not be used in advertising
12 * or publicity pertaining to distribution of the software without specific,
13 * written prior permission.  HP makes no representations about the
14 * suitability of this software for any purpose.  It is provided "as is"
15 * without express or implied warranty.
16 *
17 * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author:  Jim Gettys, HP Labs, HP.
25 */
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdio.h>
32#include <X11/Xlib.h>
33/* we need to be able to manipulate the Display structure on events */
34#include <X11/Xlibint.h>
35#include <X11/extensions/render.h>
36#include <X11/extensions/Xrender.h>
37#include "Xrandrint.h"
38
39XExtensionInfo XRRExtensionInfo;
40char XRRExtensionName[] = RANDR_NAME;
41
42static Bool     XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire);
43static Status   XRREventToWire(Display *dpy, XEvent *event, xEvent *wire);
44
45static int
46XRRCloseDisplay (Display *dpy, XExtCodes *codes);
47
48static /* const */ XExtensionHooks rr_extension_hooks = {
49    NULL,				/* create_gc */
50    NULL,				/* copy_gc */
51    NULL,				/* flush_gc */
52    NULL,				/* free_gc */
53    NULL,				/* create_font */
54    NULL,				/* free_font */
55    XRRCloseDisplay,			/* close_display */
56    XRRWireToEvent,			/* wire_to_event */
57    XRREventToWire,			/* event_to_wire */
58    NULL,				/* error */
59    NULL,				/* error_string */
60};
61
62static Bool XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire)
63{
64    XExtDisplayInfo *info = XRRFindDisplay(dpy);
65
66    RRCheckExtension(dpy, info, False);
67
68    switch ((wire->u.u.type & 0x7F) - info->codes->first_event)
69    {
70      case RRScreenChangeNotify: {
71	XRRScreenChangeNotifyEvent *aevent= (XRRScreenChangeNotifyEvent *) event;
72	xRRScreenChangeNotifyEvent *awire = (xRRScreenChangeNotifyEvent *) wire;
73	aevent->type = awire->type & 0x7F;
74	aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire);
75	aevent->send_event = (awire->type & 0x80) != 0;
76	aevent->display = dpy;
77	aevent->window = awire->window;
78	aevent->root = awire->root;
79	aevent->timestamp = awire->timestamp;
80	aevent->config_timestamp = awire->configTimestamp;
81	aevent->size_index = awire->sizeID;
82	aevent->subpixel_order = awire->subpixelOrder;
83	aevent->rotation = awire->rotation;
84	aevent->width = awire->widthInPixels;
85	aevent->height = awire->heightInPixels;
86	aevent->mwidth = awire->widthInMillimeters;
87	aevent->mheight = awire->heightInMillimeters;
88	return True;
89      }
90      case RRNotify: {
91	XRRNotifyEvent *aevent = (XRRNotifyEvent *) event;
92	xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
93	aevent->type = awire->type & 0x7F;
94	aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire);
95	aevent->send_event = (awire->type & 0x80) != 0;
96	aevent->display = dpy;
97	aevent->window = awire->window;
98	aevent->subtype = awire->subCode;
99	switch (aevent->subtype) {
100	case RRNotify_OutputChange: {
101	    XRROutputChangeNotifyEvent *aevent = (XRROutputChangeNotifyEvent *) event;
102	    xRROutputChangeNotifyEvent *awire = (xRROutputChangeNotifyEvent *) wire;
103	    aevent->output = awire->output;
104	    aevent->crtc = awire->crtc;
105	    aevent->mode = awire->mode;
106	    aevent->rotation = awire->rotation;
107	    aevent->connection = awire->connection;
108	    aevent->subpixel_order = awire->subpixelOrder;
109	    return True;
110	}
111	case RRNotify_CrtcChange: {
112	    XRRCrtcChangeNotifyEvent *aevent = (XRRCrtcChangeNotifyEvent *) event;
113	    xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
114	    aevent->crtc = awire->crtc;
115	    aevent->mode = awire->mode;
116	    aevent->rotation = awire->rotation;
117	    aevent->x = awire->x;
118	    aevent->y = awire->y;
119	    aevent->width = awire->width;
120	    aevent->height = awire->height;
121	    return True;
122	}
123	case RRNotify_OutputProperty: {
124	    XRROutputPropertyNotifyEvent *aevent = (XRROutputPropertyNotifyEvent *) event;
125	    xRROutputPropertyNotifyEvent *awire = (xRROutputPropertyNotifyEvent *) wire;
126	    aevent->output = awire->output;
127	    aevent->property = awire->atom;
128	    aevent->timestamp = awire->timestamp;
129	    aevent->state = awire->state;
130	    return True;
131	}
132
133	    break;
134	}
135      }
136    }
137
138    return False;
139}
140
141static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire)
142{
143    XExtDisplayInfo *info = XRRFindDisplay(dpy);
144
145    RRCheckExtension(dpy, info, False);
146
147    switch ((event->type & 0x7F) - info->codes->first_event)
148    {
149      case RRScreenChangeNotify: {
150	xRRScreenChangeNotifyEvent *awire = (xRRScreenChangeNotifyEvent *) wire;
151	XRRScreenChangeNotifyEvent *aevent = (XRRScreenChangeNotifyEvent *) event;
152	awire->type = aevent->type | (aevent->send_event ? 0x80 : 0);
153	awire->rotation = (CARD8) aevent->rotation;
154	awire->sequenceNumber = aevent->serial & 0xFFFF;
155	awire->timestamp = aevent->timestamp;
156	awire->configTimestamp = aevent->config_timestamp;
157	awire->root = aevent->root;
158	awire->window = aevent->window;
159	awire->sizeID = aevent->size_index;
160	awire->subpixelOrder = aevent->subpixel_order;
161	awire->widthInPixels = aevent->width;
162	awire->heightInPixels = aevent->height;
163	awire->widthInMillimeters = aevent->mwidth;
164	awire->heightInMillimeters = aevent->mheight;
165	return True;
166      }
167      case RRNotify: {
168	xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
169	XRRNotifyEvent *aevent = (XRRNotifyEvent *) event;
170	awire->type = aevent->type | (aevent->send_event ? 0x80 : 0);
171	awire->sequenceNumber = aevent->serial & 0xFFFF;
172	awire->window = aevent->window;
173	awire->subCode = aevent->subtype;
174	switch (aevent->subtype) {
175	case RRNotify_OutputChange: {
176	    xRROutputChangeNotifyEvent *awire = (xRROutputChangeNotifyEvent *) wire;
177	    XRROutputChangeNotifyEvent *aevent = (XRROutputChangeNotifyEvent *) event;
178	    awire->output = aevent->output;
179	    awire->crtc = aevent->crtc;
180	    awire->mode = aevent->mode;
181	    awire->rotation = aevent->rotation;
182	    awire->connection = aevent->connection;
183	    awire->subpixelOrder = aevent->subpixel_order;
184	    return True;
185	}
186	case RRNotify_CrtcChange: {
187	    xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
188	    XRRCrtcChangeNotifyEvent *aevent = (XRRCrtcChangeNotifyEvent *) event;
189	    awire->crtc = aevent->crtc;
190	    awire->mode = aevent->mode;
191	    awire->rotation = aevent->rotation;
192	    awire->x = aevent->x;
193	    awire->y = aevent->y;
194	    awire->width = aevent->width;
195	    awire->height = aevent->height;
196	    return True;
197	}
198	case RRNotify_OutputProperty: {
199	    xRROutputPropertyNotifyEvent *awire = (xRROutputPropertyNotifyEvent *) wire;
200	    XRROutputPropertyNotifyEvent *aevent = (XRROutputPropertyNotifyEvent *) event;
201	    awire->output = aevent->output;
202	    awire->atom = aevent->property;
203	    awire->timestamp = aevent->timestamp;
204	    awire->state = aevent->state;
205	    return True;
206	}
207	}
208      }
209    }
210    return False;
211}
212
213XExtDisplayInfo *
214XRRFindDisplay (Display *dpy)
215{
216    XExtDisplayInfo *dpyinfo;
217    XRandRInfo *xrri;
218    int i, numscreens;
219
220    dpyinfo = XextFindDisplay (&XRRExtensionInfo, dpy);
221    if (!dpyinfo) {
222	dpyinfo = XextAddDisplay (&XRRExtensionInfo, dpy,
223				  XRRExtensionName,
224				  &rr_extension_hooks,
225				  RRNumberEvents, 0);
226	numscreens = ScreenCount(dpy);
227	xrri = Xmalloc (sizeof(XRandRInfo) +
228				 sizeof(char *) * numscreens);
229	xrri->config = (XRRScreenConfiguration **)(xrri + 1);
230	for(i = 0; i < numscreens; i++)
231	  xrri->config[i] = NULL;
232	xrri->major_version = -1;
233	dpyinfo->data = (char *) xrri;
234    }
235    return dpyinfo;
236}
237
238static int
239XRRCloseDisplay (Display *dpy, XExtCodes *codes)
240{
241    int i;
242    XRRScreenConfiguration **configs;
243    XExtDisplayInfo *info = XRRFindDisplay (dpy);
244    XRandRInfo *xrri;
245
246    LockDisplay(dpy);
247    /*
248     * free cached data
249     */
250    if (XextHasExtension(info)) {
251	xrri = (XRandRInfo *) info->data;
252	if (xrri) {
253	    configs = xrri->config;
254
255	    for (i = 0; i < ScreenCount(dpy); i++) {
256		if (configs[i] != NULL) XFree (configs[i]);
257	    }
258	    XFree (xrri);
259	}
260    }
261    UnlockDisplay(dpy);
262    return XextRemoveDisplay (&XRRExtensionInfo, dpy);
263}
264
265int XRRRootToScreen(Display *dpy, Window root)
266{
267  int snum;
268  for (snum = 0; snum < ScreenCount(dpy); snum++) {
269    if (RootWindow(dpy, snum) == root) return snum;
270  }
271  return -1;
272}
273
274
275Bool XRRQueryExtension (Display *dpy, int *event_basep, int *error_basep)
276{
277  XExtDisplayInfo *info = XRRFindDisplay (dpy);
278
279    if (XextHasExtension(info)) {
280	*event_basep = info->codes->first_event;
281	*error_basep = info->codes->first_error;
282	return True;
283    } else {
284	return False;
285    }
286}
287
288Bool
289_XRRHasRates (int major, int minor)
290{
291    return major > 1 || (major == 1 && minor >= 1);
292}
293
294Status XRRQueryVersion (Display *dpy,
295			    int	    *major_versionp,
296			    int	    *minor_versionp)
297{
298    XExtDisplayInfo *info = XRRFindDisplay (dpy);
299    xRRQueryVersionReply rep;
300    xRRQueryVersionReq  *req;
301    XRandRInfo *xrri;
302
303    RRCheckExtension (dpy, info, 0);
304
305    xrri = (XRandRInfo *) info->data;
306
307    /*
308     * only get the version information from the server if we don't have it already
309     */
310    if (xrri->major_version == -1) {
311      LockDisplay (dpy);
312      GetReq (RRQueryVersion, req);
313      req->reqType = info->codes->major_opcode;
314      req->randrReqType = X_RRQueryVersion;
315      req->majorVersion = RANDR_MAJOR;
316      req->minorVersion = RANDR_MINOR;
317      if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) {
318	UnlockDisplay (dpy);
319	SyncHandle ();
320	return 0;
321      }
322      xrri->major_version = rep.majorVersion;
323      xrri->minor_version = rep.minorVersion;
324      xrri->has_rates = _XRRHasRates (xrri->major_version, xrri->minor_version);
325      UnlockDisplay (dpy);
326      SyncHandle ();
327    }
328    *major_versionp = xrri->major_version;
329    *minor_versionp = xrri->minor_version;
330    return 1;
331}
332
333Bool
334_XRRVersionHandler (Display	    *dpy,
335			xReply	    *rep,
336			char	    *buf,
337			int	    len,
338			XPointer    data)
339{
340    xRRQueryVersionReply	replbuf;
341    xRRQueryVersionReply	*repl;
342    _XRRVersionState	*state = (_XRRVersionState *) data;
343
344    if (dpy->last_request_read != state->version_seq)
345	return False;
346    if (rep->generic.type == X_Error)
347    {
348	state->error = True;
349	return False;
350    }
351    repl = (xRRQueryVersionReply *)
352	_XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
353		     (SIZEOF(xRRQueryVersionReply) - SIZEOF(xReply)) >> 2,
354			True);
355    state->major_version = repl->majorVersion;
356    state->minor_version = repl->minorVersion;
357    return True;
358}
359
360/*
361 * in protocol version 0.1, routine added to allow selecting for new events.
362 */
363
364void XRRSelectInput (Display *dpy, Window window, int mask)
365{
366    XExtDisplayInfo *info = XRRFindDisplay (dpy);
367    xRRSelectInputReq  *req;
368
369    RRSimpleCheckExtension (dpy, info);
370
371    LockDisplay (dpy);
372    GetReq (RRSelectInput, req);
373    req->reqType = info->codes->major_opcode;
374    req->randrReqType = X_RRSelectInput;
375    req->window = window;
376    req->enable = 0;
377    if (mask) req->enable = mask;
378    UnlockDisplay (dpy);
379    SyncHandle ();
380    return;
381}
382
383int XRRUpdateConfiguration(XEvent *event)
384{
385    XRRScreenChangeNotifyEvent *scevent;
386    XConfigureEvent *rcevent;
387    Display *dpy = event->xany.display;
388    XExtDisplayInfo *info;
389    XRandRInfo *xrri;
390    int snum;
391
392    /* first, see if it is a vanilla configure notify event */
393    if (event->type == ConfigureNotify) {
394	rcevent = (XConfigureEvent *) event;
395	snum = XRRRootToScreen(dpy, rcevent->window);
396	dpy->screens[snum].width   = rcevent->width;
397	dpy->screens[snum].height  = rcevent->height;
398	return 1;
399    }
400
401    info = XRRFindDisplay(dpy);
402    RRCheckExtension (dpy, info, 0);
403
404    switch (event->type - info->codes->first_event) {
405    case RRScreenChangeNotify:
406	scevent = (XRRScreenChangeNotifyEvent *) event;
407	snum = XRRRootToScreen(dpy,
408			       ((XRRScreenChangeNotifyEvent *) event)->root);
409	if (scevent->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
410		dpy->screens[snum].width   = scevent->height;
411		dpy->screens[snum].height  = scevent->width;
412		dpy->screens[snum].mwidth  = scevent->mheight;
413		dpy->screens[snum].mheight = scevent->mwidth;
414	} else {
415		dpy->screens[snum].width   = scevent->width;
416		dpy->screens[snum].height  = scevent->height;
417		dpy->screens[snum].mwidth  = scevent->mwidth;
418		dpy->screens[snum].mheight = scevent->mheight;
419	}
420	XRenderSetSubpixelOrder (dpy, snum, scevent->subpixel_order);
421	break;
422    default:
423	return 0;
424    }
425    xrri = (XRandRInfo *) info->data;
426    /*
427     * so the next time someone wants some data, it will be fetched;
428     * it might be better to force the round trip immediately, but
429     * I dislike pounding the server simultaneously when not necessary
430     */
431    if (xrri->config[snum] != NULL) {
432	XFree (xrri->config[snum]);
433	xrri->config[snum] = NULL;
434    }
435    return 1;
436}
437