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