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