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