3f84d560c539573aed6a16e574aab0ca001e45a4
[mesa.git] / src / glx / apple / apple_glx_drawable.c
1 /*
2 Copyright (c) 2008, 2009 Apple Inc.
3
4 Permission is hereby granted, free of charge, to any person
5 obtaining a copy of this software and associated documentation files
6 (the "Software"), to deal in the Software without restriction,
7 including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software,
9 and to permit persons to whom the Software is furnished to do so,
10 subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
19 HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 DEALINGS IN THE SOFTWARE.
23
24 Except as contained in this notice, the name(s) of the above
25 copyright holders shall not be used in advertising or otherwise to
26 promote the sale, use or other dealings in this Software without
27 prior written authorization.
28 */
29
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <assert.h>
34 #include <pthread.h>
35 #include <string.h>
36 #include "apple_glx.h"
37 #include "apple_glx_context.h"
38 #include "apple_glx_drawable.h"
39 #include "appledri.h"
40
41 static pthread_mutex_t drawables_lock = PTHREAD_MUTEX_INITIALIZER;
42 static struct apple_glx_drawable *drawables_list = NULL;
43
44 static void
45 lock_drawables_list(void)
46 {
47 int err;
48
49 err = pthread_mutex_lock(&drawables_lock);
50
51 if (err) {
52 fprintf(stderr, "pthread_mutex_lock failure in %s: %s\n",
53 __func__, strerror(err));
54 abort();
55 }
56 }
57
58 static void
59 unlock_drawables_list(void)
60 {
61 int err;
62
63 err = pthread_mutex_unlock(&drawables_lock);
64
65 if (err) {
66 fprintf(stderr, "pthread_mutex_unlock failure in %s: %s\n",
67 __func__, strerror(err));
68 abort();
69 }
70 }
71
72 struct apple_glx_drawable *
73 apple_glx_find_drawable(Display * dpy, GLXDrawable drawable)
74 {
75 struct apple_glx_drawable *i, *agd = NULL;
76
77 lock_drawables_list();
78
79 for (i = drawables_list; i; i = i->next) {
80 if (i->drawable == drawable) {
81 agd = i;
82 break;
83 }
84 }
85
86 unlock_drawables_list();
87
88 return agd;
89 }
90
91 static void
92 drawable_lock(struct apple_glx_drawable *agd)
93 {
94 int err;
95
96 err = pthread_mutex_lock(&agd->mutex);
97
98 if (err) {
99 fprintf(stderr, "pthread_mutex_lock error: %s\n", strerror(err));
100 abort();
101 }
102 }
103
104 static void
105 drawable_unlock(struct apple_glx_drawable *d)
106 {
107 int err;
108
109 err = pthread_mutex_unlock(&d->mutex);
110
111 if (err) {
112 fprintf(stderr, "pthread_mutex_unlock error: %s\n", strerror(err));
113 abort();
114 }
115 }
116
117
118 static void
119 reference_drawable(struct apple_glx_drawable *d)
120 {
121 d->lock(d);
122 d->reference_count++;
123 d->unlock(d);
124 }
125
126 static void
127 release_drawable(struct apple_glx_drawable *d)
128 {
129 d->lock(d);
130 d->reference_count--;
131 d->unlock(d);
132 }
133
134 /* The drawables list must be locked prior to calling this. */
135 /* Return true if the drawable was destroyed. */
136 static bool
137 destroy_drawable(struct apple_glx_drawable *d)
138 {
139 int err;
140
141 d->lock(d);
142
143 if (d->reference_count > 0) {
144 d->unlock(d);
145 return false;
146 }
147
148 d->unlock(d);
149
150 if (d->previous) {
151 d->previous->next = d->next;
152 }
153 else {
154 /*
155 * The item must be at the head of the list, if it
156 * has no previous pointer.
157 */
158 drawables_list = d->next;
159 }
160
161 if (d->next)
162 d->next->previous = d->previous;
163
164 unlock_drawables_list();
165
166 if (d->callbacks.destroy) {
167 /*
168 * Warning: this causes other routines to be called (potentially)
169 * from surface_notify_handler. It's probably best to not have
170 * any locks at this point locked.
171 */
172 d->callbacks.destroy(d->display, d);
173 }
174
175 apple_glx_diagnostic("%s: freeing %p\n", __func__, (void *) d);
176
177 err = pthread_mutex_destroy(&d->mutex);
178 if (err) {
179 fprintf(stderr, "pthread_mutex_destroy error: %s\n", strerror(err));
180 abort();
181 }
182
183 free(d);
184
185 /* So that the locks are balanced and the caller correctly unlocks. */
186 lock_drawables_list();
187
188 return true;
189 }
190
191 /*
192 * This is typically called when a context is destroyed or the current
193 * drawable is made None.
194 */
195 static bool
196 destroy_drawable_callback(struct apple_glx_drawable *d)
197 {
198 bool result;
199
200 d->lock(d);
201
202 apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__,
203 (void *) d, d->reference_count);
204
205 d->reference_count--;
206
207 if (d->reference_count > 0) {
208 d->unlock(d);
209 return false;
210 }
211
212 d->unlock(d);
213
214 lock_drawables_list();
215
216 result = destroy_drawable(d);
217
218 unlock_drawables_list();
219
220 return result;
221 }
222
223 static bool
224 is_pbuffer(struct apple_glx_drawable *d)
225 {
226 return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
227 }
228
229 static bool
230 is_pixmap(struct apple_glx_drawable *d)
231 {
232 return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
233 }
234
235 static void
236 common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d)
237 {
238 int err;
239 pthread_mutexattr_t attr;
240
241 d->display = dpy;
242 d->reference_count = 0;
243 d->drawable = drawable;
244 d->type = -1;
245
246 err = pthread_mutexattr_init(&attr);
247
248 if (err) {
249 fprintf(stderr, "pthread_mutexattr_init error: %s\n", strerror(err));
250 abort();
251 }
252
253 /*
254 * There are some patterns that require a recursive mutex,
255 * when working with locks that protect the apple_glx_drawable,
256 * and reference functions like ->reference, and ->release.
257 */
258 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
259
260 if (err) {
261 fprintf(stderr, "error: setting pthread mutex type: %s\n", strerror(err));
262 abort();
263 }
264
265 err = pthread_mutex_init(&d->mutex, &attr);
266
267 if (err) {
268 fprintf(stderr, "pthread_mutex_init error: %s\n", strerror(err));
269 abort();
270 }
271
272 (void) pthread_mutexattr_destroy(&attr);
273
274 d->lock = drawable_lock;
275 d->unlock = drawable_unlock;
276
277 d->reference = reference_drawable;
278 d->release = release_drawable;
279
280 d->destroy = destroy_drawable_callback;
281
282 d->is_pbuffer = is_pbuffer;
283 d->is_pixmap = is_pixmap;
284
285 d->width = -1;
286 d->height = -1;
287 d->row_bytes = 0;
288 d->path[0] = '\0';
289 d->fd = -1;
290 d->buffer = NULL;
291 d->buffer_length = 0;
292
293 d->previous = NULL;
294 d->next = NULL;
295 }
296
297 static void
298 link_tail(struct apple_glx_drawable *agd)
299 {
300 lock_drawables_list();
301
302 /* Link the new drawable into the global list. */
303 agd->next = drawables_list;
304
305 if (drawables_list)
306 drawables_list->previous = agd;
307
308 drawables_list = agd;
309
310 unlock_drawables_list();
311 }
312
313 /*WARNING: this returns a locked and referenced object. */
314 bool
315 apple_glx_drawable_create(Display * dpy,
316 int screen,
317 GLXDrawable drawable,
318 struct apple_glx_drawable **agdResult,
319 struct apple_glx_drawable_callbacks *callbacks)
320 {
321 struct apple_glx_drawable *d;
322
323 d = calloc(1, sizeof *d);
324
325 if (NULL == d) {
326 perror("malloc");
327 return true;
328 }
329
330 common_init(dpy, drawable, d);
331 d->type = callbacks->type;
332 d->callbacks = *callbacks;
333
334 d->reference(d);
335 d->lock(d);
336
337 link_tail(d);
338
339 apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d);
340
341 *agdResult = d;
342
343 return false;
344 }
345
346 static int error_count = 0;
347
348 static int
349 error_handler(Display * dpy, XErrorEvent * err)
350 {
351 if (err->error_code == BadWindow) {
352 ++error_count;
353 }
354
355 return 0;
356 }
357
358 void
359 apple_glx_garbage_collect_drawables(Display * dpy)
360 {
361 struct apple_glx_drawable *d, *dnext;
362 Window root;
363 int x, y;
364 unsigned int width, height, bd, depth;
365 int (*old_handler) (Display *, XErrorEvent *);
366
367
368 if (NULL == drawables_list)
369 return;
370
371 old_handler = XSetErrorHandler(error_handler);
372
373 XSync(dpy, False);
374
375 lock_drawables_list();
376
377 for (d = drawables_list; d;) {
378 dnext = d->next;
379
380 d->lock(d);
381
382 if (d->reference_count > 0) {
383 /*
384 * Skip this, because some context still retains a reference
385 * to the drawable.
386 */
387 d->unlock(d);
388 d = dnext;
389 continue;
390 }
391
392 d->unlock(d);
393
394 error_count = 0;
395
396 /*
397 * Mesa uses XGetWindowAttributes, but some of these things are
398 * most definitely not Windows, and that's against the rules.
399 * XGetGeometry on the other hand is legal with a Pixmap and Window.
400 */
401 XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd,
402 &depth);
403
404 if (error_count > 0) {
405 /*
406 * Note: this may not actually destroy the drawable.
407 * If another context retains a reference to the drawable
408 * after the reference count test above.
409 */
410 (void) destroy_drawable(d);
411 error_count = 0;
412 }
413
414 d = dnext;
415 }
416
417 XSetErrorHandler(old_handler);
418
419 unlock_drawables_list();
420 }
421
422 unsigned int
423 apple_glx_get_drawable_count(void)
424 {
425 unsigned int result = 0;
426 struct apple_glx_drawable *d;
427
428 lock_drawables_list();
429
430 for (d = drawables_list; d; d = d->next)
431 ++result;
432
433 unlock_drawables_list();
434
435 return result;
436 }
437
438 struct apple_glx_drawable *
439 apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags)
440 {
441 struct apple_glx_drawable *d;
442
443 lock_drawables_list();
444
445 for (d = drawables_list; d; d = d->next) {
446 if (d->type == type && d->drawable == drawable) {
447 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
448 d->reference(d);
449
450 if (flags & APPLE_GLX_DRAWABLE_LOCK)
451 d->lock(d);
452
453 unlock_drawables_list();
454
455 return d;
456 }
457 }
458
459 unlock_drawables_list();
460
461 return NULL;
462 }
463
464 struct apple_glx_drawable *
465 apple_glx_drawable_find(GLXDrawable drawable, int flags)
466 {
467 struct apple_glx_drawable *d;
468
469 lock_drawables_list();
470
471 for (d = drawables_list; d; d = d->next) {
472 if (d->drawable == drawable) {
473 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
474 d->reference(d);
475
476 if (flags & APPLE_GLX_DRAWABLE_LOCK)
477 d->lock(d);
478
479 unlock_drawables_list();
480
481 return d;
482 }
483 }
484
485 unlock_drawables_list();
486
487 return NULL;
488 }
489
490 /* Return true if the type is valid for the drawable. */
491 bool
492 apple_glx_drawable_destroy_by_type(Display * dpy,
493 GLXDrawable drawable, int type)
494 {
495 struct apple_glx_drawable *d;
496
497 lock_drawables_list();
498
499 for (d = drawables_list; d; d = d->next) {
500 if (drawable == d->drawable && type == d->type) {
501 /*
502 * The user has requested that we destroy this resource.
503 * However, there may be references in the contexts to it, so
504 * release it, and call destroy_drawable which doesn't destroy
505 * if the reference_count is > 0.
506 */
507 d->release(d);
508
509 apple_glx_diagnostic("%s d->reference_count %d\n",
510 __func__, d->reference_count);
511
512 destroy_drawable(d);
513 unlock_drawables_list();
514 return true;
515 }
516 }
517
518 unlock_drawables_list();
519
520 return false;
521 }
522
523 struct apple_glx_drawable *
524 apple_glx_drawable_find_by_uid(unsigned int uid, int flags)
525 {
526 struct apple_glx_drawable *d;
527
528 lock_drawables_list();
529
530 for (d = drawables_list; d; d = d->next) {
531 /* Only surfaces have a uid. */
532 if (APPLE_GLX_DRAWABLE_SURFACE == d->type) {
533 if (d->types.surface.uid == uid) {
534 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
535 d->reference(d);
536
537 if (flags & APPLE_GLX_DRAWABLE_LOCK)
538 d->lock(d);
539
540 unlock_drawables_list();
541
542 return d;
543 }
544 }
545 }
546
547 unlock_drawables_list();
548
549 return NULL;
550 }