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 51DEBUG_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 */ 57struct 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 */ 85struct xlib_sw_winsys 86{ 87 struct sw_winsys base; 88 Display *display; 89}; 90 91 92 93/** Cast wrapper */ 94static INLINE struct xlib_displaytarget * 95xlib_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 105static volatile int XErrorFlag = 0; 106 107/** 108 * Catches potential Xlib errors. 109 */ 110static int 111handle_xerror(Display *dpy, XErrorEvent *event) 112{ 113 (void) dpy; 114 (void) event; 115 XErrorFlag = 1; 116 return 0; 117} 118 119 120static char * 121alloc_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 */ 147static void 148alloc_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 194static void 195alloc_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 215static boolean 216xlib_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 225static void * 226xlib_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 236static void 237xlib_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 245static void 246xlib_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 */ 288static void 289xlib_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 */ 370static void 371xlib_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 380static struct sw_displaytarget * 381xlib_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 420no_data: 421 FREE(xlib_dt); 422no_xlib_dt: 423 return NULL; 424} 425 426 427static struct sw_displaytarget * 428xlib_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 438static boolean 439xlib_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 448static void 449xlib_destroy(struct sw_winsys *ws) 450{ 451 FREE(ws); 452} 453 454 455struct sw_winsys * 456xlib_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