feccea6320be23b8161946ec0606e33706f03657
[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[20];
112     int i = 0;
113     attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
114     attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
115     attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 16;
116
117     if (get_boolean_resource (mi->dpy, "doubleBuffer", "DoubleBuffer"))
118       attrs[i++] = NSOpenGLPFADoubleBuffer;
119
120     attrs[i] = 0;
121
122     NSOpenGLPixelFormat *pixfmt = [[NSOpenGLPixelFormat alloc] 
123                                     initWithAttributes:attrs];
124
125     ctx = [[NSOpenGLContext alloc] 
126             initWithFormat:pixfmt
127               shareContext:nil];
128 //    [pixfmt release]; // #### ???
129   }
130
131   // Sync refreshes to the vertical blanking interval
132   GLint r = 1;
133   [ctx setValues:&r forParameter:NSOpenGLCPSwapInterval];
134   check_gl_error ("NSOpenGLCPSwapInterval");
135
136   // #### "Build and Analyze" says that ctx leaks, because it doesn't
137   //      seem to realize that makeCurrentContext retains it (right?)
138   //      Not sure what to do to make this warning go away.
139
140   [ctx makeCurrentContext];
141   check_gl_error ("makeCurrentContext");
142
143   [view setOglContext:ctx];
144
145   // Clear frame buffer ASAP, else there are bits left over from other apps.
146   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
147 //  glFinish ();
148 //  glXSwapBuffers (mi->dpy, mi->window);
149
150
151   // Enable multi-threading, if possible.  This runs most OpenGL commands
152   // and GPU management on a second CPU.
153   {
154 #   ifndef  kCGLCEMPEngine
155 #    define kCGLCEMPEngine 313  // Added in MacOS 10.4.8 + XCode 2.4.
156 #   endif
157     CGLContextObj cctx = CGLGetCurrentContext();
158     CGLError err = CGLEnable (cctx, kCGLCEMPEngine);
159     if (err != kCGLNoError) {
160       NSLog (@"enabling multi-threaded OpenGL failed: %d", err);
161     }
162   }
163
164   // Caller expects a pointer to an opaque struct...  which it dereferences.
165   // Don't ask me, it's historical...
166   static int blort = -1;
167   return (void *) &blort;
168 }
169
170
171 /* Copy the back buffer to the front buffer.
172  */
173 void
174 glXSwapBuffers (Display *dpy, Window window)
175 {
176   XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
177   NSOpenGLContext *ctx = [view oglContext];
178   if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
179 }
180
181 /* Does nothing - prepareContext already did the work.
182  */
183 void
184 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
185 {
186 }
187
188
189 /* clear away any lingering error codes */
190 void
191 clear_gl_error (void)
192 {
193   while (glGetError() != GL_NO_ERROR)
194     ;
195 }
196
197
198 /* report a GL error. */
199 void
200 check_gl_error (const char *type)
201 {
202   char buf[100];
203   GLenum i;
204   const char *e;
205   switch ((i = glGetError())) {
206     case GL_NO_ERROR: return;
207     case GL_INVALID_ENUM:          e = "invalid enum";      break;
208     case GL_INVALID_VALUE:         e = "invalid value";     break;
209     case GL_INVALID_OPERATION:     e = "invalid operation"; break;
210     case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
211     case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
212     case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
213 #ifdef GL_TABLE_TOO_LARGE_EXT
214     case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
215 #endif
216 #ifdef GL_TEXTURE_TOO_LARGE_EXT
217     case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
218 #endif
219     default:
220       e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
221   }
222   NSLog (@"%s GL error: %s", type, e);
223   exit (1);
224 }