Git init
[framework/uifw/xorg/lib/libxrandr.git] / src / Xrandr.c
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
37 static XExtensionInfo XRRExtensionInfo;
38 _X_HIDDEN char XRRExtensionName[] = RANDR_NAME;
39
40 static Bool     XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire);
41 static Status   XRREventToWire(Display *dpy, XEvent *event, xEvent *wire);
42
43 static int
44 XRRCloseDisplay (Display *dpy, XExtCodes *codes);
45
46 static /* 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
60 static 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
141             break;
142         }
143       }
144     }
145
146     return False;
147 }
148
149 static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire)
150 {
151     XExtDisplayInfo *info = XRRFindDisplay(dpy);
152
153     RRCheckExtension(dpy, info, False);
154
155     switch ((event->type & 0x7F) - info->codes->first_event)
156     {
157       case RRScreenChangeNotify: {
158         xRRScreenChangeNotifyEvent *awire = (xRRScreenChangeNotifyEvent *) wire;
159         XRRScreenChangeNotifyEvent *aevent = (XRRScreenChangeNotifyEvent *) event;
160         awire->type = aevent->type | (aevent->send_event ? 0x80 : 0);
161         awire->rotation = (CARD8) aevent->rotation;
162         awire->sequenceNumber = aevent->serial & 0xFFFF;
163         awire->timestamp = aevent->timestamp;
164         awire->configTimestamp = aevent->config_timestamp;
165         awire->root = aevent->root;
166         awire->window = aevent->window;
167         awire->sizeID = aevent->size_index;
168         awire->subpixelOrder = aevent->subpixel_order;
169         awire->widthInPixels = aevent->width;
170         awire->heightInPixels = aevent->height;
171         awire->widthInMillimeters = aevent->mwidth;
172         awire->heightInMillimeters = aevent->mheight;
173         return True;
174       }
175       case RRNotify: {
176         xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
177         XRRNotifyEvent *aevent = (XRRNotifyEvent *) event;
178         awire->type = aevent->type | (aevent->send_event ? 0x80 : 0);
179         awire->sequenceNumber = aevent->serial & 0xFFFF;
180         awire->subCode = aevent->subtype;
181         switch (aevent->subtype) {
182         case RRNotify_OutputChange: {
183             xRROutputChangeNotifyEvent *awire = (xRROutputChangeNotifyEvent *) wire;
184             XRROutputChangeNotifyEvent *aevent = (XRROutputChangeNotifyEvent *) event;
185             awire->window = aevent->window;
186             awire->output = aevent->output;
187             awire->crtc = aevent->crtc;
188             awire->mode = aevent->mode;
189             awire->rotation = aevent->rotation;
190             awire->connection = aevent->connection;
191             awire->subpixelOrder = aevent->subpixel_order;
192             return True;
193         }
194         case RRNotify_CrtcChange: {
195             xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire;
196             XRRCrtcChangeNotifyEvent *aevent = (XRRCrtcChangeNotifyEvent *) event;
197             awire->window = aevent->window;
198             awire->crtc = aevent->crtc;
199             awire->mode = aevent->mode;
200             awire->rotation = aevent->rotation;
201             awire->x = aevent->x;
202             awire->y = aevent->y;
203             awire->width = aevent->width;
204             awire->height = aevent->height;
205             return True;
206         }
207         case RRNotify_OutputProperty: {
208             xRROutputPropertyNotifyEvent *awire = (xRROutputPropertyNotifyEvent *) wire;
209             XRROutputPropertyNotifyEvent *aevent = (XRROutputPropertyNotifyEvent *) event;
210             awire->window = aevent->window;
211             awire->output = aevent->output;
212             awire->atom = aevent->property;
213             awire->timestamp = aevent->timestamp;
214             awire->state = aevent->state;
215             return True;
216         }
217         }
218       }
219     }
220     return False;
221 }
222
223 _X_HIDDEN XExtDisplayInfo *
224 XRRFindDisplay (Display *dpy)
225 {
226     XExtDisplayInfo *dpyinfo;
227     XRandRInfo *xrri;
228     int i, numscreens;
229
230     dpyinfo = XextFindDisplay (&XRRExtensionInfo, dpy);
231     if (!dpyinfo) {
232         dpyinfo = XextAddDisplay (&XRRExtensionInfo, dpy, 
233                                   XRRExtensionName,
234                                   &rr_extension_hooks,
235                                   RRNumberEvents, NULL);
236         numscreens = ScreenCount(dpy);
237         xrri = Xmalloc (sizeof(XRandRInfo) + 
238                                  sizeof(char *) * numscreens);
239         xrri->config = (XRRScreenConfiguration **)(xrri + 1);
240         for(i = 0; i < numscreens; i++) 
241           xrri->config[i] = NULL;
242         xrri->major_version = -1;
243         dpyinfo->data = (char *) xrri;
244     }
245     return dpyinfo;
246 }
247
248 static int
249 XRRCloseDisplay (Display *dpy, XExtCodes *codes)
250 {
251     int i;
252     XRRScreenConfiguration **configs;
253     XExtDisplayInfo *info = XRRFindDisplay (dpy);
254     XRandRInfo *xrri;
255
256     LockDisplay(dpy);
257     /*
258      * free cached data
259      */
260     if (XextHasExtension(info)) {
261         xrri = (XRandRInfo *) info->data;
262         if (xrri) {
263             configs = xrri->config;
264
265             for (i = 0; i < ScreenCount(dpy); i++) {
266                 if (configs[i] != NULL) XFree (configs[i]);
267             }
268             XFree (xrri);
269         }
270     }
271     UnlockDisplay(dpy);
272     return XextRemoveDisplay (&XRRExtensionInfo, dpy);
273 }
274
275 int XRRRootToScreen(Display *dpy, Window root)
276 {
277   int snum;
278   for (snum = 0; snum < ScreenCount(dpy); snum++) {
279     if (RootWindow(dpy, snum) == root) return snum;
280   }
281   return -1;
282 }
283
284
285 Bool XRRQueryExtension (Display *dpy,
286                         int *event_base_return,
287                         int *error_base_return)
288 {
289   XExtDisplayInfo *info = XRRFindDisplay (dpy);
290
291     if (XextHasExtension(info)) {
292         *event_base_return = info->codes->first_event;
293         *error_base_return = info->codes->first_error;
294         return True;
295     } else {
296         return False;
297     }
298 }
299
300 _X_HIDDEN Bool
301 _XRRHasRates (int major, int minor)
302 {
303     return major > 1 || (major == 1 && minor >= 1);
304 }
305
306 Status XRRQueryVersion (Display *dpy,
307                             int     *major_versionp,
308                             int     *minor_versionp)
309 {
310     XExtDisplayInfo *info = XRRFindDisplay (dpy);
311     xRRQueryVersionReply rep;
312     xRRQueryVersionReq  *req;
313     XRandRInfo *xrri;
314
315     RRCheckExtension (dpy, info, 0);
316
317     xrri = (XRandRInfo *) info->data;
318
319     /* 
320      * only get the version information from the server if we don't have it already
321      */
322     if (xrri->major_version == -1) {
323       LockDisplay (dpy);
324       GetReq (RRQueryVersion, req);
325       req->reqType = info->codes->major_opcode;
326       req->randrReqType = X_RRQueryVersion;
327       req->majorVersion = RANDR_MAJOR;
328       req->minorVersion = RANDR_MINOR;
329       if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) {
330         UnlockDisplay (dpy);
331         SyncHandle ();
332         return 0;
333       }
334       xrri->major_version = rep.majorVersion;
335       xrri->minor_version = rep.minorVersion;
336       xrri->has_rates = _XRRHasRates (xrri->major_version, xrri->minor_version);
337       UnlockDisplay (dpy);
338       SyncHandle ();
339     }
340     *major_versionp = xrri->major_version;
341     *minor_versionp = xrri->minor_version;
342     return 1;
343 }
344
345 _X_HIDDEN Bool
346 _XRRVersionHandler (Display         *dpy,
347                         xReply      *rep,
348                         char        *buf,
349                         int         len,
350                         XPointer    data)
351 {
352     xRRQueryVersionReply        replbuf;
353     xRRQueryVersionReply        *repl;
354     _XRRVersionState    *state = (_XRRVersionState *) data;
355
356     if (dpy->last_request_read != state->version_seq)
357         return False;
358     if (rep->generic.type == X_Error)
359     {
360         state->error = True;
361         return False;
362     }
363     repl = (xRRQueryVersionReply *)
364         _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len,
365                      (SIZEOF(xRRQueryVersionReply) - SIZEOF(xReply)) >> 2,
366                         True);
367     state->major_version = repl->majorVersion;
368     state->minor_version = repl->minorVersion;
369     return True;
370 }
371
372 /* 
373  * in protocol version 0.1, routine added to allow selecting for new events.
374  */
375
376 void XRRSelectInput (Display *dpy, Window window, int mask)
377 {
378     XExtDisplayInfo *info = XRRFindDisplay (dpy);
379     xRRSelectInputReq  *req;
380
381     RRSimpleCheckExtension (dpy, info);
382
383     LockDisplay (dpy);
384     GetReq (RRSelectInput, req);
385     req->reqType = info->codes->major_opcode;
386     req->randrReqType = X_RRSelectInput;
387     req->window = window;
388     req->enable = 0;
389     if (mask) req->enable = mask;
390     UnlockDisplay (dpy);
391     SyncHandle ();
392     return;
393 }
394
395 int XRRUpdateConfiguration(XEvent *event)
396 {
397     XRRScreenChangeNotifyEvent *scevent;
398     XConfigureEvent *rcevent;
399     Display *dpy = event->xany.display;
400     XExtDisplayInfo *info;
401     XRandRInfo *xrri;
402     int snum;
403
404     /* first, see if it is a vanilla configure notify event */
405     if (event->type == ConfigureNotify) {
406         rcevent = (XConfigureEvent *) event;
407         snum = XRRRootToScreen(dpy, rcevent->window);
408         if (snum != -1) {
409             dpy->screens[snum].width   = rcevent->width;
410             dpy->screens[snum].height  = rcevent->height;
411             return 1;
412         }
413     }
414
415     info = XRRFindDisplay(dpy);
416     RRCheckExtension (dpy, info, 0);
417
418     switch (event->type - info->codes->first_event) {
419     case RRScreenChangeNotify:
420         scevent = (XRRScreenChangeNotifyEvent *) event;
421         snum = XRRRootToScreen(dpy, 
422                                ((XRRScreenChangeNotifyEvent *) event)->root);
423         if (scevent->rotation & (RR_Rotate_90 | RR_Rotate_270)) {
424                 dpy->screens[snum].width   = scevent->height;
425                 dpy->screens[snum].height  = scevent->width;
426                 dpy->screens[snum].mwidth  = scevent->mheight;
427                 dpy->screens[snum].mheight = scevent->mwidth;
428         } else {
429                 dpy->screens[snum].width   = scevent->width;
430                 dpy->screens[snum].height  = scevent->height;
431                 dpy->screens[snum].mwidth  = scevent->mwidth;
432                 dpy->screens[snum].mheight = scevent->mheight;
433         }
434         XRenderSetSubpixelOrder (dpy, snum, scevent->subpixel_order);
435         break;
436     default:
437         return 0;
438     }
439     xrri = (XRandRInfo *) info->data;
440     /* 
441      * so the next time someone wants some data, it will be fetched; 
442      * it might be better to force the round trip immediately, but 
443      * I dislike pounding the server simultaneously when not necessary
444      */
445     if (xrri->config[snum] != NULL) {
446         XFree (xrri->config[snum]);
447         xrri->config[snum] = NULL;
448     }
449     return 1;
450 }