http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / OSX / XScreenSaverGLView.m
1 /* xscreensaver, Copyright (c) 2006, 2007 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 #import <OpenGL/OpenGL.h>
24
25 /* used by the OpenGL screen savers
26  */
27 extern GLXContext *init_GL (ModeInfo *);
28 extern void glXSwapBuffers (Display *, Window);
29 extern void glXMakeCurrent (Display *, Window, GLXContext);
30 extern void clear_gl_error (void);
31 extern void check_gl_error (const char *type);
32
33
34 @implementation XScreenSaverGLView
35
36 - (void) dealloc
37 {
38   if (agl_ctx)
39     aglDestroyContext (agl_ctx);
40   [super dealloc];
41 }
42
43
44 - (void)stopAnimation
45 {
46   [super stopAnimation];
47   
48   // Without this, the GL frame stays on screen when switching tabs
49   // in System Preferences.
50   //
51   if (agl_ctx) {
52     aglSetCurrentContext (NULL);
53     aglDestroyContext (agl_ctx);
54     agl_ctx = 0;
55   }
56 }
57
58
59 // #### maybe this could/should just be on 'lockFocus' instead?
60 - (void) prepareContext
61 {
62   if (agl_ctx)
63     if (!aglSetCurrentContext (agl_ctx)) {
64       check_gl_error ("aglSetCurrentContext");
65       abort();
66     }
67 }
68       
69 - (void) resizeContext
70 {
71   if (! agl_ctx) 
72     return;
73
74   /* Constrain the AGL context to the rectangle of this view
75      (not of our parent window).
76    */
77   GLint aglrect[4];
78   NSRect rect = [[[self window] contentView] convertRect:[self frame]
79                                                 fromView:self];
80   aglrect[0] = rect.origin.x;
81   aglrect[1] = rect.origin.y;
82   aglrect[2] = rect.size.width;
83   aglrect[3] = rect.size.height;
84   aglEnable (agl_ctx, AGL_BUFFER_RECT);
85   aglSetInteger (agl_ctx, AGL_BUFFER_RECT, aglrect);
86   aglUpdateContext (agl_ctx);
87 }
88
89
90 - (void)drawRect:(NSRect)rect
91 {
92   if (! agl_ctx)
93     [super drawRect:rect];
94 }
95
96
97 - (AGLContext) aglContext
98 {
99   return agl_ctx;
100 }
101
102 - (void) setAglContext: (AGLContext) ctx
103 {
104   if (agl_ctx)
105     if (! aglDestroyContext (agl_ctx)) {
106       check_gl_error("aglDestroyContext");
107       abort();
108     }
109   agl_ctx = ctx;
110   [self resizeContext];
111 }
112
113 @end
114
115 /* Utility functions...
116  */
117
118
119 /* Called by OpenGL savers using the XLockmore API.
120  */
121 GLXContext *
122 init_GL (ModeInfo *mi)
123 {
124   Window win = mi->window;
125   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
126   
127   GLint agl_attrs[] = {
128     AGL_RGBA,
129     AGL_DOUBLEBUFFER,
130     AGL_DEPTH_SIZE, 16,
131     0 };
132   AGLPixelFormat aglpixf = aglChoosePixelFormat (NULL, 0, agl_attrs);
133   AGLContext ctx = aglCreateContext (aglpixf, NULL);
134   aglDestroyPixelFormat (aglpixf);
135   if (! ctx) {
136     check_gl_error("aglCreateContext");
137     abort();
138   }
139   
140   if (! aglSetDrawable (ctx, GetWindowPort ([[view window] windowRef]))) {
141     check_gl_error("aglSetDrawable");
142     abort();
143   }
144
145   if (! aglSetCurrentContext (ctx)) {
146     check_gl_error("aglSetCurrentContext");
147     abort();
148   }
149
150   [view setAglContext:ctx];
151
152   // Sync refreshes to the vertical blanking interval
153   GLint r = 1;
154   aglSetInteger (ctx, AGL_SWAP_INTERVAL, &r);
155
156   // Enable multi-threading, if possible.  This runs most OpenGL commands
157   // and GPU management on a second CPU.
158   {
159     CGLContextObj cctx = CGLGetCurrentContext();
160 #   define kCGLCEMPEngine 313  // #### new in MacOS 10.4.8 + XCode 2.4
161     CGLError err = CGLEnable (cctx, kCGLCEMPEngine);
162     if (err != kCGLNoError) {
163       NSLog (@"enabling multi-threaded OpenGL failed: %d", err);
164     }
165   }
166
167   // Caller expects a pointer to an opaque struct...  which it dereferences.
168   // Don't ask me, it's historical...
169   static int blort = -1;
170   return (void *) &blort;
171 }
172
173
174 /* Copy the back buffer to the front buffer.
175  */
176 void
177 glXSwapBuffers (Display *dpy, Window window)
178 {
179   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
180   AGLContext ctx = [view aglContext];
181   if (ctx) aglSwapBuffers (ctx);
182 }
183
184 /* Does nothing. 
185  */
186 void
187 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
188 {
189 }
190
191
192 /* clear away any lingering error codes */
193 void
194 clear_gl_error (void)
195 {
196   while (glGetError() != GL_NO_ERROR)
197     ;
198   while (aglGetError() != AGL_NO_ERROR)
199     ;
200 }
201
202 static void
203 check_agl_error (const char *type)
204 {
205   char buf[100];
206   GLenum i;
207   const char *e;
208   switch ((i = aglGetError())) {
209     case AGL_NO_ERROR: return;
210     case AGL_BAD_ATTRIBUTE:        e = "bad attribute"; break;
211     case AGL_BAD_PROPERTY:         e = "bad propery";   break;
212     case AGL_BAD_PIXELFMT:         e = "bad pixelfmt";  break;
213     case AGL_BAD_RENDINFO:         e = "bad rendinfo";  break;
214     case AGL_BAD_CONTEXT:          e = "bad context";   break;
215     case AGL_BAD_DRAWABLE:         e = "bad drawable";  break;
216     case AGL_BAD_GDEV:             e = "bad gdev";      break;
217     case AGL_BAD_STATE:            e = "bad state";     break;
218     case AGL_BAD_VALUE:            e = "bad value";     break;
219     case AGL_BAD_MATCH:            e = "bad match";     break;
220     case AGL_BAD_ENUM:             e = "bad enum";      break;
221     case AGL_BAD_OFFSCREEN:        e = "bad offscreen"; break;
222     case AGL_BAD_FULLSCREEN:       e = "bad fullscreen";break;
223     case AGL_BAD_WINDOW:           e = "bad window";    break;
224     case AGL_BAD_POINTER:          e = "bad pointer";   break;
225     case AGL_BAD_MODULE:           e = "bad module";    break;
226     case AGL_BAD_ALLOC:            e = "bad alloc";     break;
227     case AGL_BAD_CONNECTION:       e = "bad connection";break;
228     default:
229       e = buf; sprintf (buf, "unknown AGL error %d", (int) i); break;
230   }
231   NSLog (@"%s AGL error: %s", type, e);
232   exit (1);
233 }
234
235
236 /* report a GL error. */
237 void
238 check_gl_error (const char *type)
239 {
240   check_agl_error (type);
241   
242   char buf[100];
243   GLenum i;
244   const char *e;
245   switch ((i = glGetError())) {
246     case GL_NO_ERROR: return;
247     case GL_INVALID_ENUM:          e = "invalid enum";      break;
248     case GL_INVALID_VALUE:         e = "invalid value";     break;
249     case GL_INVALID_OPERATION:     e = "invalid operation"; break;
250     case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
251     case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
252     case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
253 #ifdef GL_TABLE_TOO_LARGE_EXT
254     case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
255 #endif
256 #ifdef GL_TEXTURE_TOO_LARGE_EXT
257     case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
258 #endif
259     default:
260       e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
261   }
262   NSLog (@"%s GL error: %s", type, e);
263   exit (1);
264 }