http://www.jwz.org/xscreensaver/xscreensaver-5.09.tar.gz
[xscreensaver] / OSX / XScreenSaverGLView.m
1 /* xscreensaver, Copyright (c) 2006-2009 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 #if 0
37 - (void) dealloc
38 {
39   /* #### Do we have to destroy ogl_ctx? */
40   [super dealloc];
41 }
42 #endif
43
44
45 - (void)stopAnimation
46 {
47   [super stopAnimation];
48   
49   // Without this, the GL frame stays on screen when switching tabs
50   // in System Preferences.
51   //
52   [NSOpenGLContext clearCurrentContext];
53 }
54
55
56 // #### maybe this could/should just be on 'lockFocus' instead?
57 - (void) prepareContext
58 {
59   if (ogl_ctx) {
60     [ogl_ctx makeCurrentContext];
61 //    check_gl_error ("makeCurrentContext");
62     [ogl_ctx update];
63   }
64 }
65
66
67 - (void) resizeContext
68 {
69   if (ogl_ctx) 
70     [ogl_ctx setView:self];
71 }
72
73
74 - (void)drawRect:(NSRect)rect
75 {
76   if (! ogl_ctx)
77     [super drawRect:rect];
78 }
79
80
81 - (NSOpenGLContext *) oglContext
82 {
83   return ogl_ctx;
84 }
85
86
87 - (void) setOglContext: (NSOpenGLContext *) ctx
88 {
89   ogl_ctx = ctx;
90   [self resizeContext];
91 }
92
93 @end
94
95
96 /* Utility functions...
97  */
98
99
100 /* Called by OpenGL savers using the XLockmore API.
101  */
102 GLXContext *
103 init_GL (ModeInfo *mi)
104 {
105   Window win = mi->window;
106   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
107   NSOpenGLContext *ctx = [view oglContext];
108
109   if (!ctx) {
110
111     NSOpenGLPixelFormatAttribute attrs[] = {
112       NSOpenGLPFADoubleBuffer,
113       NSOpenGLPFAColorSize, 24,
114       NSOpenGLPFAAlphaSize, 8,
115       NSOpenGLPFADepthSize, 16,
116       0 };
117     NSOpenGLPixelFormat *pixfmt = [[NSOpenGLPixelFormat alloc] 
118                                     initWithAttributes:attrs];
119
120     ctx = [[NSOpenGLContext alloc] 
121             initWithFormat:pixfmt
122               shareContext:nil];
123   }
124
125   // Sync refreshes to the vertical blanking interval
126   GLint r = 1;
127   [ctx setValues:&r forParameter:NSOpenGLCPSwapInterval];
128
129   [ctx makeCurrentContext];
130   check_gl_error ("makeCurrentContext");
131
132   [view setOglContext:ctx];
133
134   // Clear frame buffer ASAP, else there are bits left over from other apps.
135   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
136 //  glFinish ();
137 //  glXSwapBuffers (mi->dpy, mi->window);
138
139
140   // Enable multi-threading, if possible.  This runs most OpenGL commands
141   // and GPU management on a second CPU.
142   {
143 #   ifndef  kCGLCEMPEngine
144 #    define kCGLCEMPEngine 313  // Added in MacOS 10.4.8 + XCode 2.4.
145 #   endif
146     CGLContextObj cctx = CGLGetCurrentContext();
147     CGLError err = CGLEnable (cctx, kCGLCEMPEngine);
148     if (err != kCGLNoError) {
149       NSLog (@"enabling multi-threaded OpenGL failed: %d", err);
150     }
151   }
152
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   NSOpenGLContext *ctx = [view oglContext];
168   if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
169 }
170
171 /* Does nothing - prepareContext already did the work.
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 }
186
187
188 /* report a GL error. */
189 void
190 check_gl_error (const char *type)
191 {
192   char buf[100];
193   GLenum i;
194   const char *e;
195   switch ((i = glGetError())) {
196     case GL_NO_ERROR: return;
197     case GL_INVALID_ENUM:          e = "invalid enum";      break;
198     case GL_INVALID_VALUE:         e = "invalid value";     break;
199     case GL_INVALID_OPERATION:     e = "invalid operation"; break;
200     case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
201     case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
202     case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
203 #ifdef GL_TABLE_TOO_LARGE_EXT
204     case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
205 #endif
206 #ifdef GL_TEXTURE_TOO_LARGE_EXT
207     case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
208 #endif
209     default:
210       e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
211   }
212   NSLog (@"%s GL error: %s", type, e);
213   exit (1);
214 }