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
lock_drawables_list(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
unlock_drawables_list(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 *
apple_glx_find_drawable(Display * dpy,GLXDrawable 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
drawable_lock(struct apple_glx_drawable * agd)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
drawable_unlock(struct apple_glx_drawable * d)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
reference_drawable(struct apple_glx_drawable * d)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
release_drawable(struct apple_glx_drawable * d)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
destroy_drawable(struct apple_glx_drawable * d)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 /* Stupid recursive locks */
178 while (pthread_mutex_unlock(&d->mutex) == 0);
179
180 err = pthread_mutex_destroy(&d->mutex);
181 if (err) {
182 fprintf(stderr, "pthread_mutex_destroy error: %s\n", strerror(err));
183 abort();
184 }
185
186 free(d);
187
188 /* So that the locks are balanced and the caller correctly unlocks. */
189 lock_drawables_list();
190
191 return true;
192 }
193
194 /*
195 * This is typically called when a context is destroyed or the current
196 * drawable is made None.
197 */
198 static bool
destroy_drawable_callback(struct apple_glx_drawable * d)199 destroy_drawable_callback(struct apple_glx_drawable *d)
200 {
201 bool result;
202
203 d->lock(d);
204
205 apple_glx_diagnostic("%s: %p ->reference_count before -- %d\n", __func__,
206 (void *) d, d->reference_count);
207
208 d->reference_count--;
209
210 if (d->reference_count > 0) {
211 d->unlock(d);
212 return false;
213 }
214
215 d->unlock(d);
216
217 lock_drawables_list();
218
219 result = destroy_drawable(d);
220
221 unlock_drawables_list();
222
223 return result;
224 }
225
226 static bool
is_pbuffer(struct apple_glx_drawable * d)227 is_pbuffer(struct apple_glx_drawable *d)
228 {
229 return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
230 }
231
232 static bool
is_pixmap(struct apple_glx_drawable * d)233 is_pixmap(struct apple_glx_drawable *d)
234 {
235 return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
236 }
237
238 static void
common_init(Display * dpy,GLXDrawable drawable,struct apple_glx_drawable * d)239 common_init(Display * dpy, GLXDrawable drawable, struct apple_glx_drawable *d)
240 {
241 int err;
242 pthread_mutexattr_t attr;
243
244 d->display = dpy;
245 d->reference_count = 0;
246 d->drawable = drawable;
247 d->type = -1;
248
249 err = pthread_mutexattr_init(&attr);
250
251 if (err) {
252 fprintf(stderr, "pthread_mutexattr_init error: %s\n", strerror(err));
253 abort();
254 }
255
256 /*
257 * There are some patterns that require a recursive mutex,
258 * when working with locks that protect the apple_glx_drawable,
259 * and reference functions like ->reference, and ->release.
260 */
261 err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
262
263 if (err) {
264 fprintf(stderr, "error: setting pthread mutex type: %s\n", strerror(err));
265 abort();
266 }
267
268 err = pthread_mutex_init(&d->mutex, &attr);
269
270 if (err) {
271 fprintf(stderr, "pthread_mutex_init error: %s\n", strerror(err));
272 abort();
273 }
274
275 (void) pthread_mutexattr_destroy(&attr);
276
277 d->lock = drawable_lock;
278 d->unlock = drawable_unlock;
279
280 d->reference = reference_drawable;
281 d->release = release_drawable;
282
283 d->destroy = destroy_drawable_callback;
284
285 d->is_pbuffer = is_pbuffer;
286 d->is_pixmap = is_pixmap;
287
288 d->width = -1;
289 d->height = -1;
290 d->row_bytes = 0;
291 d->path[0] = '\0';
292 d->fd = -1;
293 d->buffer = NULL;
294 d->buffer_length = 0;
295
296 d->previous = NULL;
297 d->next = NULL;
298 }
299
300 static void
link_tail(struct apple_glx_drawable * agd)301 link_tail(struct apple_glx_drawable *agd)
302 {
303 lock_drawables_list();
304
305 /* Link the new drawable into the global list. */
306 agd->next = drawables_list;
307
308 if (drawables_list)
309 drawables_list->previous = agd;
310
311 drawables_list = agd;
312
313 unlock_drawables_list();
314 }
315
316 /*WARNING: this returns a locked and referenced object. */
317 bool
apple_glx_drawable_create(Display * dpy,int screen,GLXDrawable drawable,struct apple_glx_drawable ** agdResult,struct apple_glx_drawable_callbacks * callbacks)318 apple_glx_drawable_create(Display * dpy,
319 int screen,
320 GLXDrawable drawable,
321 struct apple_glx_drawable **agdResult,
322 struct apple_glx_drawable_callbacks *callbacks)
323 {
324 struct apple_glx_drawable *d;
325
326 d = calloc(1, sizeof *d);
327
328 if (NULL == d) {
329 perror("malloc");
330 return true;
331 }
332
333 common_init(dpy, drawable, d);
334 d->type = callbacks->type;
335 d->callbacks = *callbacks;
336
337 d->reference(d);
338 d->lock(d);
339
340 link_tail(d);
341
342 apple_glx_diagnostic("%s: new drawable %p\n", __func__, (void *) d);
343
344 *agdResult = d;
345
346 return false;
347 }
348
349 static int error_count = 0;
350
351 static int
error_handler(Display * dpy,XErrorEvent * err)352 error_handler(Display * dpy, XErrorEvent * err)
353 {
354 if (err->error_code == BadWindow) {
355 ++error_count;
356 }
357
358 return 0;
359 }
360
361 void
apple_glx_garbage_collect_drawables(Display * dpy)362 apple_glx_garbage_collect_drawables(Display * dpy)
363 {
364 struct apple_glx_drawable *d, *dnext;
365 Window root;
366 int x, y;
367 unsigned int width, height, bd, depth;
368 int (*old_handler) (Display *, XErrorEvent *);
369
370
371 if (NULL == drawables_list)
372 return;
373
374 old_handler = XSetErrorHandler(error_handler);
375
376 XSync(dpy, False);
377
378 lock_drawables_list();
379
380 for (d = drawables_list; d;) {
381 dnext = d->next;
382
383 d->lock(d);
384
385 if (d->reference_count > 0) {
386 /*
387 * Skip this, because some context still retains a reference
388 * to the drawable.
389 */
390 d->unlock(d);
391 d = dnext;
392 continue;
393 }
394
395 d->unlock(d);
396
397 error_count = 0;
398
399 /*
400 * Mesa uses XGetWindowAttributes, but some of these things are
401 * most definitely not Windows, and that's against the rules.
402 * XGetGeometry on the other hand is legal with a Pixmap and Window.
403 */
404 XGetGeometry(dpy, d->drawable, &root, &x, &y, &width, &height, &bd,
405 &depth);
406
407 if (error_count > 0) {
408 /*
409 * Note: this may not actually destroy the drawable.
410 * If another context retains a reference to the drawable
411 * after the reference count test above.
412 */
413 (void) destroy_drawable(d);
414 error_count = 0;
415 }
416
417 d = dnext;
418 }
419
420 XSetErrorHandler(old_handler);
421
422 unlock_drawables_list();
423 }
424
425 unsigned int
apple_glx_get_drawable_count(void)426 apple_glx_get_drawable_count(void)
427 {
428 unsigned int result = 0;
429 struct apple_glx_drawable *d;
430
431 lock_drawables_list();
432
433 for (d = drawables_list; d; d = d->next)
434 ++result;
435
436 unlock_drawables_list();
437
438 return result;
439 }
440
441 struct apple_glx_drawable *
apple_glx_drawable_find_by_type(GLXDrawable drawable,int type,int flags)442 apple_glx_drawable_find_by_type(GLXDrawable drawable, int type, int flags)
443 {
444 struct apple_glx_drawable *d;
445
446 lock_drawables_list();
447
448 for (d = drawables_list; d; d = d->next) {
449 if (d->type == type && d->drawable == drawable) {
450 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
451 d->reference(d);
452
453 if (flags & APPLE_GLX_DRAWABLE_LOCK)
454 d->lock(d);
455
456 unlock_drawables_list();
457
458 return d;
459 }
460 }
461
462 unlock_drawables_list();
463
464 return NULL;
465 }
466
467 struct apple_glx_drawable *
apple_glx_drawable_find(GLXDrawable drawable,int flags)468 apple_glx_drawable_find(GLXDrawable drawable, int flags)
469 {
470 struct apple_glx_drawable *d;
471
472 lock_drawables_list();
473
474 for (d = drawables_list; d; d = d->next) {
475 if (d->drawable == drawable) {
476 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
477 d->reference(d);
478
479 if (flags & APPLE_GLX_DRAWABLE_LOCK)
480 d->lock(d);
481
482 unlock_drawables_list();
483
484 return d;
485 }
486 }
487
488 unlock_drawables_list();
489
490 return NULL;
491 }
492
493 /* Return true if the type is valid for the drawable. */
494 bool
apple_glx_drawable_destroy_by_type(Display * dpy,GLXDrawable drawable,int type)495 apple_glx_drawable_destroy_by_type(Display * dpy,
496 GLXDrawable drawable, int type)
497 {
498 struct apple_glx_drawable *d;
499
500 lock_drawables_list();
501
502 for (d = drawables_list; d; d = d->next) {
503 if (drawable == d->drawable && type == d->type) {
504 /*
505 * The user has requested that we destroy this resource.
506 * However, there may be references in the contexts to it, so
507 * release it, and call destroy_drawable which doesn't destroy
508 * if the reference_count is > 0.
509 */
510 d->release(d);
511
512 apple_glx_diagnostic("%s d->reference_count %d\n",
513 __func__, d->reference_count);
514
515 destroy_drawable(d);
516 unlock_drawables_list();
517 return true;
518 }
519 }
520
521 unlock_drawables_list();
522
523 return false;
524 }
525
526 struct apple_glx_drawable *
apple_glx_drawable_find_by_uid(unsigned int uid,int flags)527 apple_glx_drawable_find_by_uid(unsigned int uid, int flags)
528 {
529 struct apple_glx_drawable *d;
530
531 lock_drawables_list();
532
533 for (d = drawables_list; d; d = d->next) {
534 /* Only surfaces have a uid. */
535 if (APPLE_GLX_DRAWABLE_SURFACE == d->type) {
536 if (d->types.surface.uid == uid) {
537 if (flags & APPLE_GLX_DRAWABLE_REFERENCE)
538 d->reference(d);
539
540 if (flags & APPLE_GLX_DRAWABLE_LOCK)
541 d->lock(d);
542
543 unlock_drawables_list();
544
545 return d;
546 }
547 }
548 }
549
550 unlock_drawables_list();
551
552 return NULL;
553 }
554