/* * $XFree86: xc/lib/Xrandr/Xrandr.c,v 1.13tsi Exp $ * * Copyright © 2000 Compaq Computer Corporation, Inc. * Copyright © 2002 Hewlett Packard Company, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Compaq or HP not be used in advertising * or publicity pertaining to distribution of the software without specific, * written prior permission. HP makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Jim Gettys, HP Labs, HP. */ #ifdef HAVE_CONFIG_H #include #endif #include #include /* we need to be able to manipulate the Display structure on events */ #include #include #include #include "Xrandrint.h" XExtensionInfo XRRExtensionInfo; char XRRExtensionName[] = RANDR_NAME; static Bool XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire); static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire); static XRRScreenConfiguration *_XRRGetScreenInfo (Display *dpy, Window window); static int XRRCloseDisplay (Display *dpy, XExtCodes *codes); static /* const */ XExtensionHooks rr_extension_hooks = { NULL, /* create_gc */ NULL, /* copy_gc */ NULL, /* flush_gc */ NULL, /* free_gc */ NULL, /* create_font */ NULL, /* free_font */ XRRCloseDisplay, /* close_display */ XRRWireToEvent, /* wire_to_event */ XRREventToWire, /* event_to_wire */ NULL, /* error */ NULL, /* error_string */ }; static Bool XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire) { XExtDisplayInfo *info = XRRFindDisplay(dpy); XRRScreenChangeNotifyEvent *aevent; xRRScreenChangeNotifyEvent *awire; RRCheckExtension(dpy, info, False); switch ((wire->u.u.type & 0x7F) - info->codes->first_event) { case RRScreenChangeNotify: awire = (xRRScreenChangeNotifyEvent *) wire; aevent = (XRRScreenChangeNotifyEvent *) event; aevent->type = awire->type & 0x7F; aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); aevent->send_event = (awire->type & 0x80) != 0; aevent->display = dpy; aevent->window = awire->window; aevent->root = awire->root; aevent->timestamp = awire->timestamp; aevent->config_timestamp = awire->configTimestamp; aevent->size_index = awire->sizeID; aevent->subpixel_order = awire->subpixelOrder; aevent->rotation = awire->rotation; aevent->width = awire->widthInPixels; aevent->height = awire->heightInPixels; aevent->mwidth = awire->widthInMillimeters; aevent->mheight = awire->heightInMillimeters; return True; } return False; } static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire) { XExtDisplayInfo *info = XRRFindDisplay(dpy); XRRScreenChangeNotifyEvent *aevent; xRRScreenChangeNotifyEvent *awire; RRCheckExtension(dpy, info, False); switch ((event->type & 0x7F) - info->codes->first_event) { case RRScreenChangeNotify: awire = (xRRScreenChangeNotifyEvent *) wire; aevent = (XRRScreenChangeNotifyEvent *) event; awire->type = aevent->type | (aevent->send_event ? 0x80 : 0); awire->rotation = (CARD8) aevent->rotation; awire->sequenceNumber = aevent->serial & 0xFFFF; awire->timestamp = aevent->timestamp; awire->configTimestamp = aevent->config_timestamp; awire->root = aevent->root; awire->window = aevent->window; awire->sizeID = aevent->size_index; awire->subpixelOrder = aevent->subpixel_order; awire->widthInPixels = aevent->width; awire->heightInPixels = aevent->height; awire->widthInMillimeters = aevent->mwidth; awire->heightInMillimeters = aevent->mheight; return True; } return False; } XExtDisplayInfo * XRRFindDisplay (Display *dpy) { XExtDisplayInfo *dpyinfo; XRandRInfo *xrri; int i, numscreens; dpyinfo = XextFindDisplay (&XRRExtensionInfo, dpy); if (!dpyinfo) { dpyinfo = XextAddDisplay (&XRRExtensionInfo, dpy, XRRExtensionName, &rr_extension_hooks, RRNumberEvents, 0); numscreens = ScreenCount(dpy); xrri = Xmalloc (sizeof(XRandRInfo) + sizeof(char *) * numscreens); xrri->config = (XRRScreenConfiguration **)(xrri + 1); for(i = 0; i < numscreens; i++) xrri->config[i] = NULL; xrri->major_version = -1; dpyinfo->data = (char *) xrri; } return dpyinfo; } static int XRRCloseDisplay (Display *dpy, XExtCodes *codes) { int i; XRRScreenConfiguration **configs; XExtDisplayInfo *info = XRRFindDisplay (dpy); XRandRInfo *xrri; LockDisplay(dpy); /* * free cached data */ if (XextHasExtension(info)) { xrri = (XRandRInfo *) info->data; if (xrri) { configs = xrri->config; for (i = 0; i < ScreenCount(dpy); i++) { if (configs[i] != NULL) XFree (configs[i]); } XFree (xrri); } } UnlockDisplay(dpy); return XextRemoveDisplay (&XRRExtensionInfo, dpy); } Rotation XRRConfigRotations(XRRScreenConfiguration *config, Rotation *current_rotation) { *current_rotation = config->current_rotation; return config->rotations; } XRRScreenSize *XRRConfigSizes(XRRScreenConfiguration *config, int *nsizes) { *nsizes = config->nsizes; return config->sizes; } short *XRRConfigRates (XRRScreenConfiguration *config, int sizeID, int *nrates) { short *r = config->rates; int nents = config->nrates; /* Skip over the intervening rate lists */ while (sizeID > 0 && nents > 0) { int i = (*r + 1); r += i; nents -= i; sizeID--; } if (!nents) { *nrates = 0; return 0; } *nrates = (int) *r; return r + 1; } Time XRRConfigTimes (XRRScreenConfiguration *config, Time *config_timestamp) { *config_timestamp = config->config_timestamp; return config->timestamp; } SizeID XRRConfigCurrentConfiguration (XRRScreenConfiguration *config, Rotation *rotation) { *rotation = (Rotation) config->current_rotation; return (SizeID) config->current_size; } short XRRConfigCurrentRate (XRRScreenConfiguration *config) { return config->current_rate; } /* * Go get the screen configuration data and salt it away for future use; * returns NULL if extension not supported */ static XRRScreenConfiguration *_XRRValidateCache (Display *dpy, int screen) { XExtDisplayInfo *info = XRRFindDisplay (dpy); XRRScreenConfiguration **configs; XRandRInfo *xrri; if (XextHasExtension(info)) { xrri = (XRandRInfo *) info->data; configs = xrri->config; if (configs[screen] == NULL) configs[screen] = _XRRGetScreenInfo (dpy, RootWindow(dpy, screen)); return configs[screen]; } else { return NULL; } } /* given a screen, return the information from the (possibly) cached data */ Rotation XRRRotations(Display *dpy, int screen, Rotation *current_rotation) { XRRScreenConfiguration *config; Rotation cr; LockDisplay(dpy); if ((config = _XRRValidateCache(dpy, screen))) { *current_rotation = config->current_rotation; cr = config->rotations; UnlockDisplay(dpy); return cr; } else { UnlockDisplay(dpy); *current_rotation = RR_Rotate_0; return 0; /* no rotations supported */ } } /* given a screen, return the information from the (possibly) cached data */ XRRScreenSize *XRRSizes(Display *dpy, int screen, int *nsizes) { XRRScreenConfiguration *config; XRRScreenSize *sizes; LockDisplay(dpy); if ((config = _XRRValidateCache(dpy, screen))) { *nsizes = config->nsizes; sizes = config->sizes; UnlockDisplay(dpy); return sizes; } else { UnlockDisplay(dpy); *nsizes = 0; return NULL; } } short *XRRRates (Display *dpy, int screen, int sizeID, int *nrates) { XRRScreenConfiguration *config; short *rates; LockDisplay(dpy); if ((config = _XRRValidateCache(dpy, screen))) { rates = XRRConfigRates (config, sizeID, nrates); UnlockDisplay(dpy); return rates; } else { UnlockDisplay(dpy); *nrates = 0; return NULL; } } /* given a screen, return the information from the (possibly) cached data */ Time XRRTimes (Display *dpy, int screen, Time *config_timestamp) { XRRScreenConfiguration *config; Time ts; LockDisplay(dpy); if ((config = _XRRValidateCache(dpy, screen))) { *config_timestamp = config->config_timestamp; ts = config->timestamp; UnlockDisplay(dpy); return ts; } else { UnlockDisplay(dpy); return CurrentTime; } } int XRRRootToScreen(Display *dpy, Window root) { int snum; for (snum = 0; snum < ScreenCount(dpy); snum++) { if (RootWindow(dpy, snum) == root) return snum; } return -1; } Bool XRRQueryExtension (Display *dpy, int *event_basep, int *error_basep) { XExtDisplayInfo *info = XRRFindDisplay (dpy); if (XextHasExtension(info)) { *event_basep = info->codes->first_event; *error_basep = info->codes->first_error; return True; } else { return False; } } static Bool _XRRHasRates (int major, int minor) { return major > 1 || (major == 1 && minor >= 1); } Status XRRQueryVersion (Display *dpy, int *major_versionp, int *minor_versionp) { XExtDisplayInfo *info = XRRFindDisplay (dpy); xRRQueryVersionReply rep; xRRQueryVersionReq *req; XRandRInfo *xrri; RRCheckExtension (dpy, info, 0); xrri = (XRandRInfo *) info->data; /* * only get the version information from the server if we don't have it already */ if (xrri->major_version == -1) { LockDisplay (dpy); GetReq (RRQueryVersion, req); req->reqType = info->codes->major_opcode; req->randrReqType = X_RRQueryVersion; req->majorVersion = RANDR_MAJOR; req->minorVersion = RANDR_MINOR; if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) { UnlockDisplay (dpy); SyncHandle (); return 0; } xrri->major_version = rep.majorVersion; xrri->minor_version = rep.minorVersion; xrri->has_rates = _XRRHasRates (xrri->major_version, xrri->minor_version); UnlockDisplay (dpy); SyncHandle (); } *major_versionp = xrri->major_version; *minor_versionp = xrri->minor_version; return 1; } typedef struct _randrVersionState { unsigned long version_seq; Bool error; int major_version; int minor_version; } _XRRVersionState; static Bool _XRRVersionHandler (Display *dpy, xReply *rep, char *buf, int len, XPointer data) { xRRQueryVersionReply replbuf; xRRQueryVersionReply *repl; _XRRVersionState *state = (_XRRVersionState *) data; if (dpy->last_request_read != state->version_seq) return False; if (rep->generic.type == X_Error) { state->error = True; return False; } repl = (xRRQueryVersionReply *) _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, (SIZEOF(xRRQueryVersionReply) - SIZEOF(xReply)) >> 2, True); state->major_version = repl->majorVersion; state->minor_version = repl->minorVersion; return True; } /* need a version that does not hold the display lock */ static XRRScreenConfiguration *_XRRGetScreenInfo (Display *dpy, Window window) { XExtDisplayInfo *info = XRRFindDisplay(dpy); xRRGetScreenInfoReply rep; xRRGetScreenInfoReq *req; _XAsyncHandler async; _XRRVersionState async_state; int nbytes, nbytesRead, rbytes; int i; xScreenSizes size; struct _XRRScreenConfiguration *scp; XRRScreenSize *ssp; short *rates; xRRQueryVersionReq *vreq; XRandRInfo *xrri; Bool getting_version = False; RRCheckExtension (dpy, info, 0); xrri = (XRandRInfo *) info->data; if (xrri->major_version == -1) { /* hide a version query in the request */ GetReq (RRQueryVersion, vreq); vreq->reqType = info->codes->major_opcode; vreq->randrReqType = X_RRQueryVersion; vreq->majorVersion = RANDR_MAJOR; vreq->minorVersion = RANDR_MINOR; async_state.version_seq = dpy->request; async_state.error = False; async.next = dpy->async_handlers; async.handler = _XRRVersionHandler; async.data = (XPointer) &async_state; dpy->async_handlers = &async; getting_version = True; } GetReq (RRGetScreenInfo, req); req->reqType = info->codes->major_opcode; req->randrReqType = X_RRGetScreenInfo; req->window = window; if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) { if (getting_version) DeqAsyncHandler (dpy, &async); SyncHandle (); return NULL; } if (getting_version) { DeqAsyncHandler (dpy, &async); if (async_state.error) { SyncHandle(); } xrri->major_version = async_state.major_version; xrri->minor_version = async_state.minor_version; xrri->has_rates = _XRRHasRates (xrri->minor_version, xrri->major_version); } /* * Make the reply compatible with v1.1 */ if (!xrri->has_rates) { rep.rate = 0; rep.nrateEnts = 0; } nbytes = (long) rep.length << 2; nbytesRead = (long) (rep.nSizes * SIZEOF (xScreenSizes) + ((rep.nrateEnts + 1)& ~1) * 2 /* SIZEOF (CARD16) */); /* * first we must compute how much space to allocate for * randr library's use; we'll allocate the structures in a single * allocation, on cleanlyness grounds. */ rbytes = sizeof (XRRScreenConfiguration) + (rep.nSizes * sizeof (XRRScreenSize) + rep.nrateEnts * sizeof (int)); scp = (struct _XRRScreenConfiguration *) Xmalloc(rbytes); if (scp == NULL) { _XEatData (dpy, (unsigned long) nbytes); SyncHandle (); return NULL; } ssp = (XRRScreenSize *)(scp + 1); rates = (short *) (ssp + rep.nSizes); /* set up the screen configuration structure */ scp->screen = ScreenOfDisplay (dpy, XRRRootToScreen(dpy, rep.root)); scp->sizes = ssp; scp->rates = rates; scp->rotations = rep.setOfRotations; scp->current_size = rep.sizeID; scp->current_rate = rep.rate; scp->current_rotation = rep.rotation; scp->timestamp = rep.timestamp; scp->config_timestamp = rep.configTimestamp; scp->nsizes = rep.nSizes; scp->nrates = rep.nrateEnts; /* * Time to unpack the data from the server. */ /* * First the size information */ for (i = 0; i < rep.nSizes; i++) { _XReadPad (dpy, (char *) &size, SIZEOF (xScreenSizes)); ssp[i].width = size.widthInPixels; ssp[i].height = size.heightInPixels; ssp[i].mwidth = size.widthInMillimeters; ssp[i].mheight = size.heightInMillimeters; } /* * And the rates */ _XRead16Pad (dpy, rates, 2 /* SIZEOF (CARD16) */ * rep.nrateEnts); /* * Skip any extra data */ if (nbytes > nbytesRead) _XEatData (dpy, (unsigned long) (nbytes - nbytesRead)); return (XRRScreenConfiguration *)(scp); } XRRScreenConfiguration *XRRGetScreenInfo (Display *dpy, Window window) { XRRScreenConfiguration *config; XRRFindDisplay(dpy); LockDisplay (dpy); config = _XRRGetScreenInfo(dpy, window); UnlockDisplay (dpy); SyncHandle (); return config; } void XRRFreeScreenConfigInfo (XRRScreenConfiguration *config) { Xfree (config); } /* * in protocol version 0.1, routine added to allow selecting for new events. */ void XRRSelectInput (Display *dpy, Window window, int mask) { XExtDisplayInfo *info = XRRFindDisplay (dpy); xRRSelectInputReq *req; RRSimpleCheckExtension (dpy, info); LockDisplay (dpy); GetReq (RRSelectInput, req); req->reqType = info->codes->major_opcode; req->randrReqType = X_RRSelectInput; req->window = window; req->enable = 0; if (mask) req->enable = mask; UnlockDisplay (dpy); SyncHandle (); return; } Status XRRSetScreenConfigAndRate (Display *dpy, XRRScreenConfiguration *config, Drawable draw, int size_index, Rotation rotation, short rate, Time timestamp) { XExtDisplayInfo *info = XRRFindDisplay (dpy); xRRSetScreenConfigReply rep; XRandRInfo *xrri; int major, minor; RRCheckExtension (dpy, info, 0); /* Make sure has_rates is set */ if (!XRRQueryVersion (dpy, &major, &minor)) return 0; LockDisplay (dpy); xrri = (XRandRInfo *) info->data; if (xrri->has_rates) { xRRSetScreenConfigReq *req; GetReq (RRSetScreenConfig, req); req->reqType = info->codes->major_opcode; req->randrReqType = X_RRSetScreenConfig; req->drawable = draw; req->sizeID = size_index; req->rotation = rotation; req->timestamp = timestamp; req->configTimestamp = config->config_timestamp; req->rate = rate; } else { xRR1_0SetScreenConfigReq *req; GetReq (RR1_0SetScreenConfig, req); req->reqType = info->codes->major_opcode; req->randrReqType = X_RRSetScreenConfig; req->drawable = draw; req->sizeID = size_index; req->rotation = rotation; req->timestamp = timestamp; req->configTimestamp = config->config_timestamp; } (void) _XReply (dpy, (xReply *) &rep, 0, xTrue); if (rep.status == RRSetConfigSuccess) { /* if we succeed, set our view of reality to what we set it to */ config->config_timestamp = rep.newConfigTimestamp; config->timestamp = rep.newTimestamp; config->screen = ScreenOfDisplay (dpy, XRRRootToScreen(dpy, rep.root)); config->current_size = size_index; config->current_rotation = rotation; } UnlockDisplay (dpy); SyncHandle (); return(rep.status); } Status XRRSetScreenConfig (Display *dpy, XRRScreenConfiguration *config, Drawable draw, int size_index, Rotation rotation, Time timestamp) { return XRRSetScreenConfigAndRate (dpy, config, draw, size_index, rotation, 0, timestamp); } int XRRUpdateConfiguration(XEvent *event) { XRRScreenChangeNotifyEvent *scevent; XConfigureEvent *rcevent; Display *dpy = event->xany.display; XExtDisplayInfo *info; XRandRInfo *xrri; int snum; /* first, see if it is a vanilla configure notify event */ if (event->type == ConfigureNotify) { rcevent = (XConfigureEvent *) event; snum = XRRRootToScreen(dpy, rcevent->window); dpy->screens[snum].width = rcevent->width; dpy->screens[snum].height = rcevent->height; return 1; } info = XRRFindDisplay(dpy); RRCheckExtension (dpy, info, 0); switch (event->type - info->codes->first_event) { case RRScreenChangeNotify: scevent = (XRRScreenChangeNotifyEvent *) event; snum = XRRRootToScreen(dpy, ((XRRScreenChangeNotifyEvent *) event)->root); if (scevent->rotation & (RR_Rotate_90 | RR_Rotate_270)) { dpy->screens[snum].width = scevent->height; dpy->screens[snum].height = scevent->width; dpy->screens[snum].mwidth = scevent->mheight; dpy->screens[snum].mheight = scevent->mwidth; } else { dpy->screens[snum].width = scevent->width; dpy->screens[snum].height = scevent->height; dpy->screens[snum].mwidth = scevent->mwidth; dpy->screens[snum].mheight = scevent->mheight; } XRenderSetSubpixelOrder (dpy, snum, scevent->subpixel_order); break; default: return 0; } xrri = (XRandRInfo *) info->data; /* * so the next time someone wants some data, it will be fetched; * it might be better to force the round trip immediately, but * I dislike pounding the server simultaneously when not necessary */ if (xrri->config[snum] != NULL) { XFree (xrri->config[snum]); xrri->config[snum] = NULL; } return 1; }