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