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