Home | History | Annotate | Download | only in xlib
      1 /**************************************************************************
      2  *
      3  * Copyright 2007 VMware, Inc., Bismarck, ND., USA
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     17  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     20  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     21  *
     22  * The above copyright notice and this permission notice (including the
     23  * next paragraph) shall be included in all copies or substantial portions
     24  * of the Software.
     25  *
     26  *
     27  **************************************************************************/
     28 
     29 /*
     30  * Authors:
     31  *   Keith Whitwell
     32  *   Brian Paul
     33  */
     34 
     35 #include "pipe/p_format.h"
     36 #include "pipe/p_context.h"
     37 #include "util/u_inlines.h"
     38 #include "util/u_format.h"
     39 #include "util/u_math.h"
     40 #include "util/u_memory.h"
     41 
     42 #include "state_tracker/xlibsw_api.h"
     43 #include "xlib_sw_winsys.h"
     44 
     45 #include <X11/Xlib.h>
     46 #include <X11/Xlibint.h>
     47 #include <X11/Xutil.h>
     48 #include <sys/ipc.h>
     49 #include <sys/shm.h>
     50 #include <X11/extensions/XShm.h>
     51 
     52 DEBUG_GET_ONCE_BOOL_OPTION(xlib_no_shm, "XLIB_NO_SHM", FALSE)
     53 
     54 /**
     55  * Display target for Xlib winsys.
     56  * Low-level OS/window system memory buffer
     57  */
     58 struct xlib_displaytarget
     59 {
     60    enum pipe_format format;
     61    unsigned width;
     62    unsigned height;
     63    unsigned stride;
     64 
     65    void *data;
     66    void *mapped;
     67 
     68    Display *display;
     69    Visual *visual;
     70    XImage *tempImage;
     71    GC gc;
     72 
     73    /* This is the last drawable that this display target was presented
     74     * against.  May need to recreate gc, tempImage when this changes??
     75     */
     76    Drawable drawable;
     77 
     78    XShmSegmentInfo shminfo;
     79    Bool shm;  /** Using shared memory images? */
     80 };
     81 
     82 
     83 /**
     84  * Subclass of sw_winsys for Xlib winsys
     85  */
     86 struct xlib_sw_winsys
     87 {
     88    struct sw_winsys base;
     89    Display *display;
     90 };
     91 
     92 
     93 
     94 /** Cast wrapper */
     95 static inline struct xlib_displaytarget *
     96 xlib_displaytarget(struct sw_displaytarget *dt)
     97 {
     98    return (struct xlib_displaytarget *) dt;
     99 }
    100 
    101 
    102 /**
    103  * X Shared Memory Image extension code
    104  */
    105 
    106 static volatile int XErrorFlag = 0;
    107 
    108 /**
    109  * Catches potential Xlib errors.
    110  */
    111 static int
    112 handle_xerror(Display *dpy, XErrorEvent *event)
    113 {
    114    (void) dpy;
    115    (void) event;
    116    XErrorFlag = 1;
    117    return 0;
    118 }
    119 
    120 
    121 static char *
    122 alloc_shm(struct xlib_displaytarget *buf, unsigned size)
    123 {
    124    XShmSegmentInfo *const shminfo = & buf->shminfo;
    125 
    126    shminfo->shmid = -1;
    127    shminfo->shmaddr = (char *) -1;
    128 
    129    shminfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
    130    if (shminfo->shmid < 0) {
    131       return NULL;
    132    }
    133 
    134    shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0);
    135    if (shminfo->shmaddr == (char *) -1) {
    136       shmctl(shminfo->shmid, IPC_RMID, 0);
    137       return NULL;
    138    }
    139 
    140    shminfo->readOnly = False;
    141    return shminfo->shmaddr;
    142 }
    143 
    144 
    145 /**
    146  * Allocate a shared memory XImage back buffer for the given display target.
    147  */
    148 static void
    149 alloc_shm_ximage(struct xlib_displaytarget *xlib_dt,
    150                  struct xlib_drawable *xmb,
    151                  unsigned width, unsigned height)
    152 {
    153    /*
    154     * We have to do a _lot_ of error checking here to be sure we can
    155     * really use the XSHM extension.  It seems different servers trigger
    156     * errors at different points if the extension won't work.  Therefore
    157     * we have to be very careful...
    158     */
    159    int (*old_handler)(Display *, XErrorEvent *);
    160 
    161    xlib_dt->tempImage = XShmCreateImage(xlib_dt->display,
    162                                       xmb->visual,
    163                                       xmb->depth,
    164                                       ZPixmap,
    165                                       NULL,
    166                                       &xlib_dt->shminfo,
    167                                       width, height);
    168    if (xlib_dt->tempImage == NULL) {
    169       shmctl(xlib_dt->shminfo.shmid, IPC_RMID, 0);
    170       xlib_dt->shm = False;
    171       return;
    172    }
    173 
    174 
    175    XErrorFlag = 0;
    176    old_handler = XSetErrorHandler(handle_xerror);
    177    /* This may trigger the X protocol error we're ready to catch: */
    178    XShmAttach(xlib_dt->display, &xlib_dt->shminfo);
    179    XSync(xlib_dt->display, False);
    180 
    181    /* Mark the segment to be destroyed, so that it is automatically destroyed
    182     * when this process dies.  Needs to be after XShmAttach() for *BSD.
    183     */
    184    shmctl(xlib_dt->shminfo.shmid, IPC_RMID, 0);
    185 
    186    if (XErrorFlag) {
    187       /* we are on a remote display, this error is normal, don't print it */
    188       XFlush(xlib_dt->display);
    189       XErrorFlag = 0;
    190       XDestroyImage(xlib_dt->tempImage);
    191       xlib_dt->tempImage = NULL;
    192       xlib_dt->shm = False;
    193       (void) XSetErrorHandler(old_handler);
    194       return;
    195    }
    196 
    197    xlib_dt->shm = True;
    198 }
    199 
    200 
    201 static void
    202 alloc_ximage(struct xlib_displaytarget *xlib_dt,
    203              struct xlib_drawable *xmb,
    204              unsigned width, unsigned height)
    205 {
    206    /* try allocating a shared memory image first */
    207    if (xlib_dt->shm) {
    208       alloc_shm_ximage(xlib_dt, xmb, width, height);
    209       if (xlib_dt->tempImage)
    210          return; /* success */
    211    }
    212 
    213    /* try regular (non-shared memory) image */
    214    xlib_dt->tempImage = XCreateImage(xlib_dt->display,
    215                                    xmb->visual,
    216                                    xmb->depth,
    217                                    ZPixmap, 0,
    218                                    NULL, width, height,
    219                                    8, 0);
    220 }
    221 
    222 static boolean
    223 xlib_is_displaytarget_format_supported(struct sw_winsys *ws,
    224                                        unsigned tex_usage,
    225                                        enum pipe_format format)
    226 {
    227    /* TODO: check visuals or other sensible thing here */
    228    return TRUE;
    229 }
    230 
    231 
    232 static void *
    233 xlib_displaytarget_map(struct sw_winsys *ws,
    234                        struct sw_displaytarget *dt,
    235                        unsigned flags)
    236 {
    237    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    238    xlib_dt->mapped = xlib_dt->data;
    239    return xlib_dt->mapped;
    240 }
    241 
    242 
    243 static void
    244 xlib_displaytarget_unmap(struct sw_winsys *ws,
    245                          struct sw_displaytarget *dt)
    246 {
    247    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    248    xlib_dt->mapped = NULL;
    249 }
    250 
    251 
    252 static void
    253 xlib_displaytarget_destroy(struct sw_winsys *ws,
    254                            struct sw_displaytarget *dt)
    255 {
    256    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    257 
    258    if (xlib_dt->data) {
    259       if (xlib_dt->shminfo.shmid >= 0) {
    260          shmdt(xlib_dt->shminfo.shmaddr);
    261          shmctl(xlib_dt->shminfo.shmid, IPC_RMID, 0);
    262 
    263          xlib_dt->shminfo.shmid = -1;
    264          xlib_dt->shminfo.shmaddr = (char *) -1;
    265 
    266          xlib_dt->data = NULL;
    267          if (xlib_dt->tempImage)
    268             xlib_dt->tempImage->data = NULL;
    269       }
    270       else {
    271          align_free(xlib_dt->data);
    272          if (xlib_dt->tempImage && xlib_dt->tempImage->data == xlib_dt->data) {
    273             xlib_dt->tempImage->data = NULL;
    274          }
    275          xlib_dt->data = NULL;
    276       }
    277    }
    278 
    279    if (xlib_dt->tempImage) {
    280       XDestroyImage(xlib_dt->tempImage);
    281       xlib_dt->tempImage = NULL;
    282    }
    283 
    284    if (xlib_dt->gc)
    285       XFreeGC(xlib_dt->display, xlib_dt->gc);
    286 
    287    FREE(xlib_dt);
    288 }
    289 
    290 
    291 /**
    292  * Display/copy the image in the surface into the X window specified
    293  * by the display target.
    294  */
    295 static void
    296 xlib_sw_display(struct xlib_drawable *xlib_drawable,
    297                 struct sw_displaytarget *dt)
    298 {
    299    static boolean no_swap = 0;
    300    static boolean firsttime = 1;
    301    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    302    Display *display = xlib_dt->display;
    303    XImage *ximage;
    304 
    305    if (firsttime) {
    306       no_swap = getenv("SP_NO_RAST") != NULL;
    307       firsttime = 0;
    308    }
    309 
    310    if (no_swap)
    311       return;
    312 
    313    if (xlib_dt->drawable != xlib_drawable->drawable) {
    314       if (xlib_dt->gc) {
    315          XFreeGC(display, xlib_dt->gc);
    316          xlib_dt->gc = NULL;
    317       }
    318 
    319       if (xlib_dt->tempImage) {
    320          XDestroyImage(xlib_dt->tempImage);
    321          xlib_dt->tempImage = NULL;
    322       }
    323 
    324       xlib_dt->drawable = xlib_drawable->drawable;
    325    }
    326 
    327    if (xlib_dt->tempImage == NULL) {
    328       assert(util_format_get_blockwidth(xlib_dt->format) == 1);
    329       assert(util_format_get_blockheight(xlib_dt->format) == 1);
    330       alloc_ximage(xlib_dt, xlib_drawable,
    331                    xlib_dt->stride / util_format_get_blocksize(xlib_dt->format),
    332                    xlib_dt->height);
    333       if (!xlib_dt->tempImage)
    334          return;
    335    }
    336 
    337    if (xlib_dt->gc == NULL) {
    338       xlib_dt->gc = XCreateGC(display, xlib_drawable->drawable, 0, NULL);
    339       XSetFunction(display, xlib_dt->gc, GXcopy);
    340    }
    341 
    342    if (xlib_dt->shm) {
    343       ximage = xlib_dt->tempImage;
    344       ximage->data = xlib_dt->data;
    345 
    346       /* _debug_printf("XSHM\n"); */
    347       XShmPutImage(xlib_dt->display, xlib_drawable->drawable, xlib_dt->gc,
    348                    ximage, 0, 0, 0, 0, xlib_dt->width, xlib_dt->height, False);
    349    }
    350    else {
    351       /* display image in Window */
    352       ximage = xlib_dt->tempImage;
    353       ximage->data = xlib_dt->data;
    354 
    355       /* check that the XImage has been previously initialized */
    356       assert(ximage->format);
    357       assert(ximage->bitmap_unit);
    358 
    359       /* update XImage's fields */
    360       ximage->width = xlib_dt->width;
    361       ximage->height = xlib_dt->height;
    362       ximage->bytes_per_line = xlib_dt->stride;
    363 
    364       /* _debug_printf("XPUT\n"); */
    365       XPutImage(xlib_dt->display, xlib_drawable->drawable, xlib_dt->gc,
    366                 ximage, 0, 0, 0, 0, xlib_dt->width, xlib_dt->height);
    367    }
    368 
    369    XFlush(xlib_dt->display);
    370 }
    371 
    372 
    373 /**
    374  * Display/copy the image in the surface into the X window specified
    375  * by the display target.
    376  */
    377 static void
    378 xlib_displaytarget_display(struct sw_winsys *ws,
    379                            struct sw_displaytarget *dt,
    380                            void *context_private,
    381                            struct pipe_box *box)
    382 {
    383    struct xlib_drawable *xlib_drawable = (struct xlib_drawable *)context_private;
    384    xlib_sw_display(xlib_drawable, dt);
    385 }
    386 
    387 
    388 static struct sw_displaytarget *
    389 xlib_displaytarget_create(struct sw_winsys *winsys,
    390                           unsigned tex_usage,
    391                           enum pipe_format format,
    392                           unsigned width, unsigned height,
    393                           unsigned alignment,
    394                           const void *front_private,
    395                           unsigned *stride)
    396 {
    397    struct xlib_displaytarget *xlib_dt;
    398    unsigned nblocksy, size;
    399 
    400    xlib_dt = CALLOC_STRUCT(xlib_displaytarget);
    401    if (!xlib_dt)
    402       goto no_xlib_dt;
    403 
    404    xlib_dt->display = ((struct xlib_sw_winsys *)winsys)->display;
    405    xlib_dt->format = format;
    406    xlib_dt->width = width;
    407    xlib_dt->height = height;
    408 
    409    nblocksy = util_format_get_nblocksy(format, height);
    410    xlib_dt->stride = align(util_format_get_stride(format, width), alignment);
    411    size = xlib_dt->stride * nblocksy;
    412 
    413    if (!debug_get_option_xlib_no_shm()) {
    414       xlib_dt->data = alloc_shm(xlib_dt, size);
    415       if (xlib_dt->data) {
    416          xlib_dt->shm = True;
    417       }
    418    }
    419 
    420    if (!xlib_dt->data) {
    421       xlib_dt->data = align_malloc(size, alignment);
    422       if (!xlib_dt->data)
    423          goto no_data;
    424    }
    425 
    426    *stride = xlib_dt->stride;
    427    return (struct sw_displaytarget *)xlib_dt;
    428 
    429 no_data:
    430    FREE(xlib_dt);
    431 no_xlib_dt:
    432    return NULL;
    433 }
    434 
    435 
    436 static struct sw_displaytarget *
    437 xlib_displaytarget_from_handle(struct sw_winsys *winsys,
    438                                const struct pipe_resource *templet,
    439                                struct winsys_handle *whandle,
    440                                unsigned *stride)
    441 {
    442    assert(0);
    443    return NULL;
    444 }
    445 
    446 
    447 static boolean
    448 xlib_displaytarget_get_handle(struct sw_winsys *winsys,
    449                               struct sw_displaytarget *dt,
    450                               struct winsys_handle *whandle)
    451 {
    452    assert(0);
    453    return FALSE;
    454 }
    455 
    456 
    457 static void
    458 xlib_destroy(struct sw_winsys *ws)
    459 {
    460    FREE(ws);
    461 }
    462 
    463 
    464 struct sw_winsys *
    465 xlib_create_sw_winsys(Display *display)
    466 {
    467    struct xlib_sw_winsys *ws;
    468 
    469    ws = CALLOC_STRUCT(xlib_sw_winsys);
    470    if (!ws)
    471       return NULL;
    472 
    473    ws->display = display;
    474    ws->base.destroy = xlib_destroy;
    475 
    476    ws->base.is_displaytarget_format_supported = xlib_is_displaytarget_format_supported;
    477 
    478    ws->base.displaytarget_create = xlib_displaytarget_create;
    479    ws->base.displaytarget_from_handle = xlib_displaytarget_from_handle;
    480    ws->base.displaytarget_get_handle = xlib_displaytarget_get_handle;
    481    ws->base.displaytarget_map = xlib_displaytarget_map;
    482    ws->base.displaytarget_unmap = xlib_displaytarget_unmap;
    483    ws->base.displaytarget_destroy = xlib_displaytarget_destroy;
    484 
    485    ws->base.displaytarget_display = xlib_displaytarget_display;
    486 
    487    return &ws->base;
    488 }
    489