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 <limits.h>
34#include <assert.h>
35#include <pthread.h>
36
37#include <fcntl.h>
38#include <sys/mman.h>
39#include <unistd.h>
40
41// Get the newer glext.h first
42#include <GL/gl.h>
43#include <GL/glext.h>
44
45#include <OpenGL/CGLTypes.h>
46#include <OpenGL/CGLCurrent.h>
47#include <OpenGL/OpenGL.h>
48
49#include "glxclient.h"
50
51#include "apple_glx.h"
52#include "apple_glx_context.h"
53#include "appledri.h"
54#include "apple_visual.h"
55#include "apple_cgl.h"
56#include "apple_glx_drawable.h"
57
58static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER;
59
60/*
61 * This should be locked on creation and destruction of the
62 * apple_glx_contexts.
63 *
64 * It's also locked when the surface_notify_handler is searching
65 * for a uid associated with a surface.
66 */
67static struct apple_glx_context *context_list = NULL;
68
69/* This guards the context_list above. */
70static void
71lock_context_list(void)
72{
73   int err;
74
75   err = pthread_mutex_lock(&context_lock);
76
77   if (err) {
78      fprintf(stderr, "pthread_mutex_lock failure in %s: %d\n",
79              __func__, err);
80      abort();
81   }
82}
83
84static void
85unlock_context_list(void)
86{
87   int err;
88
89   err = pthread_mutex_unlock(&context_lock);
90
91   if (err) {
92      fprintf(stderr, "pthread_mutex_unlock failure in %s: %d\n",
93              __func__, err);
94      abort();
95   }
96}
97
98static bool
99is_context_valid(struct apple_glx_context *ac)
100{
101   struct apple_glx_context *i;
102
103   lock_context_list();
104
105   for (i = context_list; i; i = i->next) {
106      if (ac == i) {
107         unlock_context_list();
108         return true;
109      }
110   }
111
112   unlock_context_list();
113
114   return false;
115}
116
117/* This creates an apple_private_context struct.
118 *
119 * It's typically called to save the struct in a GLXContext.
120 *
121 * This is also where the CGLContextObj is created, and the CGLPixelFormatObj.
122 */
123bool
124apple_glx_create_context(void **ptr, Display * dpy, int screen,
125                         const void *mode, void *sharedContext,
126                         int *errorptr, bool * x11errorptr)
127{
128   struct apple_glx_context *ac;
129   struct apple_glx_context *sharedac = sharedContext;
130   CGLError error;
131
132   *ptr = NULL;
133
134   ac = malloc(sizeof *ac);
135
136   if (NULL == ac) {
137      *errorptr = BadAlloc;
138      *x11errorptr = true;
139      return true;
140   }
141
142   if (sharedac && !is_context_valid(sharedac)) {
143      *errorptr = GLXBadContext;
144      *x11errorptr = false;
145      free(ac);
146      return true;
147   }
148
149   ac->context_obj = NULL;
150   ac->pixel_format_obj = NULL;
151   ac->drawable = NULL;
152   ac->thread_id = pthread_self();
153   ac->screen = screen;
154   ac->double_buffered = false;
155   ac->uses_stereo = false;
156   ac->need_update = false;
157   ac->is_current = false;
158   ac->made_current = false;
159   ac->last_surface_window = None;
160
161   apple_visual_create_pfobj(&ac->pixel_format_obj, mode,
162                             &ac->double_buffered, &ac->uses_stereo,
163                             /*offscreen */ false);
164
165   error = apple_cgl.create_context(ac->pixel_format_obj,
166                                    sharedac ? sharedac->context_obj : NULL,
167                                    &ac->context_obj);
168
169
170   if (error) {
171      (void) apple_cgl.destroy_pixel_format(ac->pixel_format_obj);
172
173      free(ac);
174
175      if (kCGLBadMatch == error) {
176         *errorptr = BadMatch;
177         *x11errorptr = true;
178      }
179      else {
180         *errorptr = GLXBadContext;
181         *x11errorptr = false;
182      }
183
184      if (getenv("LIBGL_DIAGNOSTIC"))
185         fprintf(stderr, "error: %s\n", apple_cgl.error_string(error));
186
187      return true;
188   }
189
190   /* The context creation succeeded, so we can link in the new context. */
191   lock_context_list();
192
193   if (context_list)
194      context_list->previous = ac;
195
196   ac->previous = NULL;
197   ac->next = context_list;
198   context_list = ac;
199
200   *ptr = ac;
201
202   apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
203                        __func__, (void *) ac, (void *) ac->context_obj);
204
205   unlock_context_list();
206
207   return false;
208}
209
210void
211apple_glx_destroy_context(void **ptr, Display * dpy)
212{
213   struct apple_glx_context *ac = *ptr;
214
215   if (NULL == ac)
216      return;
217
218   apple_glx_diagnostic("%s: ac %p ac->context_obj %p\n",
219                        __func__, (void *) ac, (void *) ac->context_obj);
220
221   if (apple_cgl.get_current_context() == ac->context_obj) {
222      apple_glx_diagnostic("%s: context ac->context_obj %p "
223                           "is still current!\n", __func__,
224                           (void *) ac->context_obj);
225      if (apple_cgl.set_current_context(NULL)) {
226         abort();
227      }
228   }
229
230   /* Remove ac from the context_list as soon as possible. */
231   lock_context_list();
232
233   if (ac->previous) {
234      ac->previous->next = ac->next;
235   }
236   else {
237      context_list = ac->next;
238   }
239
240   if (ac->next) {
241      ac->next->previous = ac->previous;
242   }
243
244   unlock_context_list();
245
246
247   if (apple_cgl.clear_drawable(ac->context_obj)) {
248      fprintf(stderr, "error: while clearing drawable!\n");
249      abort();
250   }
251
252   /*
253    * This potentially causes surface_notify_handler to be called in
254    * apple_glx.c...
255    * We can NOT have a lock held at this point.  It would result in
256    * an abort due to an attempted deadlock.  This is why we earlier
257    * removed the ac pointer from the double-linked list.
258    */
259   if (ac->drawable) {
260      ac->drawable->destroy(ac->drawable);
261   }
262
263   if (apple_cgl.destroy_pixel_format(ac->pixel_format_obj)) {
264      fprintf(stderr, "error: destroying pixel format in %s\n", __func__);
265      abort();
266   }
267
268   if (apple_cgl.destroy_context(ac->context_obj)) {
269      fprintf(stderr, "error: destroying context_obj in %s\n", __func__);
270      abort();
271   }
272
273   free(ac);
274
275   *ptr = NULL;
276
277   apple_glx_garbage_collect_drawables(dpy);
278}
279
280
281/* Return true if an error occurred. */
282bool
283apple_glx_make_current_context(Display * dpy, void *oldptr, void *ptr,
284                               GLXDrawable drawable)
285{
286   struct apple_glx_context *oldac = oldptr;
287   struct apple_glx_context *ac = ptr;
288   struct apple_glx_drawable *newagd = NULL;
289   CGLError cglerr;
290   bool same_drawable = false;
291
292#if 0
293   apple_glx_diagnostic("%s: oldac %p ac %p drawable 0x%lx\n",
294                        __func__, (void *) oldac, (void *) ac, drawable);
295
296   apple_glx_diagnostic("%s: oldac->context_obj %p ac->context_obj %p\n",
297                        __func__,
298                        (void *) (oldac ? oldac->context_obj : NULL),
299                        (void *) (ac ? ac->context_obj : NULL));
300#endif
301
302   /* This a common path for GLUT and other apps, so special case it. */
303   if (ac && ac->drawable && ac->drawable->drawable == drawable) {
304      same_drawable = true;
305
306      if (ac->is_current)
307         return false;
308   }
309
310   /* Reset the is_current state of the old context, if non-NULL. */
311   if (oldac && (ac != oldac))
312      oldac->is_current = false;
313
314   if (NULL == ac) {
315      /*Clear the current context for this thread. */
316      apple_cgl.set_current_context(NULL);
317
318      if (oldac) {
319         oldac->is_current = false;
320
321         if (oldac->drawable) {
322            oldac->drawable->destroy(oldac->drawable);
323            oldac->drawable = NULL;
324         }
325
326         /* Invalidate this to prevent surface recreation. */
327         oldac->last_surface_window = None;
328      }
329
330      return false;
331   }
332
333   if (None == drawable) {
334      bool error = false;
335
336      /* Clear the current drawable for this context_obj. */
337
338      if (apple_cgl.set_current_context(ac->context_obj))
339         error = true;
340
341      if (apple_cgl.clear_drawable(ac->context_obj))
342         error = true;
343
344      if (ac->drawable) {
345         ac->drawable->destroy(ac->drawable);
346         ac->drawable = NULL;
347      }
348
349      /* Invalidate this to prevent surface recreation. */
350      ac->last_surface_window = None;
351
352      apple_glx_diagnostic("%s: drawable is None, error is: %d\n",
353                           __func__, error);
354
355      return error;
356   }
357
358   /* This is an optimisation to avoid searching for the current drawable. */
359   if (ac->drawable && ac->drawable->drawable == drawable) {
360      newagd = ac->drawable;
361   }
362   else {
363      /* Find the drawable if possible, and retain a reference to it. */
364      newagd =
365         apple_glx_drawable_find(drawable, APPLE_GLX_DRAWABLE_REFERENCE);
366   }
367
368   /*
369    * Try to destroy the old drawable, so long as the new one
370    * isn't the old.
371    */
372   if (ac->drawable && !same_drawable) {
373      ac->drawable->destroy(ac->drawable);
374      ac->drawable = NULL;
375   }
376
377   if (NULL == newagd) {
378      if (apple_glx_surface_create(dpy, ac->screen, drawable, &newagd))
379         return true;
380
381      /* The drawable is referenced once by apple_glx_surface_create. */
382
383      /*
384       * FIXME: We actually need 2 references to prevent premature surface
385       * destruction.  The problem is that the surface gets destroyed in
386       * the case of the context being reused for another window, and
387       * we then lose the surface contents.  Wait for destruction of a
388       * window to destroy a surface.
389       *
390       * Note: this may leave around surfaces we don't want around, if
391       * say we are using X for raster drawing after OpenGL rendering,
392       * but it will be compatible with the old libGL's behavior.
393       *
394       * Someday the X11 and OpenGL rendering must be unified at some
395       * layer.  I suspect we can do that via shared memory and
396       * multiple threads in the X server (1 for each context created
397       * by a client).  This would also allow users to render from
398       * multiple clients to the same OpenGL surface.  In fact it could
399       * all be OpenGL.
400       *
401       */
402      newagd->reference(newagd);
403
404      /* Save the new drawable with the context structure. */
405      ac->drawable = newagd;
406   }
407   else {
408      /* We are reusing an existing drawable structure. */
409
410      if (same_drawable) {
411         assert(ac->drawable == newagd);
412         /* The drawable_find above retained a reference for us. */
413      }
414      else {
415         ac->drawable = newagd;
416      }
417   }
418
419   /*
420    * Avoid this costly path if this is the same drawable and the
421    * context is already current.
422    */
423
424   if (same_drawable && ac->is_current) {
425      apple_glx_diagnostic("same_drawable and ac->is_current\n");
426      return false;
427   }
428
429   cglerr = apple_cgl.set_current_context(ac->context_obj);
430
431   if (kCGLNoError != cglerr) {
432      fprintf(stderr, "set current error: %s\n",
433              apple_cgl.error_string(cglerr));
434      return true;
435   }
436
437   ac->is_current = true;
438
439   assert(NULL != ac->context_obj);
440   assert(NULL != ac->drawable);
441
442   ac->thread_id = pthread_self();
443
444   /* This will be set if the pending_destroy code indicates it should be: */
445   ac->last_surface_window = None;
446
447   switch (ac->drawable->type) {
448   case APPLE_GLX_DRAWABLE_PBUFFER:
449   case APPLE_GLX_DRAWABLE_SURFACE:
450   case APPLE_GLX_DRAWABLE_PIXMAP:
451      if (ac->drawable->callbacks.make_current) {
452         if (ac->drawable->callbacks.make_current(ac, ac->drawable))
453            return true;
454      }
455      break;
456
457   default:
458      fprintf(stderr, "internal error: invalid drawable type: %d\n",
459              ac->drawable->type);
460      abort();
461   }
462
463   return false;
464}
465
466bool
467apple_glx_is_current_drawable(Display * dpy, void *ptr, GLXDrawable drawable)
468{
469   struct apple_glx_context *ac = ptr;
470
471   if (ac->drawable && ac->drawable->drawable == drawable) {
472      return true;
473   }
474   else if (NULL == ac->drawable && None != ac->last_surface_window) {
475      apple_glx_context_update(dpy, ac);
476
477      return (ac->drawable && ac->drawable->drawable == drawable);
478   }
479
480   return false;
481}
482
483bool
484apple_glx_copy_context(void *currentptr, void *srcptr, void *destptr,
485                       unsigned long mask, int *errorptr, bool * x11errorptr)
486{
487   struct apple_glx_context *src, *dest;
488   CGLError err;
489
490   src = srcptr;
491   dest = destptr;
492
493   if (src->screen != dest->screen) {
494      *errorptr = BadMatch;
495      *x11errorptr = true;
496      return true;
497   }
498
499   if (dest == currentptr || dest->is_current) {
500      *errorptr = BadAccess;
501      *x11errorptr = true;
502      return true;
503   }
504
505   /*
506    * If srcptr is the current context then we should do an implicit glFlush.
507    */
508   if (currentptr == srcptr)
509      glFlush();
510
511   err = apple_cgl.copy_context(src->context_obj, dest->context_obj,
512                                (GLbitfield) mask);
513
514   if (kCGLNoError != err) {
515      *errorptr = GLXBadContext;
516      *x11errorptr = false;
517      return true;
518   }
519
520   return false;
521}
522
523/*
524 * The value returned is the total number of contexts set to update.
525 * It's meant for debugging/introspection.
526 */
527int
528apple_glx_context_surface_changed(unsigned int uid, pthread_t caller)
529{
530   struct apple_glx_context *ac;
531   int updated = 0;
532
533   lock_context_list();
534
535   for (ac = context_list; ac; ac = ac->next) {
536      if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type
537          && ac->drawable->types.surface.uid == uid) {
538
539         if (caller == ac->thread_id) {
540            apple_glx_diagnostic("caller is the same thread for uid %u\n",
541                                 uid);
542
543            xp_update_gl_context(ac->context_obj);
544         }
545         else {
546            ac->need_update = true;
547            ++updated;
548         }
549      }
550   }
551
552   unlock_context_list();
553
554   return updated;
555}
556
557void
558apple_glx_context_update(Display * dpy, void *ptr)
559{
560   struct apple_glx_context *ac = ptr;
561
562   if (NULL == ac->drawable && None != ac->last_surface_window) {
563      bool failed;
564
565      /* Attempt to recreate the surface for a destroyed drawable. */
566      failed =
567         apple_glx_make_current_context(dpy, ac, ac, ac->last_surface_window);
568
569      apple_glx_diagnostic("%s: surface recreation failed? %s\n", __func__,
570                           failed ? "YES" : "NO");
571   }
572
573   if (ac->need_update) {
574      xp_update_gl_context(ac->context_obj);
575      ac->need_update = false;
576
577      apple_glx_diagnostic("%s: updating context %p\n", __func__, ptr);
578   }
579
580   if (ac->drawable && APPLE_GLX_DRAWABLE_SURFACE == ac->drawable->type
581       && ac->drawable->types.surface.pending_destroy) {
582      apple_glx_diagnostic("%s: clearing drawable %p\n", __func__, ptr);
583      apple_cgl.clear_drawable(ac->context_obj);
584
585      if (ac->drawable) {
586         struct apple_glx_drawable *d;
587
588         apple_glx_diagnostic("%s: attempting to destroy drawable %p\n",
589                              __func__, ptr);
590         apple_glx_diagnostic("%s: ac->drawable->drawable is 0x%lx\n",
591                              __func__, ac->drawable->drawable);
592
593         d = ac->drawable;
594
595         ac->last_surface_window = d->drawable;
596
597         ac->drawable = NULL;
598
599         /*
600          * This will destroy the surface drawable if there are
601          * no references to it.
602          * It also subtracts 1 from the reference_count.
603          * If there are references to it, then it's probably made
604          * current in another context.
605          */
606         d->destroy(d);
607      }
608   }
609}
610
611bool
612apple_glx_context_uses_stereo(void *ptr)
613{
614   struct apple_glx_context *ac = ptr;
615
616   return ac->uses_stereo;
617}
618