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