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