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