1 /* xscreensaver, Copyright (c) 2006-2015 Jamie Zawinski <jwz@jwz.org>
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
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.
18 #import "XScreenSaverGLView.h"
19 #import "XScreenSaverConfigSheet.h"
20 #import "screenhackI.h"
21 #import "xlockmoreI.h"
25 # import <OpenGLES/ES1/glext.h>
27 # import <OpenGL/OpenGL.h>
30 /* used by the OpenGL screen savers
32 extern GLXContext *init_GL (ModeInfo *);
33 extern void glXSwapBuffers (Display *, Window);
34 extern void glXMakeCurrent (Display *, Window, GLXContext);
35 extern void clear_gl_error (void);
36 extern void check_gl_error (const char *type);
39 @implementation XScreenSaverGLView
42 - (NSOpenGLContext *) oglContext
49 /* With GL programs, drawing at full resolution isn't a problem.
51 - (CGFloat) hackedContentScaleFactor
53 NSSize ssize = [[[UIScreen mainScreen] currentMode] size];
54 NSSize bsize = [self bounds].size;
56 // Ratio of screen size in pixels to view size in points.
57 GLfloat s = ((ssize.width > ssize.height ? ssize.width : ssize.height) /
58 (bsize.width > bsize.height ? bsize.width : bsize.height));
67 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
68 [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES];
75 - (void) animateOneFrame
78 UIGraphicsPushContext (backbuffer);
84 UIGraphicsPopContext();
89 /* GL screenhacks don't display a backbuffer, so this is a stub. */
90 - (void) enableBackbuffer:(CGSize)new_backbuffer_size
95 /* The backbuffer isn't actually used for GL programs, but it needs to
96 be there for X11 calls to not error out. However, nothing done with
97 X11 calls will ever show up! It all gets written into the backbuffer
98 and discarded. That's ok, though, because mostly it's just calls to
99 XClearWindow and housekeeping stuff like that. So we make a tiny one.
101 - (void) createBackbuffer:(CGSize)new_size
103 NSAssert (! backbuffer_texture,
104 @"backbuffer_texture shouldn't be used for GL hacks");
106 backbuffer_size = new_size;
109 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
112 backbuffer = CGBitmapContextCreate (NULL, w, h, // yup, only 8px x 8px.
114 (kCGBitmapByteOrder32Little |
115 kCGImageAlphaNoneSkipLast));
116 CGColorSpaceRelease (cs);
121 /* Another stub for GL screenhacks. */
122 - (void) drawBackbuffer
127 /* Likewise. GL screenhacks control display with glXSwapBuffers(). */
128 - (void) flushBackbufer
133 # endif // USE_BACKBUFFER
136 /* When changing the device orientation, leave the X11 Window and glViewport
137 in portrait configuration. OpenGL hacks examine current_device_rotation()
138 within the scene as needed.
140 - (BOOL)reshapeRotatedWindow
148 - (NSOpenGLPixelFormat *) getGLPixelFormat
150 NSOpenGLPixelFormatAttribute attrs[40];
152 attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
153 attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
154 attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
156 if ([prefsReader getBooleanResource:"doubleBuffer"])
157 attrs[i++] = NSOpenGLPFADoubleBuffer;
159 Bool ms_p = [prefsReader getBooleanResource:"multiSample"];
161 /* Sometimes, turning on multisampling kills performance. At one point,
162 I thought the answer was, "only run multisampling on one screen, and
163 leave it turned off on other screens". That's what this code does,
164 but it turns out, that solution is insufficient. I can't really tell
165 what causes poor performance with multisampling, but it's not
166 predictable. Without changing the code, some times a given saver will
167 perform fine with multisampling on, and other times it will perform
168 very badly. Without multisampling, they always perform fine.
170 // if (ms_p && [[view window] screen] != [[NSScreen screens] objectAtIndex:0])
174 attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
175 attrs[i++] = NSOpenGLPFASamples; attrs[i++] = 6;
176 // Don't really understand what this means:
177 // attrs[i++] = NSOpenGLPFANoRecovery;
182 NSOpenGLPixelFormat *pixfmt = [[NSOpenGLPixelFormat alloc]
183 initWithAttributes:attrs];
185 if (ms_p && !pixfmt) { // Retry without multisampling.
188 pixfmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
191 return [pixfmt autorelease];
196 - (NSDictionary *)getGLProperties
198 Bool dbuf_p = [prefsReader getBooleanResource:"doubleBuffer"];
200 /* There seems to be no way to actually turn off double-buffering in
201 EAGLContext (e.g., no way to draw to the front buffer directly)
202 but if we turn on "retained backing" for non-buffering apps like
203 "pipes", at least the back buffer isn't auto-cleared on them.
206 return [NSDictionary dictionaryWithObjectsAndKeys:
207 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
208 [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking,
212 - (void)addExtraRenderbuffers:(CGSize)size
217 if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer);
219 glGenRenderbuffersOES (1, &gl_depthbuffer);
220 // [EAGLContext renderbufferStorage:fromDrawable:] must be called before this.
221 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer);
222 glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
224 glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
225 GL_RENDERBUFFER_OES, gl_depthbuffer);
228 #endif // !USE_IPHONE
242 /* Utility functions...
246 // redefine NSAssert, etc. here since they don't work when not inside
253 jwxyz_abort ("%s", [(S) cStringUsingEncoding:NSUTF8StringEncoding])
254 #define NSAssert(CC,S) do { if (!(CC)) { NSASS((S)); }} while(0)
255 #define NSAssert1(CC,S,A) do { if (!(CC)) { \
256 NSASS(([NSString stringWithFormat: S, A])); }} while(0)
257 #define NSAssert2(CC,S,A,B) do { if (!(CC)) { \
258 NSASS(([NSString stringWithFormat: S, A, B])); }} while(0)
261 /* Called by OpenGL savers using the XLockmore API.
264 init_GL (ModeInfo *mi)
266 Window win = mi->window;
267 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
268 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
269 @"wrong view class: %@", view);
271 // OpenGL initialization is in [XScreenSaverView startAnimation].
275 # endif // USE_IPHONE
277 // I don't know why this is necessary, but it beats randomly having some
278 // textures be upside down.
280 glMatrixMode(GL_TEXTURE);
282 glMatrixMode(GL_PROJECTION);
284 glMatrixMode(GL_MODELVIEW);
287 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
289 // Caller expects a pointer to an opaque struct... which it dereferences.
290 // Don't ask me, it's historical...
291 static int blort = -1;
292 return (void *) &blort;
296 /* Copy the back buffer to the front buffer.
299 glXSwapBuffers (Display *dpy, Window window)
301 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
302 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
303 @"wrong view class: %@", view);
305 NSOpenGLContext *ctx = [view oglContext];
306 if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
307 #else /* USE_IPHONE */
309 #endif /* USE_IPHONE */
312 /* Does nothing - prepareContext already did the work.
315 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
320 /* clear away any lingering error codes */
322 clear_gl_error (void)
324 while (glGetError() != GL_NO_ERROR)
329 #if defined GL_INVALID_FRAMEBUFFER_OPERATION_OES && \
330 !defined GL_INVALID_FRAMEBUFFER_OPERATION
331 # define GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES
335 /* report a GL error. */
337 check_gl_error (const char *type)
342 switch ((i = glGetError())) {
343 case GL_NO_ERROR: return;
344 case GL_INVALID_ENUM: e = "invalid enum"; break;
345 case GL_INVALID_VALUE: e = "invalid value"; break;
346 case GL_INVALID_OPERATION: e = "invalid operation"; break;
347 case GL_STACK_OVERFLOW: e = "stack overflow"; break;
348 case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
349 case GL_OUT_OF_MEMORY: e = "out of memory"; break;
350 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
351 case GL_INVALID_FRAMEBUFFER_OPERATION:
352 e = "invalid framebuffer operation";
355 #ifdef GL_TABLE_TOO_LARGE_EXT
356 case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
358 #ifdef GL_TEXTURE_TOO_LARGE_EXT
359 case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
362 e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
364 NSAssert2 (0, @"%s GL error: %s", type, e);