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