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