bc07c7fbb071301f43d1ce35cb133aff9e6c09a3
[xscreensaver] / OSX / XScreenSaverGLView.m
1 /* xscreensaver, Copyright (c) 2006 Jamie Zawinski <jwz@jwz.org>
2 *
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation.  No representations are made about the suitability of this
8 * software for any purpose.  It is provided "as is" without express or 
9 * implied warranty.
10 */
11
12 /* This is a subclass of Apple's ScreenSaverView that knows how to run
13    xscreensaver programs without X11 via the dark magic of the "jwxyz"
14    library.  In xscreensaver terminology, this is the replacement for
15    the "screenhack.c" module.
16  */
17
18 #import "XScreenSaverGLView.h"
19 #import "XScreenSaverConfigSheet.h"
20 #import "screenhackI.h"
21 #import "xlockmoreI.h"
22
23 /* used by the OpenGL screen savers
24  */
25 extern GLXContext *init_GL (ModeInfo *);
26 extern void glXSwapBuffers (Display *, Window);
27 extern void glXMakeCurrent (Display *, Window, GLXContext);
28 extern void clear_gl_error (void);
29 extern void check_gl_error (const char *type);
30
31
32 @implementation XScreenSaverGLView
33
34 - (void) dealloc
35 {
36   if (agl_ctx)
37     aglDestroyContext (agl_ctx);
38   [super dealloc];
39 }
40
41
42 - (void)stopAnimation
43 {
44   [super stopAnimation];
45   
46   // Without this, the GL frame stays on screen when switching tabs
47   // in System Preferences.
48   //
49   if (agl_ctx) {
50     aglSetCurrentContext (NULL);
51     aglDestroyContext (agl_ctx);
52     agl_ctx = 0;
53   }
54 }
55
56
57 // #### maybe this could/should just be on 'lockFocus' instead?
58 - (void) prepareContext
59 {
60   if (agl_ctx)
61     if (!aglSetCurrentContext (agl_ctx)) {
62       check_gl_error ("aglSetCurrentContext");
63       abort();
64     }
65 }
66       
67 - (void) resizeContext
68 {
69   if (! agl_ctx) 
70     return;
71
72   /* Constrain the AGL context to the rectangle of this view
73      (not of our parent window).
74    */
75   GLint aglrect[4];
76   NSRect rect = [[[self window] contentView] convertRect:[self frame]
77                                                 fromView:self];
78   aglrect[0] = rect.origin.x;
79   aglrect[1] = rect.origin.y;
80   aglrect[2] = rect.size.width;
81   aglrect[3] = rect.size.height;
82   aglEnable (agl_ctx, AGL_BUFFER_RECT);
83   aglSetInteger (agl_ctx, AGL_BUFFER_RECT, aglrect);
84   aglUpdateContext (agl_ctx);
85 }
86
87
88 - (void)drawRect:(NSRect)rect
89 {
90   if (! agl_ctx)
91     [super drawRect:rect];
92 }
93
94
95 - (AGLContext) aglContext
96 {
97   return agl_ctx;
98 }
99
100 - (void) setAglContext: (AGLContext) ctx
101 {
102   if (agl_ctx)
103     if (! aglDestroyContext (agl_ctx)) {
104       check_gl_error("aglDestroyContext");
105       abort();
106     }
107   agl_ctx = ctx;
108   [self resizeContext];
109 }
110
111 @end
112
113 /* Utility functions...
114  */
115
116
117 /* Called by OpenGL savers using the XLockmore API.
118  */
119 GLXContext *
120 init_GL (ModeInfo *mi)
121 {
122   Window win = mi->window;
123   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
124   
125   GLint agl_attrs[] = {
126     AGL_RGBA,
127     AGL_DOUBLEBUFFER,
128     AGL_DEPTH_SIZE, 16,
129     0 };
130   AGLPixelFormat aglpixf = aglChoosePixelFormat (NULL, 0, agl_attrs);
131   AGLContext ctx = aglCreateContext (aglpixf, NULL);
132   aglDestroyPixelFormat (aglpixf);
133   if (! ctx) {
134     check_gl_error("aglCreateContext");
135     abort();
136   }
137   
138   if (! aglSetDrawable (ctx, GetWindowPort ([[view window] windowRef]))) {
139     check_gl_error("aglSetDrawable");
140     abort();
141   }
142
143   if (! aglSetCurrentContext (ctx)) {
144     check_gl_error("aglSetCurrentContext");
145     abort();
146   }
147
148   [view setAglContext:ctx];
149
150   // Sync refreshes to the vertical blanking interval
151   GLint r = 1;
152   aglSetInteger (ctx, AGL_SWAP_INTERVAL, &r);
153
154   // Caller expects a pointer to an opaque struct...  which it dereferences.
155   // Don't ask me, it's historical...
156   static int blort = -1;
157   return (void *) &blort;
158 }
159
160
161 /* Copy the back buffer to the front buffer.
162  */
163 void
164 glXSwapBuffers (Display *dpy, Window window)
165 {
166   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
167   AGLContext ctx = [view aglContext];
168   if (ctx) aglSwapBuffers (ctx);
169 }
170
171 /* Does nothing. 
172  */
173 void
174 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
175 {
176 }
177
178
179 /* clear away any lingering error codes */
180 void
181 clear_gl_error (void)
182 {
183   while (glGetError() != GL_NO_ERROR)
184     ;
185   while (aglGetError() != AGL_NO_ERROR)
186     ;
187 }
188
189 static void
190 check_agl_error (const char *type)
191 {
192   char buf[100];
193   GLenum i;
194   const char *e;
195   switch ((i = aglGetError())) {
196     case AGL_NO_ERROR: return;
197     case AGL_BAD_ATTRIBUTE:        e = "bad attribute"; break;
198     case AGL_BAD_PROPERTY:         e = "bad propery";   break;
199     case AGL_BAD_PIXELFMT:         e = "bad pixelfmt";  break;
200     case AGL_BAD_RENDINFO:         e = "bad rendinfo";  break;
201     case AGL_BAD_CONTEXT:          e = "bad context";   break;
202     case AGL_BAD_DRAWABLE:         e = "bad drawable";  break;
203     case AGL_BAD_GDEV:             e = "bad gdev";      break;
204     case AGL_BAD_STATE:            e = "bad state";     break;
205     case AGL_BAD_VALUE:            e = "bad value";     break;
206     case AGL_BAD_MATCH:            e = "bad match";     break;
207     case AGL_BAD_ENUM:             e = "bad enum";      break;
208     case AGL_BAD_OFFSCREEN:        e = "bad offscreen"; break;
209     case AGL_BAD_FULLSCREEN:       e = "bad fullscreen";break;
210     case AGL_BAD_WINDOW:           e = "bad window";    break;
211     case AGL_BAD_POINTER:          e = "bad pointer";   break;
212     case AGL_BAD_MODULE:           e = "bad module";    break;
213     case AGL_BAD_ALLOC:            e = "bad alloc";     break;
214     case AGL_BAD_CONNECTION:       e = "bad connection";break;
215     default:
216       e = buf; sprintf (buf, "unknown AGL error %d", (int) i); break;
217   }
218   NSLog (@"%s AGL error: %s\n", type, e);
219   exit (1);
220 }
221
222
223 /* report a GL error. */
224 void
225 check_gl_error (const char *type)
226 {
227   check_agl_error (type);
228   
229   char buf[100];
230   GLenum i;
231   const char *e;
232   switch ((i = glGetError())) {
233     case GL_NO_ERROR: return;
234     case GL_INVALID_ENUM:          e = "invalid enum";      break;
235     case GL_INVALID_VALUE:         e = "invalid value";     break;
236     case GL_INVALID_OPERATION:     e = "invalid operation"; break;
237     case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
238     case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
239     case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
240 #ifdef GL_TABLE_TOO_LARGE_EXT
241     case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
242 #endif
243 #ifdef GL_TEXTURE_TOO_LARGE_EXT
244     case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
245 #endif
246     default:
247       e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
248   }
249   NSLog (@"%s GL error: %s\n", type, e);
250   exit (1);
251 }