Tizen 2.1 base
[sdk/emulator/qemu.git] / gl / mesa / 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    /* 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
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
227 is_pbuffer(struct apple_glx_drawable *d)
228 {
229    return APPLE_GLX_DRAWABLE_PBUFFER == d->type;
230 }
231
232 static bool
233 is_pixmap(struct apple_glx_drawable *d)
234 {
235    return APPLE_GLX_DRAWABLE_PIXMAP == d->type;
236 }
237
238 static void
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
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
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
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
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
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 *
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 *
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
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 *
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 }