1 /* xscreensaver, Copyright (c) 2006-2016 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 "jwxyz-cocoa.h"
22 #import "screenhackI.h"
23 #import "xlockmoreI.h"
27 # import <OpenGLES/ES1/glext.h>
29 # import <OpenGL/OpenGL.h>
32 /* used by the OpenGL screen savers
34 extern GLXContext *init_GL (ModeInfo *);
35 extern void glXSwapBuffers (Display *, Window);
36 extern void glXMakeCurrent (Display *, Window, GLXContext);
37 extern void clear_gl_error (void);
38 extern void check_gl_error (const char *type);
41 @implementation XScreenSaverGLView
45 /* With GL programs, drawing at full resolution isn't a problem.
47 - (CGFloat) hackedContentScaleFactor
49 return [self contentScaleFactor];
52 - (BOOL)ignoreRotation
54 return FALSE; // Allow xwindow and the glViewport to change shape
57 - (BOOL) suppressRotationAnimation
59 return _suppressRotationAnimation; // per-hack setting, default FALSE
62 - (BOOL) rotateTouches
64 return TRUE; // We need the XY axes swapped in our events
71 GLint gl_renderbuffer = xwindow->gl_renderbuffer;
73 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
74 [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES];
79 - (void) animateOneFrame
81 # if defined USE_IPHONE && defined JWXYZ_QUARTZ
82 UIGraphicsPushContext (backbuffer);
87 # if defined USE_IPHONE && defined JWXYZ_QUARTZ
88 UIGraphicsPopContext();
93 /* GL screenhacks don't display a backbuffer, so this is a stub. */
94 - (void) enableBackbuffer:(CGSize)new_backbuffer_size
99 /* GL screenhacks set their own viewport and matrices. */
107 /* Keep the GL scene oriented into a portrait-mode View, regardless of
108 what the physical device orientation is.
114 glMatrixMode(GL_PROJECTION);
115 glRotatef (-current_device_rotation(), 0, 0, 1);
116 glMatrixMode(GL_MODELVIEW);
121 BOOL was_initted_p = initted_p;
124 if (! was_initted_p && xdpy)
125 _suppressRotationAnimation =
126 get_boolean_resource (xdpy,
127 "suppressRotationAnimation",
128 "SuppressRotationAnimation");
135 /* The backbuffer isn't actually used for GL programs, but it needs to
136 be there for X11 calls to not error out. However, nothing done with
137 X11 calls will ever show up! It all gets written into the backbuffer
138 and discarded. That's ok, though, because mostly it's just calls to
139 XClearWindow and housekeeping stuff like that. So we make a tiny one.
141 - (void) createBackbuffer:(CGSize)new_size
144 NSAssert (! backbuffer_texture,
145 @"backbuffer_texture shouldn't be used for GL hacks");
148 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
151 backbuffer = CGBitmapContextCreate (NULL, w, h, // yup, only 8px x 8px.
153 (kCGBitmapByteOrder32Little |
154 kCGImageAlphaNoneSkipLast));
155 CGColorSpaceRelease (cs);
157 #endif // JWXYZ_QUARTZ
161 /* Another stub for GL screenhacks. */
162 - (void) drawBackbuffer
167 /* Likewise. GL screenhacks control display with glXSwapBuffers(). */
168 - (void) flushBackbuffer
175 - (NSOpenGLPixelFormat *) getGLPixelFormat
177 NSOpenGLPixelFormatAttribute attrs[40];
179 attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
180 attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
181 attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
183 if ([prefsReader getBooleanResource:"doubleBuffer"])
184 attrs[i++] = NSOpenGLPFADoubleBuffer;
186 Bool ms_p = [prefsReader getBooleanResource:"multiSample"];
188 /* Sometimes, turning on multisampling kills performance. At one point,
189 I thought the answer was, "only run multisampling on one screen, and
190 leave it turned off on other screens". That's what this code does,
191 but it turns out, that solution is insufficient. I can't really tell
192 what causes poor performance with multisampling, but it's not
193 predictable. Without changing the code, some times a given saver will
194 perform fine with multisampling on, and other times it will perform
195 very badly. Without multisampling, they always perform fine.
197 // if (ms_p && [[view window] screen] != [[NSScreen screens] objectAtIndex:0])
201 attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
202 attrs[i++] = NSOpenGLPFASamples; attrs[i++] = 6;
203 // Don't really understand what this means:
204 // attrs[i++] = NSOpenGLPFANoRecovery;
207 attrs[i++] = NSOpenGLPFAWindow;
209 attrs[i++] = NSOpenGLPFAPixelBuffer;
214 NSOpenGLPixelFormat *result = [[NSOpenGLPixelFormat alloc]
215 initWithAttributes:attrs];
217 if (ms_p && !result) { // Retry without multisampling.
220 result = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
223 return [result autorelease];
228 - (NSDictionary *)getGLProperties
230 Bool dbuf_p = [prefsReader getBooleanResource:"doubleBuffer"];
232 /* There seems to be no way to actually turn off double-buffering in
233 EAGLContext (e.g., no way to draw to the front buffer directly)
234 but if we turn on "retained backing" for non-buffering apps like
235 "pipes", at least the back buffer isn't auto-cleared on them.
238 return [NSDictionary dictionaryWithObjectsAndKeys:
239 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
240 [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking,
244 - (void)addExtraRenderbuffers:(CGSize)size
249 if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer);
251 glGenRenderbuffersOES (1, &gl_depthbuffer);
252 // [EAGLContext renderbufferStorage:fromDrawable:] must be called before this.
253 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer);
254 glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
256 glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
257 GL_RENDERBUFFER_OES, gl_depthbuffer);
260 - (NSString *)getCAGravity
262 return kCAGravityCenter;
265 - (void) startAnimation
267 [super startAnimation];
268 if (ogl_ctx) /* Almost always true. */
269 _glesState = jwzgles_make_state ();
272 - (void) stopAnimation
274 [super stopAnimation];
277 [EAGLContext setCurrentContext:ogl_ctx];
278 jwzgles_make_current (_glesState);
279 jwzgles_free_state ();
284 - (void) prepareContext
286 [super prepareContext];
287 jwzgles_make_current (_glesState);
290 #endif // !USE_IPHONE
304 /* Utility functions...
308 // redefine NSAssert, etc. here since they don't work when not inside
315 jwxyz_abort ("%s", [(S) cStringUsingEncoding:NSUTF8StringEncoding])
316 #define NSAssert(CC,S) do { if (!(CC)) { NSASS((S)); }} while(0)
317 #define NSAssert1(CC,S,A) do { if (!(CC)) { \
318 NSASS(([NSString stringWithFormat: S, A])); }} while(0)
319 #define NSAssert2(CC,S,A,B) do { if (!(CC)) { \
320 NSASS(([NSString stringWithFormat: S, A, B])); }} while(0)
323 /* Called by OpenGL savers using the XLockmore API.
326 init_GL (ModeInfo *mi)
328 Window win = mi->window;
329 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
330 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
331 @"wrong view class: %@", view);
333 // OpenGL initialization is in [XScreenSaverView startAnimation].
335 // I don't know why this is necessary, but it beats randomly having some
336 // textures be upside down.
338 glMatrixMode(GL_TEXTURE);
340 glMatrixMode(GL_PROJECTION);
342 glMatrixMode(GL_MODELVIEW);
345 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
347 // Caller expects a pointer to an opaque struct... which it dereferences.
348 // Don't ask me, it's historical...
349 static int blort = -1;
350 return (void *) &blort;
354 /* Copy the back buffer to the front buffer.
357 glXSwapBuffers (Display *dpy, Window window)
359 // This all is very much like what's in -[XScreenSaverView flushBackbuffer].
361 jwxyz_bind_drawable (window, window);
364 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
365 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
366 @"wrong view class: %@", view);
368 NSOpenGLContext *ctx = [view oglContext];
369 if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
370 #else /* USE_IPHONE */
372 #endif /* USE_IPHONE */
375 /* Does nothing - prepareContext already did the work.
378 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
383 /* clear away any lingering error codes */
385 clear_gl_error (void)
387 while (glGetError() != GL_NO_ERROR)
392 #if defined GL_INVALID_FRAMEBUFFER_OPERATION_OES && \
393 !defined GL_INVALID_FRAMEBUFFER_OPERATION
394 # define GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES
398 /* report a GL error. */
400 check_gl_error (const char *type)
405 switch ((i = glGetError())) {
406 case GL_NO_ERROR: return;
407 case GL_INVALID_ENUM: e = "invalid enum"; break;
408 case GL_INVALID_VALUE: e = "invalid value"; break;
409 case GL_INVALID_OPERATION: e = "invalid operation"; break;
410 case GL_STACK_OVERFLOW: e = "stack overflow"; break;
411 case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
412 case GL_OUT_OF_MEMORY: e = "out of memory"; break;
413 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
414 case GL_INVALID_FRAMEBUFFER_OPERATION:
415 e = "invalid framebuffer operation";
418 #ifdef GL_TABLE_TOO_LARGE_EXT
419 case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
421 #ifdef GL_TEXTURE_TOO_LARGE_EXT
422 case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
425 e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
427 NSAssert2 (0, @"%s GL error: %s", type, e);