/* * $Id$ * * Copyright © 2000 SuSE, 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 SuSE not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. SuSE makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE * 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: Keith Packard, SuSE, Inc. */ #include "Xrenderint.h" XRenderExtInfo XRenderExtensionInfo; char XRenderExtensionName[] = RENDER_NAME; static int XRenderCloseDisplay (Display *dpy, XExtCodes *codes); /* * XRenderExtFindDisplay - look for a display in this extension; keeps a * cache of the most-recently used for efficiency. (Replaces * XextFindDisplay.) */ static XRenderExtDisplayInfo * XRenderExtFindDisplay (XRenderExtInfo *extinfo, Display *dpy) { XRenderExtDisplayInfo *dpyinfo; /* * see if this was the most recently accessed display */ if ((dpyinfo = extinfo->cur) && dpyinfo->display == dpy) return dpyinfo; /* * look for display in list */ _XLockMutex(_Xglobal_lock); for (dpyinfo = extinfo->head; dpyinfo; dpyinfo = dpyinfo->next) { if (dpyinfo->display == dpy) { extinfo->cur = dpyinfo; /* cache most recently used */ _XUnlockMutex(_Xglobal_lock); return dpyinfo; } } _XUnlockMutex(_Xglobal_lock); return NULL; } /* * If the server is missing support for any of the required depths on * any screen, tell the application that Render is not present. */ #define DEPTH_MASK(d) (1 << ((d) - 1)) /* * Render requires support for depth 1, 4, 8, 24 and 32 pixmaps */ #define REQUIRED_DEPTHS (DEPTH_MASK(1) | \ DEPTH_MASK(4) | \ DEPTH_MASK(8) | \ DEPTH_MASK(24) | \ DEPTH_MASK(32)) typedef struct _DepthCheckRec { struct _DepthCheckRec *next; Display *dpy; CARD32 missing; unsigned long serial; } DepthCheckRec, *DepthCheckPtr; static DepthCheckPtr depthChecks; static int XRenderDepthCheckErrorHandler (Display *dpy, XErrorEvent *evt) { if (evt->request_code == X_CreatePixmap && evt->error_code == BadValue) { DepthCheckPtr d; _XLockMutex(_Xglobal_lock); for (d = depthChecks; d; d = d->next) if (d->dpy == dpy) { if ((long) (evt->serial - d->serial) >= 0) d->missing |= DEPTH_MASK(evt->resourceid); break; } _XUnlockMutex (_Xglobal_lock); } } static Bool XRenderHasDepths (Display *dpy) { int s; for (s = 0; s < ScreenCount (dpy); s++) { CARD32 depths = 0; CARD32 missing; Screen *scr = ScreenOfDisplay (dpy, s); int d; for (d = 0; d < scr->ndepths; d++) depths |= DEPTH_MASK(scr->depths[d].depth); missing = ~depths & REQUIRED_DEPTHS; if (missing) { DepthCheckRec dc, **dp; XErrorHandler previousHandler; /* * Ok, this is ugly. It should be sufficient at this * point to just return False, but Xinerama is broken at * this point and only advertises depths which have an * associated visual. Of course, the other depths still * work, but the only way to find out is to try them. */ dc.dpy = dpy; dc.missing = 0; dc.serial = XNextRequest (dpy); _XLockMutex(_Xglobal_lock); dc.next = depthChecks; depthChecks = &dc; _XUnlockMutex (_Xglobal_lock); /* * I suspect this is not really thread safe, but Xlib doesn't * provide a lot of options here */ previousHandler = XSetErrorHandler (XRenderDepthCheckErrorHandler); /* * Try each missing depth and see if pixmap creation succeeds */ for (d = 1; d <= 32; d++) /* don't check depth 1 == Xcursor recurses... */ if ((missing & DEPTH_MASK(d)) && d != 1) { Pixmap p; p = XCreatePixmap (dpy, RootWindow (dpy, s), 1, 1, d); XFreePixmap (dpy, p); } XSync (dpy, False); XSetErrorHandler (previousHandler); /* * Unhook from the list of depth check records */ _XLockMutex(_Xglobal_lock); for (dp = &depthChecks; *dp; dp = &(*dp)->next) { if (*dp == &dc) { *dp = dc.next; break; } } _XUnlockMutex (_Xglobal_lock); if (dc.missing) return False; } } return True; } /* * XRenderExtAddDisplay - add a display to this extension. (Replaces * XextAddDisplay) */ static XRenderExtDisplayInfo * XRenderExtAddDisplay (XRenderExtInfo *extinfo, Display *dpy, char *ext_name) { XRenderExtDisplayInfo *dpyinfo; dpyinfo = (XRenderExtDisplayInfo *) Xmalloc (sizeof (XRenderExtDisplayInfo)); if (!dpyinfo) return NULL; dpyinfo->display = dpy; dpyinfo->info = NULL; if (XRenderHasDepths (dpy)) dpyinfo->codes = XInitExtension (dpy, ext_name); else dpyinfo->codes = NULL; /* * if the server has the extension, then we can initialize the * appropriate function vectors */ if (dpyinfo->codes) { XESetCloseDisplay (dpy, dpyinfo->codes->extension, XRenderCloseDisplay); } else { /* The server doesn't have this extension. * Use a private Xlib-internal extension to hang the close_display * hook on so that the "cache" (extinfo->cur) is properly cleaned. * (XBUG 7955) */ XExtCodes *codes = XAddExtension(dpy); if (!codes) { XFree(dpyinfo); return NULL; } XESetCloseDisplay (dpy, codes->extension, XRenderCloseDisplay); } /* * now, chain it onto the list */ _XLockMutex(_Xglobal_lock); dpyinfo->next = extinfo->head; extinfo->head = dpyinfo; extinfo->cur = dpyinfo; extinfo->ndisplays++; _XUnlockMutex(_Xglobal_lock); return dpyinfo; } /* * XRenderExtRemoveDisplay - remove the indicated display from the * extension object. (Replaces XextRemoveDisplay.) */ static int XRenderExtRemoveDisplay (XRenderExtInfo *extinfo, Display *dpy) { XRenderExtDisplayInfo *dpyinfo, *prev; /* * locate this display and its back link so that it can be removed */ _XLockMutex(_Xglobal_lock); prev = NULL; for (dpyinfo = extinfo->head; dpyinfo; dpyinfo = dpyinfo->next) { if (dpyinfo->display == dpy) break; prev = dpyinfo; } if (!dpyinfo) { _XUnlockMutex(_Xglobal_lock); return 0; /* hmm, actually an error */ } /* * remove the display from the list; handles going to zero */ if (prev) prev->next = dpyinfo->next; else extinfo->head = dpyinfo->next; extinfo->ndisplays--; if (dpyinfo == extinfo->cur) extinfo->cur = NULL; /* flush cache */ _XUnlockMutex(_Xglobal_lock); Xfree ((char *) dpyinfo); return 1; } XRenderExtDisplayInfo * XRenderFindDisplay (Display *dpy) { XRenderExtDisplayInfo *dpyinfo; dpyinfo = XRenderExtFindDisplay (&XRenderExtensionInfo, dpy); if (!dpyinfo) dpyinfo = XRenderExtAddDisplay (&XRenderExtensionInfo, dpy, XRenderExtensionName); return dpyinfo; } static int XRenderCloseDisplay (Display *dpy, XExtCodes *codes) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); if (info->info) XFree (info->info); return XRenderExtRemoveDisplay (&XRenderExtensionInfo, dpy); } /**************************************************************************** * * * Render public interfaces * * * ****************************************************************************/ Bool XRenderQueryExtension (Display *dpy, int *event_basep, int *error_basep) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); if (RenderHasExtension(info)) { *event_basep = info->codes->first_event; *error_basep = info->codes->first_error; return True; } else { return False; } } Status XRenderQueryVersion (Display *dpy, int *major_versionp, int *minor_versionp) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); XRenderInfo *xri; if (!RenderHasExtension (info)) return 0; if (!XRenderQueryFormats (dpy)) return 0; xri = info->info; *major_versionp = xri->major_version; *minor_versionp = xri->minor_version; return 1; } static XRenderPictFormat * _XRenderFindFormat (XRenderInfo *xri, PictFormat format) { int nf; for (nf = 0; nf < xri->nformat; nf++) if (xri->format[nf].id == format) return &xri->format[nf]; return 0; } static Visual * _XRenderFindVisual (Display *dpy, VisualID vid) { return _XVIDtoVisual (dpy, vid); } typedef struct _renderVersionState { unsigned long version_seq; Bool error; int major_version; int minor_version; } _XrenderVersionState; static Bool _XRenderVersionHandler (Display *dpy, xReply *rep, char *buf, int len, XPointer data) { xRenderQueryVersionReply replbuf; xRenderQueryVersionReply *repl; _XrenderVersionState *state = (_XrenderVersionState *) data; if (dpy->last_request_read != state->version_seq) return False; if (rep->generic.type == X_Error) { state->error = True; return False; } repl = (xRenderQueryVersionReply *) _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, (SIZEOF(xRenderQueryVersionReply) - SIZEOF(xReply)) >> 2, True); state->major_version = repl->majorVersion; state->minor_version = repl->minorVersion; return True; } Status XRenderQueryFormats (Display *dpy) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); _XAsyncHandler async; _XrenderVersionState async_state; xRenderQueryVersionReq *vreq; xRenderQueryPictFormatsReply rep; xRenderQueryPictFormatsReq *req; XRenderInfo *xri; XRenderPictFormat *format; XRenderScreen *screen; XRenderDepth *depth; XRenderVisual *visual; xPictFormInfo *xFormat; xPictScreen *xScreen; xPictDepth *xDepth; xPictVisual *xVisual; CARD32 *xSubpixel; void *xData; int nf, ns, nd, nv; int rlength; int nbytes; RenderCheckExtension (dpy, info, 0); LockDisplay (dpy); if (info->info) { UnlockDisplay (dpy); return 1; } GetReq (RenderQueryVersion, vreq); vreq->reqType = info->codes->major_opcode; vreq->renderReqType = X_RenderQueryVersion; vreq->majorVersion = RENDER_MAJOR; vreq->minorVersion = RENDER_MINOR; async_state.version_seq = dpy->request; async_state.error = False; async.next = dpy->async_handlers; async.handler = _XRenderVersionHandler; async.data = (XPointer) &async_state; dpy->async_handlers = &async; GetReq (RenderQueryPictFormats, req); req->reqType = info->codes->major_opcode; req->renderReqType = X_RenderQueryPictFormats; if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) { DeqAsyncHandler (dpy, &async); UnlockDisplay (dpy); SyncHandle (); return 0; } DeqAsyncHandler (dpy, &async); if (async_state.error) { UnlockDisplay(dpy); SyncHandle(); return 0; } /* * Check for the lack of sub-pixel data */ if (async_state.major_version == 0 && async_state.minor_version < 6) rep.numSubpixel = 0; xri = (XRenderInfo *) Xmalloc (sizeof (XRenderInfo) + rep.numFormats * sizeof (XRenderPictFormat) + rep.numScreens * sizeof (XRenderScreen) + rep.numDepths * sizeof (XRenderDepth) + rep.numVisuals * sizeof (XRenderVisual)); xri->major_version = async_state.major_version; xri->minor_version = async_state.minor_version; xri->format = (XRenderPictFormat *) (xri + 1); xri->nformat = rep.numFormats; xri->screen = (XRenderScreen *) (xri->format + rep.numFormats); xri->nscreen = rep.numScreens; xri->depth = (XRenderDepth *) (xri->screen + rep.numScreens); xri->ndepth = rep.numDepths; xri->visual = (XRenderVisual *) (xri->depth + rep.numDepths); xri->nvisual = rep.numVisuals; rlength = (rep.numFormats * sizeof (xPictFormInfo) + rep.numScreens * sizeof (xPictScreen) + rep.numDepths * sizeof (xPictDepth) + rep.numVisuals * sizeof (xPictVisual) + rep.numSubpixel * 4); xData = (void *) Xmalloc (rlength); nbytes = (int) rep.length << 2; if (!xri || !xData || nbytes < rlength) { if (xri) Xfree (xri); if (xData) Xfree (xData); _XEatData (dpy, nbytes); UnlockDisplay (dpy); SyncHandle (); return 0; } _XRead (dpy, (char *) xData, rlength); format = xri->format; xFormat = (xPictFormInfo *) xData; for (nf = 0; nf < rep.numFormats; nf++) { format->id = xFormat->id; format->type = xFormat->type; format->depth = xFormat->depth; format->direct.red = xFormat->direct.red; format->direct.redMask = xFormat->direct.redMask; format->direct.green = xFormat->direct.green; format->direct.greenMask = xFormat->direct.greenMask; format->direct.blue = xFormat->direct.blue; format->direct.blueMask = xFormat->direct.blueMask; format->direct.alpha = xFormat->direct.alpha; format->direct.alphaMask = xFormat->direct.alphaMask; format->colormap = xFormat->colormap; format++; xFormat++; } xScreen = (xPictScreen *) xFormat; screen = xri->screen; depth = xri->depth; visual = xri->visual; for (ns = 0; ns < xri->nscreen; ns++) { screen->depths = depth; screen->ndepths = xScreen->nDepth; screen->fallback = _XRenderFindFormat (xri, xScreen->fallback); screen->subpixel = SubPixelUnknown; xDepth = (xPictDepth *) (xScreen + 1); for (nd = 0; nd < screen->ndepths; nd++) { depth->depth = xDepth->depth; depth->nvisuals = xDepth->nPictVisuals; depth->visuals = visual; xVisual = (xPictVisual *) (xDepth + 1); for (nv = 0; nv < depth->nvisuals; nv++) { visual->visual = _XRenderFindVisual (dpy, xVisual->visual); visual->format = _XRenderFindFormat (xri, xVisual->format); visual++; xVisual++; } depth++; xDepth = (xPictDepth *) xVisual; } screen++; xScreen = (xPictScreen *) xDepth; } xSubpixel = (CARD32 *) xScreen; screen = xri->screen; for (ns = 0; ns < rep.numSubpixel; ns++) { screen->subpixel = *xSubpixel; xSubpixel++; screen++; } info->info = xri; /* * Skip any extra data */ if (nbytes > rlength) _XEatData (dpy, (unsigned long) (nbytes - rlength)); UnlockDisplay (dpy); SyncHandle (); Xfree (xData); return 1; } int XRenderQuerySubpixelOrder (Display *dpy, int screen) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); XRenderInfo *xri; if (!RenderHasExtension (info)) return SubPixelUnknown; if (!XRenderQueryFormats (dpy)) return SubPixelUnknown; xri = info->info; return xri->screen[screen].subpixel; } Bool XRenderSetSubpixelOrder (Display *dpy, int screen, int subpixel) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); XRenderInfo *xri; if (!RenderHasExtension (info)) return False; if (!XRenderQueryFormats (dpy)) return False; xri = info->info; xri->screen[screen].subpixel = subpixel; return True; } XRenderPictFormat * XRenderFindVisualFormat (Display *dpy, _Xconst Visual *visual) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); int nv; XRenderInfo *xri; XRenderVisual *xrv; RenderCheckExtension (dpy, info, 0); if (!XRenderQueryFormats (dpy)) return 0; xri = info->info; for (nv = 0, xrv = xri->visual; nv < xri->nvisual; nv++, xrv++) if (xrv->visual == visual) return xrv->format; return 0; } XRenderPictFormat * XRenderFindFormat (Display *dpy, unsigned long mask, _Xconst XRenderPictFormat *template, int count) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); int nf; XRenderInfo *xri; RenderCheckExtension (dpy, info, 0); if (!XRenderQueryFormats (dpy)) return 0; xri = info->info; for (nf = 0; nf < xri->nformat; nf++) { if (mask & PictFormatID) if (template->id != xri->format[nf].id) continue; if (mask & PictFormatType) if (template->type != xri->format[nf].type) continue; if (mask & PictFormatDepth) if (template->depth != xri->format[nf].depth) continue; if (mask & PictFormatRed) if (template->direct.red != xri->format[nf].direct.red) continue; if (mask & PictFormatRedMask) if (template->direct.redMask != xri->format[nf].direct.redMask) continue; if (mask & PictFormatGreen) if (template->direct.green != xri->format[nf].direct.green) continue; if (mask & PictFormatGreenMask) if (template->direct.greenMask != xri->format[nf].direct.greenMask) continue; if (mask & PictFormatBlue) if (template->direct.blue != xri->format[nf].direct.blue) continue; if (mask & PictFormatBlueMask) if (template->direct.blueMask != xri->format[nf].direct.blueMask) continue; if (mask & PictFormatAlpha) if (template->direct.alpha != xri->format[nf].direct.alpha) continue; if (mask & PictFormatAlphaMask) if (template->direct.alphaMask != xri->format[nf].direct.alphaMask) continue; if (mask & PictFormatColormap) if (template->colormap != xri->format[nf].colormap) continue; if (count-- == 0) return &xri->format[nf]; } return 0; } XRenderPictFormat * XRenderFindStandardFormat (Display *dpy, int format) { static struct { XRenderPictFormat templ; unsigned long mask; } standardFormats[PictStandardNUM] = { /* PictStandardARGB32 */ { { 0, /* id */ PictTypeDirect, /* type */ 32, /* depth */ { /* direct */ 16, /* direct.red */ 0xff, /* direct.redMask */ 8, /* direct.green */ 0xff, /* direct.greenMask */ 0, /* direct.blue */ 0xff, /* direct.blueMask */ 24, /* direct.alpha */ 0xff, /* direct.alphaMask */ }, 0, /* colormap */ }, PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask, }, /* PictStandardRGB24 */ { { 0, /* id */ PictTypeDirect, /* type */ 24, /* depth */ { /* direct */ 16, /* direct.red */ 0xff, /* direct.redMask */ 8, /* direct.green */ 0xff, /* direct.greenMask */ 0, /* direct.blue */ 0xff, /* direct.blueMask */ 0, /* direct.alpha */ 0x00, /* direct.alphaMask */ }, 0, /* colormap */ }, PictFormatType | PictFormatDepth | PictFormatRed | PictFormatRedMask | PictFormatGreen | PictFormatGreenMask | PictFormatBlue | PictFormatBlueMask | PictFormatAlphaMask, }, /* PictStandardA8 */ { { 0, /* id */ PictTypeDirect, /* type */ 8, /* depth */ { /* direct */ 0, /* direct.red */ 0x00, /* direct.redMask */ 0, /* direct.green */ 0x00, /* direct.greenMask */ 0, /* direct.blue */ 0x00, /* direct.blueMask */ 0, /* direct.alpha */ 0xff, /* direct.alphaMask */ }, 0, /* colormap */ }, PictFormatType | PictFormatDepth | PictFormatRedMask | PictFormatGreenMask | PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask, }, /* PictStandardA4 */ { { 0, /* id */ PictTypeDirect, /* type */ 4, /* depth */ { /* direct */ 0, /* direct.red */ 0x00, /* direct.redMask */ 0, /* direct.green */ 0x00, /* direct.greenMask */ 0, /* direct.blue */ 0x00, /* direct.blueMask */ 0, /* direct.alpha */ 0x0f, /* direct.alphaMask */ }, 0, /* colormap */ }, PictFormatType | PictFormatDepth | PictFormatRedMask | PictFormatGreenMask | PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask, }, /* PictStandardA1 */ { { 0, /* id */ PictTypeDirect, /* type */ 1, /* depth */ { /* direct */ 0, /* direct.red */ 0x00, /* direct.redMask */ 0, /* direct.green */ 0x00, /* direct.greenMask */ 0, /* direct.blue */ 0x00, /* direct.blueMask */ 0, /* direct.alpha */ 0x01, /* direct.alphaMask */ }, 0, /* colormap */ }, PictFormatType | PictFormatDepth | PictFormatRedMask | PictFormatGreenMask | PictFormatBlueMask | PictFormatAlpha | PictFormatAlphaMask, }, }; if (0 <= format && format < PictStandardNUM) return XRenderFindFormat (dpy, standardFormats[format].mask, &standardFormats[format].templ, 0); return 0; } XIndexValue * XRenderQueryPictIndexValues(Display *dpy, _Xconst XRenderPictFormat *format, int *num) { XRenderExtDisplayInfo *info = XRenderFindDisplay (dpy); xRenderQueryPictIndexValuesReq *req; xRenderQueryPictIndexValuesReply rep; XIndexValue *values; int nbytes, nread, rlength, i; RenderCheckExtension (dpy, info, 0); LockDisplay (dpy); GetReq (RenderQueryPictIndexValues, req); req->reqType = info->codes->major_opcode; req->renderReqType = X_RenderQueryPictIndexValues; req->format = format->id; if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) { UnlockDisplay (dpy); SyncHandle (); return 0; } /* request data length */ nbytes = (long)rep.length << 2; /* bytes of actual data in the request */ nread = rep.numIndexValues * SIZEOF (xIndexValue); /* size of array returned to application */ rlength = rep.numIndexValues * sizeof (XIndexValue); /* allocate returned data */ values = (XIndexValue *)Xmalloc (rlength); if (!values) { _XEatData (dpy, nbytes); UnlockDisplay (dpy); SyncHandle (); return 0; } /* read the values one at a time and convert */ *num = rep.numIndexValues; for(i = 0; i < rep.numIndexValues; i++) { xIndexValue value; _XRead (dpy, (char *) &value, SIZEOF (xIndexValue)); values[i].pixel = value.pixel; values[i].red = value.red; values[i].green = value.green; values[i].blue = value.blue; values[i].alpha = value.alpha; } /* skip any padding */ if(nbytes > nread) { _XEatData (dpy, (unsigned long) (nbytes - nread)); } UnlockDisplay (dpy); SyncHandle (); return values; }