Merge commit 'origin/gallium-0.1'
[mesa.git] / src / mesa / drivers / x11 / xm_buffer.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.2
4 *
5 * Copyright (C) 1999-2006 Brian Paul All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26 /**
27 * \file xm_buffer.h
28 * Framebuffer and renderbuffer-related functions.
29 */
30
31
32 #include "glxheader.h"
33 #include "xmesaP.h"
34 #include "main/imports.h"
35 #include "main/framebuffer.h"
36 #include "main/renderbuffer.h"
37
38
39 #if defined(USE_XSHM) && !defined(XFree86Server)
40 static volatile int mesaXErrorFlag = 0;
41
42 /**
43 * Catches potential Xlib errors.
44 */
45 static int
46 mesaHandleXError(XMesaDisplay *dpy, XErrorEvent *event)
47 {
48 (void) dpy;
49 (void) event;
50 mesaXErrorFlag = 1;
51 return 0;
52 }
53
54 /**
55 * Allocate a shared memory XImage back buffer for the given XMesaBuffer.
56 * Return: GL_TRUE if success, GL_FALSE if error
57 */
58 static GLboolean
59 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
60 {
61 /*
62 * We have to do a _lot_ of error checking here to be sure we can
63 * really use the XSHM extension. It seems different servers trigger
64 * errors at different points if the extension won't work. Therefore
65 * we have to be very careful...
66 */
67 GC gc;
68 int (*old_handler)(XMesaDisplay *, XErrorEvent *);
69
70 if (width == 0 || height == 0) {
71 /* this will be true the first time we're called on 'b' */
72 return GL_FALSE;
73 }
74
75 b->backxrb->ximage = XShmCreateImage(b->xm_visual->display,
76 b->xm_visual->visinfo->visual,
77 b->xm_visual->visinfo->depth,
78 ZPixmap, NULL, &b->shminfo,
79 width, height);
80 if (b->backxrb->ximage == NULL) {
81 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (XShmCreateImage), disabling.\n");
82 b->shm = 0;
83 return GL_FALSE;
84 }
85
86 b->shminfo.shmid = shmget(IPC_PRIVATE, b->backxrb->ximage->bytes_per_line
87 * b->backxrb->ximage->height, IPC_CREAT|0777);
88 if (b->shminfo.shmid < 0) {
89 _mesa_warning(NULL, "shmget failed while allocating back buffer.\n");
90 XDestroyImage(b->backxrb->ximage);
91 b->backxrb->ximage = NULL;
92 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmget), disabling.\n");
93 b->shm = 0;
94 return GL_FALSE;
95 }
96
97 b->shminfo.shmaddr = b->backxrb->ximage->data
98 = (char*)shmat(b->shminfo.shmid, 0, 0);
99 if (b->shminfo.shmaddr == (char *) -1) {
100 _mesa_warning(NULL, "shmat() failed while allocating back buffer.\n");
101 XDestroyImage(b->backxrb->ximage);
102 shmctl(b->shminfo.shmid, IPC_RMID, 0);
103 b->backxrb->ximage = NULL;
104 _mesa_warning(NULL, "alloc_back_buffer: Shared memory error (shmat), disabling.\n");
105 b->shm = 0;
106 return GL_FALSE;
107 }
108
109 b->shminfo.readOnly = False;
110 mesaXErrorFlag = 0;
111 old_handler = XSetErrorHandler(mesaHandleXError);
112 /* This may trigger the X protocol error we're ready to catch: */
113 XShmAttach(b->xm_visual->display, &b->shminfo);
114 XSync(b->xm_visual->display, False);
115
116 if (mesaXErrorFlag) {
117 /* we are on a remote display, this error is normal, don't print it */
118 XFlush(b->xm_visual->display);
119 mesaXErrorFlag = 0;
120 XDestroyImage(b->backxrb->ximage);
121 shmdt(b->shminfo.shmaddr);
122 shmctl(b->shminfo.shmid, IPC_RMID, 0);
123 b->backxrb->ximage = NULL;
124 b->shm = 0;
125 (void) XSetErrorHandler(old_handler);
126 return GL_FALSE;
127 }
128
129 shmctl(b->shminfo.shmid, IPC_RMID, 0); /* nobody else needs it */
130
131 /* Finally, try an XShmPutImage to be really sure the extension works */
132 gc = XCreateGC(b->xm_visual->display, b->frontxrb->drawable, 0, NULL);
133 XShmPutImage(b->xm_visual->display, b->frontxrb->drawable, gc,
134 b->backxrb->ximage, 0, 0, 0, 0, 1, 1 /*one pixel*/, False);
135 XSync(b->xm_visual->display, False);
136 XFreeGC(b->xm_visual->display, gc);
137 (void) XSetErrorHandler(old_handler);
138 if (mesaXErrorFlag) {
139 XFlush(b->xm_visual->display);
140 mesaXErrorFlag = 0;
141 XDestroyImage(b->backxrb->ximage);
142 shmdt(b->shminfo.shmaddr);
143 shmctl(b->shminfo.shmid, IPC_RMID, 0);
144 b->backxrb->ximage = NULL;
145 b->shm = 0;
146 return GL_FALSE;
147 }
148
149 return GL_TRUE;
150 }
151 #else
152 static GLboolean
153 alloc_back_shm_ximage(XMesaBuffer b, GLuint width, GLuint height)
154 {
155 /* Can't compile XSHM support */
156 return GL_FALSE;
157 }
158 #endif
159
160
161
162 /**
163 * Setup an off-screen pixmap or Ximage to use as the back buffer.
164 * Input: b - the X/Mesa buffer
165 */
166 static void
167 alloc_back_buffer(XMesaBuffer b, GLuint width, GLuint height)
168 {
169 if (b->db_mode == BACK_XIMAGE) {
170 /* Deallocate the old backxrb->ximage, if any */
171 if (b->backxrb->ximage) {
172 #if defined(USE_XSHM) && !defined(XFree86Server)
173 if (b->shm) {
174 XShmDetach(b->xm_visual->display, &b->shminfo);
175 XDestroyImage(b->backxrb->ximage);
176 shmdt(b->shminfo.shmaddr);
177 }
178 else
179 #endif
180 XMesaDestroyImage(b->backxrb->ximage);
181 b->backxrb->ximage = NULL;
182 }
183
184 if (width == 0 || height == 0)
185 return;
186
187 /* Allocate new back buffer */
188 if (b->shm == 0 || !alloc_back_shm_ximage(b, width, height)) {
189 /* Allocate a regular XImage for the back buffer. */
190 #ifdef XFree86Server
191 b->backxrb->ximage = XMesaCreateImage(b->xm_visual->BitsPerPixel,
192 width, height, NULL);
193 #else
194 b->backxrb->ximage = XCreateImage(b->xm_visual->display,
195 b->xm_visual->visinfo->visual,
196 GET_VISUAL_DEPTH(b->xm_visual),
197 ZPixmap, 0, /* format, offset */
198 NULL,
199 width, height,
200 8, 0); /* pad, bytes_per_line */
201 #endif
202 if (!b->backxrb->ximage) {
203 _mesa_warning(NULL, "alloc_back_buffer: XCreateImage failed.\n");
204 return;
205 }
206 b->backxrb->ximage->data = (char *) MALLOC(b->backxrb->ximage->height
207 * b->backxrb->ximage->bytes_per_line);
208 if (!b->backxrb->ximage->data) {
209 _mesa_warning(NULL, "alloc_back_buffer: MALLOC failed.\n");
210 XMesaDestroyImage(b->backxrb->ximage);
211 b->backxrb->ximage = NULL;
212 }
213 }
214 b->backxrb->pixmap = None;
215 }
216 else if (b->db_mode == BACK_PIXMAP) {
217 /* Free the old back pixmap */
218 if (b->backxrb->pixmap) {
219 XMesaFreePixmap(b->xm_visual->display, b->backxrb->pixmap);
220 b->backxrb->pixmap = 0;
221 }
222
223 if (width > 0 && height > 0) {
224 /* Allocate new back pixmap */
225 b->backxrb->pixmap = XMesaCreatePixmap(b->xm_visual->display,
226 b->frontxrb->drawable,
227 width, height,
228 GET_VISUAL_DEPTH(b->xm_visual));
229 }
230
231 b->backxrb->ximage = NULL;
232 }
233 }
234
235
236 static void
237 xmesa_delete_renderbuffer(struct gl_renderbuffer *rb)
238 {
239 /* XXX Note: the ximage or Pixmap attached to this renderbuffer
240 * should probably get freed here, but that's currently done in
241 * XMesaDestroyBuffer().
242 */
243 _mesa_free(rb);
244 }
245
246
247 /**
248 * Reallocate renderbuffer storage for front color buffer.
249 * Called via gl_renderbuffer::AllocStorage()
250 */
251 static GLboolean
252 xmesa_alloc_front_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
253 GLenum internalFormat, GLuint width, GLuint height)
254 {
255 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
256
257 /* just clear these to be sure we don't accidentally use them */
258 xrb->origin1 = NULL;
259 xrb->origin2 = NULL;
260 xrb->origin3 = NULL;
261 xrb->origin4 = NULL;
262
263 /* for the FLIP macro: */
264 xrb->bottom = height - 1;
265
266 rb->Width = width;
267 rb->Height = height;
268 rb->InternalFormat = internalFormat;
269
270 return GL_TRUE;
271 }
272
273
274 /**
275 * Reallocate renderbuffer storage for back color buffer.
276 * Called via gl_renderbuffer::AllocStorage()
277 */
278 static GLboolean
279 xmesa_alloc_back_storage(GLcontext *ctx, struct gl_renderbuffer *rb,
280 GLenum internalFormat, GLuint width, GLuint height)
281 {
282 struct xmesa_renderbuffer *xrb = xmesa_renderbuffer(rb);
283
284 /* reallocate the back buffer XImage or Pixmap */
285 assert(xrb->Parent);
286 alloc_back_buffer(xrb->Parent, width, height);
287
288 /* same as front buffer */
289 /* XXX why is this here? */
290 (void) xmesa_alloc_front_storage(ctx, rb, internalFormat, width, height);
291
292 /* plus... */
293 if (xrb->ximage) {
294 /* Needed by PIXELADDR1 macro */
295 xrb->width1 = xrb->ximage->bytes_per_line;
296 xrb->origin1 = (GLubyte *) xrb->ximage->data + xrb->width1 * (height - 1);
297
298 /* Needed by PIXELADDR2 macro */
299 xrb->width2 = xrb->ximage->bytes_per_line / 2;
300 xrb->origin2 = (GLushort *) xrb->ximage->data + xrb->width2 * (height - 1);
301
302 /* Needed by PIXELADDR3 macro */
303 xrb->width3 = xrb->ximage->bytes_per_line;
304 xrb->origin3 = (GLubyte *) xrb->ximage->data + xrb->width3 * (height - 1);
305
306 /* Needed by PIXELADDR4 macro */
307 xrb->width4 = xrb->ximage->width;
308 xrb->origin4 = (GLuint *) xrb->ximage->data + xrb->width4 * (height - 1);
309 }
310 else {
311 /* out of memory or buffer size is 0 x 0 */
312 xrb->width1 = xrb->width2 = xrb->width3 = xrb->width4 = 0;
313 xrb->origin1 = NULL;
314 xrb->origin2 = NULL;
315 xrb->origin3 = NULL;
316 xrb->origin4 = NULL;
317 }
318
319 return GL_TRUE;
320 }
321
322
323 struct xmesa_renderbuffer *
324 xmesa_new_renderbuffer(GLcontext *ctx, GLuint name, const GLvisual *visual,
325 GLboolean backBuffer)
326 {
327 struct xmesa_renderbuffer *xrb = CALLOC_STRUCT(xmesa_renderbuffer);
328 if (xrb) {
329 GLuint name = 0;
330 _mesa_init_renderbuffer(&xrb->Base, name);
331
332 xrb->Base.Delete = xmesa_delete_renderbuffer;
333 if (backBuffer)
334 xrb->Base.AllocStorage = xmesa_alloc_back_storage;
335 else
336 xrb->Base.AllocStorage = xmesa_alloc_front_storage;
337
338 if (visual->rgbMode) {
339 xrb->Base.InternalFormat = GL_RGBA;
340 xrb->Base._BaseFormat = GL_RGBA;
341 xrb->Base.DataType = GL_UNSIGNED_BYTE;
342 xrb->Base.RedBits = visual->redBits;
343 xrb->Base.GreenBits = visual->greenBits;
344 xrb->Base.BlueBits = visual->blueBits;
345 xrb->Base.AlphaBits = visual->alphaBits;
346 }
347 else {
348 xrb->Base.InternalFormat = GL_COLOR_INDEX;
349 xrb->Base._BaseFormat = GL_COLOR_INDEX;
350 xrb->Base.DataType = GL_UNSIGNED_INT;
351 xrb->Base.IndexBits = visual->indexBits;
352 }
353 /* only need to set Red/Green/EtcBits fields for user-created RBs */
354 }
355 return xrb;
356 }
357
358
359 /**
360 * Called via gl_framebuffer::Delete() method when this buffer
361 * is _really_ being deleted.
362 */
363 void
364 xmesa_delete_framebuffer(struct gl_framebuffer *fb)
365 {
366 XMesaBuffer b = XMESA_BUFFER(fb);
367
368 if (b->num_alloced > 0) {
369 /* If no other buffer uses this X colormap then free the colors. */
370 if (!xmesa_find_buffer(b->display, b->cmap, b)) {
371 #ifdef XFree86Server
372 int client = 0;
373 if (b->frontxrb->drawable)
374 client = CLIENT_ID(b->frontxrb->drawable->id);
375 (void)FreeColors(b->cmap, client,
376 b->num_alloced, b->alloced_colors, 0);
377 #else
378 XFreeColors(b->display, b->cmap,
379 b->alloced_colors, b->num_alloced, 0);
380 #endif
381 }
382 }
383
384 if (b->gc)
385 XMesaFreeGC(b->display, b->gc);
386 if (b->cleargc)
387 XMesaFreeGC(b->display, b->cleargc);
388 if (b->swapgc)
389 XMesaFreeGC(b->display, b->swapgc);
390
391 if (fb->Visual.doubleBufferMode) {
392 /* free back ximage/pixmap/shmregion */
393 if (b->backxrb->ximage) {
394 #if defined(USE_XSHM) && !defined(XFree86Server)
395 if (b->shm) {
396 XShmDetach( b->display, &b->shminfo );
397 XDestroyImage( b->backxrb->ximage );
398 shmdt( b->shminfo.shmaddr );
399 }
400 else
401 #endif
402 XMesaDestroyImage( b->backxrb->ximage );
403 b->backxrb->ximage = NULL;
404 }
405 if (b->backxrb->pixmap) {
406 XMesaFreePixmap( b->display, b->backxrb->pixmap );
407 if (b->xm_visual->hpcr_clear_flag) {
408 XMesaFreePixmap( b->display,
409 b->xm_visual->hpcr_clear_pixmap );
410 XMesaDestroyImage( b->xm_visual->hpcr_clear_ximage );
411 }
412 }
413 }
414
415 if (b->rowimage) {
416 _mesa_free( b->rowimage->data );
417 b->rowimage->data = NULL;
418 XMesaDestroyImage( b->rowimage );
419 }
420
421 _mesa_free_framebuffer_data(fb);
422 _mesa_free(fb);
423 }