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