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