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