Home | History | Annotate | Download | only in xlib
      1 /**************************************************************************
      2  *
      3  * Copyright 2007 Tungsten Graphics, 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/xlib_sw_winsys.h"
     43 
     44 #include <X11/Xlib.h>
     45 #include <X11/Xlibint.h>
     46 #include <X11/Xutil.h>
     47 #include <sys/ipc.h>
     48 #include <sys/shm.h>
     49 #include <X11/extensions/XShm.h>
     50 
     51 DEBUG_GET_ONCE_BOOL_OPTION(xlib_no_shm, "XLIB_NO_SHM", FALSE)
     52 
     53 /**
     54  * Display target for Xlib winsys.
     55  * Low-level OS/window system memory buffer
     56  */
     57 struct xlib_displaytarget
     58 {
     59    enum pipe_format format;
     60    unsigned width;
     61    unsigned height;
     62    unsigned stride;
     63 
     64    void *data;
     65    void *mapped;
     66 
     67    Display *display;
     68    Visual *visual;
     69    XImage *tempImage;
     70    GC gc;
     71 
     72    /* This is the last drawable that this display target was presented
     73     * against.  May need to recreate gc, tempImage when this changes??
     74     */
     75    Drawable drawable;
     76 
     77    XShmSegmentInfo shminfo;
     78    Bool shm;  /** Using shared memory images? */
     79 };
     80 
     81 
     82 /**
     83  * Subclass of sw_winsys for Xlib winsys
     84  */
     85 struct xlib_sw_winsys
     86 {
     87    struct sw_winsys base;
     88    Display *display;
     89 };
     90 
     91 
     92 
     93 /** Cast wrapper */
     94 static INLINE struct xlib_displaytarget *
     95 xlib_displaytarget(struct sw_displaytarget *dt)
     96 {
     97    return (struct xlib_displaytarget *) dt;
     98 }
     99 
    100 
    101 /**
    102  * X Shared Memory Image extension code
    103  */
    104 
    105 static volatile int XErrorFlag = 0;
    106 
    107 /**
    108  * Catches potential Xlib errors.
    109  */
    110 static int
    111 handle_xerror(Display *dpy, XErrorEvent *event)
    112 {
    113    (void) dpy;
    114    (void) event;
    115    XErrorFlag = 1;
    116    return 0;
    117 }
    118 
    119 
    120 static char *
    121 alloc_shm(struct xlib_displaytarget *buf, unsigned size)
    122 {
    123    XShmSegmentInfo *const shminfo = & buf->shminfo;
    124 
    125    shminfo->shmid = -1;
    126    shminfo->shmaddr = (char *) -1;
    127 
    128    shminfo->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
    129    if (shminfo->shmid < 0) {
    130       return NULL;
    131    }
    132 
    133    shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0);
    134    if (shminfo->shmaddr == (char *) -1) {
    135       shmctl(shminfo->shmid, IPC_RMID, 0);
    136       return NULL;
    137    }
    138 
    139    shminfo->readOnly = False;
    140    return shminfo->shmaddr;
    141 }
    142 
    143 
    144 /**
    145  * Allocate a shared memory XImage back buffer for the given display target.
    146  */
    147 static void
    148 alloc_shm_ximage(struct xlib_displaytarget *xlib_dt,
    149                  struct xlib_drawable *xmb,
    150                  unsigned width, unsigned height)
    151 {
    152    /*
    153     * We have to do a _lot_ of error checking here to be sure we can
    154     * really use the XSHM extension.  It seems different servers trigger
    155     * errors at different points if the extension won't work.  Therefore
    156     * we have to be very careful...
    157     */
    158    int (*old_handler)(Display *, XErrorEvent *);
    159 
    160    xlib_dt->tempImage = XShmCreateImage(xlib_dt->display,
    161                                       xmb->visual,
    162                                       xmb->depth,
    163                                       ZPixmap,
    164                                       NULL,
    165                                       &xlib_dt->shminfo,
    166                                       width, height);
    167    if (xlib_dt->tempImage == NULL) {
    168       xlib_dt->shm = False;
    169       return;
    170    }
    171 
    172 
    173    XErrorFlag = 0;
    174    old_handler = XSetErrorHandler(handle_xerror);
    175    /* This may trigger the X protocol error we're ready to catch: */
    176    XShmAttach(xlib_dt->display, &xlib_dt->shminfo);
    177    XSync(xlib_dt->display, False);
    178 
    179    if (XErrorFlag) {
    180       /* we are on a remote display, this error is normal, don't print it */
    181       XFlush(xlib_dt->display);
    182       XErrorFlag = 0;
    183       XDestroyImage(xlib_dt->tempImage);
    184       xlib_dt->tempImage = NULL;
    185       xlib_dt->shm = False;
    186       (void) XSetErrorHandler(old_handler);
    187       return;
    188    }
    189 
    190    xlib_dt->shm = True;
    191 }
    192 
    193 
    194 static void
    195 alloc_ximage(struct xlib_displaytarget *xlib_dt,
    196              struct xlib_drawable *xmb,
    197              unsigned width, unsigned height)
    198 {
    199    /* try allocating a shared memory image first */
    200    if (xlib_dt->shm) {
    201       alloc_shm_ximage(xlib_dt, xmb, width, height);
    202       if (xlib_dt->tempImage)
    203          return; /* success */
    204    }
    205 
    206    /* try regular (non-shared memory) image */
    207    xlib_dt->tempImage = XCreateImage(xlib_dt->display,
    208                                    xmb->visual,
    209                                    xmb->depth,
    210                                    ZPixmap, 0,
    211                                    NULL, width, height,
    212                                    8, 0);
    213 }
    214 
    215 static boolean
    216 xlib_is_displaytarget_format_supported(struct sw_winsys *ws,
    217                                        unsigned tex_usage,
    218                                        enum pipe_format format)
    219 {
    220    /* TODO: check visuals or other sensible thing here */
    221    return TRUE;
    222 }
    223 
    224 
    225 static void *
    226 xlib_displaytarget_map(struct sw_winsys *ws,
    227                        struct sw_displaytarget *dt,
    228                        unsigned flags)
    229 {
    230    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    231    xlib_dt->mapped = xlib_dt->data;
    232    return xlib_dt->mapped;
    233 }
    234 
    235 
    236 static void
    237 xlib_displaytarget_unmap(struct sw_winsys *ws,
    238                          struct sw_displaytarget *dt)
    239 {
    240    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    241    xlib_dt->mapped = NULL;
    242 }
    243 
    244 
    245 static void
    246 xlib_displaytarget_destroy(struct sw_winsys *ws,
    247                            struct sw_displaytarget *dt)
    248 {
    249    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    250 
    251    if (xlib_dt->data) {
    252       if (xlib_dt->shminfo.shmid >= 0) {
    253          shmdt(xlib_dt->shminfo.shmaddr);
    254          shmctl(xlib_dt->shminfo.shmid, IPC_RMID, 0);
    255 
    256          xlib_dt->shminfo.shmid = -1;
    257          xlib_dt->shminfo.shmaddr = (char *) -1;
    258 
    259          xlib_dt->data = NULL;
    260          if (xlib_dt->tempImage)
    261             xlib_dt->tempImage->data = NULL;
    262       }
    263       else {
    264          FREE(xlib_dt->data);
    265          if (xlib_dt->tempImage && xlib_dt->tempImage->data == xlib_dt->data) {
    266             xlib_dt->tempImage->data = NULL;
    267          }
    268          xlib_dt->data = NULL;
    269       }
    270    }
    271 
    272    if (xlib_dt->tempImage) {
    273       XDestroyImage(xlib_dt->tempImage);
    274       xlib_dt->tempImage = NULL;
    275    }
    276 
    277    if (xlib_dt->gc)
    278       XFreeGC(xlib_dt->display, xlib_dt->gc);
    279 
    280    FREE(xlib_dt);
    281 }
    282 
    283 
    284 /**
    285  * Display/copy the image in the surface into the X window specified
    286  * by the display target.
    287  */
    288 static void
    289 xlib_sw_display(struct xlib_drawable *xlib_drawable,
    290                 struct sw_displaytarget *dt)
    291 {
    292    static boolean no_swap = 0;
    293    static boolean firsttime = 1;
    294    struct xlib_displaytarget *xlib_dt = xlib_displaytarget(dt);
    295    Display *display = xlib_dt->display;
    296    XImage *ximage;
    297 
    298    if (firsttime) {
    299       no_swap = getenv("SP_NO_RAST") != NULL;
    300       firsttime = 0;
    301    }
    302 
    303    if (no_swap)
    304       return;
    305 
    306    if (xlib_dt->drawable != xlib_drawable->drawable) {
    307       if (xlib_dt->gc) {
    308          XFreeGC(display, xlib_dt->gc);
    309          xlib_dt->gc = NULL;
    310       }
    311 
    312       if (xlib_dt->tempImage) {
    313          XDestroyImage(xlib_dt->tempImage);
    314          xlib_dt->tempImage = NULL;
    315       }
    316 
    317       xlib_dt->drawable = xlib_drawable->drawable;
    318    }
    319 
    320    if (xlib_dt->tempImage == NULL) {
    321       assert(util_format_get_blockwidth(xlib_dt->format) == 1);
    322       assert(util_format_get_blockheight(xlib_dt->format) == 1);
    323       alloc_ximage(xlib_dt, xlib_drawable,
    324                    xlib_dt->stride / util_format_get_blocksize(xlib_dt->format),
    325                    xlib_dt->height);
    326       if (!xlib_dt->tempImage)
    327          return;
    328    }
    329 
    330    if (xlib_dt->gc == NULL) {
    331       xlib_dt->gc = XCreateGC(display, xlib_drawable->drawable, 0, NULL);
    332       XSetFunction(display, xlib_dt->gc, GXcopy);
    333    }
    334 
    335    if (xlib_dt->shm) {
    336       ximage = xlib_dt->tempImage;
    337       ximage->data = xlib_dt->data;
    338 
    339       /* _debug_printf("XSHM\n"); */
    340       XShmPutImage(xlib_dt->display, xlib_drawable->drawable, xlib_dt->gc,
    341                    ximage, 0, 0, 0, 0, xlib_dt->width, xlib_dt->height, False);
    342    }
    343    else {
    344       /* display image in Window */
    345       ximage = xlib_dt->tempImage;
    346       ximage->data = xlib_dt->data;
    347 
    348       /* check that the XImage has been previously initialized */
    349       assert(ximage->format);
    350       assert(ximage->bitmap_unit);
    351 
    352       /* update XImage's fields */
    353       ximage->width = xlib_dt->width;
    354       ximage->height = xlib_dt->height;
    355       ximage->bytes_per_line = xlib_dt->stride;
    356 
    357       /* _debug_printf("XPUT\n"); */
    358       XPutImage(xlib_dt->display, xlib_drawable->drawable, xlib_dt->gc,
    359                 ximage, 0, 0, 0, 0, xlib_dt->width, xlib_dt->height);
    360    }
    361 
    362    XFlush(xlib_dt->display);
    363 }
    364 
    365 
    366 /**
    367  * Display/copy the image in the surface into the X window specified
    368  * by the display target.
    369  */
    370 static void
    371 xlib_displaytarget_display(struct sw_winsys *ws,
    372                            struct sw_displaytarget *dt,
    373                            void *context_private)
    374 {
    375    struct xlib_drawable *xlib_drawable = (struct xlib_drawable *)context_private;
    376    xlib_sw_display(xlib_drawable, dt);
    377 }
    378 
    379 
    380 static struct sw_displaytarget *
    381 xlib_displaytarget_create(struct sw_winsys *winsys,
    382                           unsigned tex_usage,
    383                           enum pipe_format format,
    384                           unsigned width, unsigned height,
    385                           unsigned alignment,
    386                           unsigned *stride)
    387 {
    388    struct xlib_displaytarget *xlib_dt;
    389    unsigned nblocksy, size;
    390 
    391    xlib_dt = CALLOC_STRUCT(xlib_displaytarget);
    392    if (!xlib_dt)
    393       goto no_xlib_dt;
    394 
    395    xlib_dt->display = ((struct xlib_sw_winsys *)winsys)->display;
    396    xlib_dt->format = format;
    397    xlib_dt->width = width;
    398    xlib_dt->height = height;
    399 
    400    nblocksy = util_format_get_nblocksy(format, height);
    401    xlib_dt->stride = align(util_format_get_stride(format, width), alignment);
    402    size = xlib_dt->stride * nblocksy;
    403 
    404    if (!debug_get_option_xlib_no_shm()) {
    405       xlib_dt->data = alloc_shm(xlib_dt, size);
    406       if (xlib_dt->data) {
    407          xlib_dt->shm = True;
    408       }
    409    }
    410 
    411    if (!xlib_dt->data) {
    412       xlib_dt->data = align_malloc(size, alignment);
    413       if (!xlib_dt->data)
    414          goto no_data;
    415    }
    416 
    417    *stride = xlib_dt->stride;
    418    return (struct sw_displaytarget *)xlib_dt;
    419 
    420 no_data:
    421    FREE(xlib_dt);
    422 no_xlib_dt:
    423    return NULL;
    424 }
    425 
    426 
    427 static struct sw_displaytarget *
    428 xlib_displaytarget_from_handle(struct sw_winsys *winsys,
    429                                const struct pipe_resource *templet,
    430                                struct winsys_handle *whandle,
    431                                unsigned *stride)
    432 {
    433    assert(0);
    434    return NULL;
    435 }
    436 
    437 
    438 static boolean
    439 xlib_displaytarget_get_handle(struct sw_winsys *winsys,
    440                               struct sw_displaytarget *dt,
    441                               struct winsys_handle *whandle)
    442 {
    443    assert(0);
    444    return FALSE;
    445 }
    446 
    447 
    448 static void
    449 xlib_destroy(struct sw_winsys *ws)
    450 {
    451    FREE(ws);
    452 }
    453 
    454 
    455 struct sw_winsys *
    456 xlib_create_sw_winsys(Display *display)
    457 {
    458    struct xlib_sw_winsys *ws;
    459 
    460    ws = CALLOC_STRUCT(xlib_sw_winsys);
    461    if (!ws)
    462       return NULL;
    463 
    464    ws->display = display;
    465    ws->base.destroy = xlib_destroy;
    466 
    467    ws->base.is_displaytarget_format_supported = xlib_is_displaytarget_format_supported;
    468 
    469    ws->base.displaytarget_create = xlib_displaytarget_create;
    470    ws->base.displaytarget_from_handle = xlib_displaytarget_from_handle;
    471    ws->base.displaytarget_get_handle = xlib_displaytarget_get_handle;
    472    ws->base.displaytarget_map = xlib_displaytarget_map;
    473    ws->base.displaytarget_unmap = xlib_displaytarget_unmap;
    474    ws->base.displaytarget_destroy = xlib_displaytarget_destroy;
    475 
    476    ws->base.displaytarget_display = xlib_displaytarget_display;
    477 
    478    return &ws->base;
    479 }
    480