-/* xscreensaver, Copyright (c) 2006-2014 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2006-2017 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
#import "XScreenSaverGLView.h"
#import "XScreenSaverConfigSheet.h"
+#import "jwxyz-cocoa.h"
+#import "jwxyzI.h"
#import "screenhackI.h"
#import "xlockmoreI.h"
#ifdef USE_IPHONE
# include "jwzgles.h"
+# import <OpenGLES/ES1/glext.h>
#else
# import <OpenGL/OpenGL.h>
#endif
@implementation XScreenSaverGLView
-- (void)stopAnimation
-{
- [super stopAnimation];
-
- // Without this, the GL frame stays on screen when switching tabs
- // in System Preferences.
- //
-# ifndef USE_IPHONE
- [NSOpenGLContext clearCurrentContext];
-# endif // !USE_IPHONE
- clear_gl_error(); // This hack is defunct, don't let this linger.
+/* With GL programs, drawing at full resolution isn't a problem.
+ */
+- (CGFloat) hackedContentScaleFactor
+{
+# ifdef USE_IPHONE
+ return [self contentScaleFactor];
+# else
+ return self.window.backingScaleFactor;
+# endif
}
+# ifdef USE_IPHONE
-// #### maybe this could/should just be on 'lockFocus' instead?
-- (void) prepareContext
+- (BOOL)ignoreRotation
{
- if (ogl_ctx) {
-#ifdef USE_IPHONE
- [EAGLContext setCurrentContext:ogl_ctx];
-#else // !USE_IPHONE
- [ogl_ctx makeCurrentContext];
-// check_gl_error ("makeCurrentContext");
- [ogl_ctx update];
-#endif // !USE_IPHONE
- }
+ return FALSE; // Allow xwindow and the glViewport to change shape
}
-
-- (void) resizeContext
+- (BOOL) suppressRotationAnimation
{
-# ifndef USE_IPHONE
- if (ogl_ctx)
- [ogl_ctx setView:self];
-# endif // !USE_IPHONE
+ return _suppressRotationAnimation; // per-hack setting, default FALSE
}
-
-- (NSOpenGLContext *) oglContext
+- (BOOL) rotateTouches
{
- return ogl_ctx;
+ return TRUE; // We need the XY axes swapped in our events
}
-#ifdef USE_IPHONE
-/* With GL programs, drawing at full resolution isn't a problem.
- */
-- (CGFloat) hackedContentScaleFactor
+- (void) swapBuffers
{
- NSSize ssize = [[[UIScreen mainScreen] currentMode] size];
- NSSize bsize = [self bounds].size;
-
- // Ratio of screen size in pixels to view size in points.
- GLfloat s = ((ssize.width > ssize.height ? ssize.width : ssize.height) /
- (bsize.width > bsize.height ? bsize.width : bsize.height));
- return s;
+# ifdef JWXYZ_GL
+ GLint gl_renderbuffer = xwindow->gl_renderbuffer;
+# endif // JWXYZ_GL
+ glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
+ [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES];
}
#endif // USE_IPHONE
-- (void) setOglContext: (NSOpenGLContext *) ctx
+- (void) animateOneFrame
{
- ogl_ctx = ctx;
-
-# ifdef USE_IPHONE
- [EAGLContext setCurrentContext: ogl_ctx];
-
- double s = [self hackedContentScaleFactor];
- int w = s * [self bounds].size.width;
- int h = s * [self bounds].size.height;
-
- if (gl_framebuffer) glDeleteFramebuffersOES (1, &gl_framebuffer);
- if (gl_renderbuffer) glDeleteRenderbuffersOES (1, &gl_renderbuffer);
- if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer);
-
- glGenFramebuffersOES (1, &gl_framebuffer);
- glBindFramebufferOES (GL_FRAMEBUFFER_OES, gl_framebuffer);
-
- glGenRenderbuffersOES (1, &gl_renderbuffer);
- glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
-
-// redundant?
-// glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_RGBA8_OES, w, h);
- [ogl_ctx renderbufferStorage:GL_RENDERBUFFER_OES
- fromDrawable:(CAEAGLLayer*)self.layer];
-
- glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
- GL_RENDERBUFFER_OES, gl_renderbuffer);
-
- glGenRenderbuffersOES (1, &gl_depthbuffer);
- // renderbufferStorage: must be called before this.
- glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer);
- glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
- w, h);
- glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
- GL_RENDERBUFFER_OES, gl_depthbuffer);
-
- int err = glCheckFramebufferStatusOES (GL_FRAMEBUFFER_OES);
- switch (err) {
- case GL_FRAMEBUFFER_COMPLETE_OES:
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES:
- NSAssert (0, @"framebuffer incomplete attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES:
- NSAssert (0, @"framebuffer incomplete missing attachment");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES:
- NSAssert (0, @"framebuffer incomplete dimensions");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES:
- NSAssert (0, @"framebuffer incomplete formats");
- break;
- case GL_FRAMEBUFFER_UNSUPPORTED_OES:
- NSAssert (0, @"framebuffer unsupported");
- break;
-/*
- case GL_FRAMEBUFFER_UNDEFINED:
- NSAssert (0, @"framebuffer undefined");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
- NSAssert (0, @"framebuffer incomplete draw buffer");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
- NSAssert (0, @"framebuffer incomplete read buffer");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
- NSAssert (0, @"framebuffer incomplete multisample");
- break;
- case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
- NSAssert (0, @"framebuffer incomplete layer targets");
- break;
- */
- default:
- NSAssert (0, @"framebuffer incomplete, unknown error 0x%04X", err);
- break;
- }
-
- check_gl_error ("setOglContext");
+# if defined USE_IPHONE && defined JWXYZ_QUARTZ
+ UIGraphicsPushContext (backbuffer);
+# endif
-# endif // USE_IPHONE
+ [self render_x11];
- [self resizeContext];
+# if defined USE_IPHONE && defined JWXYZ_QUARTZ
+ UIGraphicsPopContext();
+# endif
}
-#ifdef USE_IPHONE
-+ (Class) layerClass
+
+/* GL screenhacks don't display a backbuffer, so this is a stub. */
+- (void) enableBackbuffer:(CGSize)new_backbuffer_size
{
- return [CAEAGLLayer class];
}
-- (void) swapBuffers
+
+/* GL screenhacks set their own viewport and matrices. */
+- (void) setViewport
{
- glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
- [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES];
}
-#endif // USE_IPHONE
-#ifdef USE_BACKBUFFER
+#ifdef USE_IPHONE
-- (void) initLayer
+/* Keep the GL scene oriented into a portrait-mode View, regardless of
+ what the physical device orientation is.
+ */
+- (void) reshape_x11
{
- // Do nothing.
-}
+ [super reshape_x11];
-- (void)drawRect:(NSRect)rect
-{
+ glMatrixMode(GL_PROJECTION);
+ glRotatef (-current_device_rotation(), 0, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
}
-
-- (void) animateOneFrame
+- (void) render_x11
{
-# ifdef USE_IPHONE
- UIGraphicsPushContext (backbuffer);
-# endif
+ BOOL was_initted_p = initted_p;
+ [super render_x11];
+
+ if (! was_initted_p && xdpy)
+ _suppressRotationAnimation =
+ get_boolean_resource (xdpy,
+ "suppressRotationAnimation",
+ "SuppressRotationAnimation");
+}
- [self render_x11];
+#endif // USE_IPHONE
-# ifdef USE_IPHONE
- UIGraphicsPopContext();
-# endif
-}
/* The backbuffer isn't actually used for GL programs, but it needs to
*/
- (void) createBackbuffer:(CGSize)new_size
{
- backbuffer_size = new_size;
+#ifdef JWXYZ_QUARTZ
+ NSAssert (! backbuffer_texture,
+ @"backbuffer_texture shouldn't be used for GL hacks");
if (! backbuffer) {
CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
int h = 8;
backbuffer = CGBitmapContextCreate (NULL, w, h, // yup, only 8px x 8px.
8, w*4, cs,
- kCGImageAlphaPremultipliedLast);
+ (kCGBitmapByteOrder32Little |
+ kCGImageAlphaNoneSkipLast));
CGColorSpaceRelease (cs);
}
+#endif // JWXYZ_QUARTZ
}
-# endif // USE_BACKBUFFER
-/* When changing the device orientation, leave the X11 Window and glViewport
- in portrait configuration. OpenGL hacks examine current_device_rotation()
- within the scene as needed.
- */
-- (BOOL)reshapeRotatedWindow
+/* Another stub for GL screenhacks. */
+- (void) drawBackbuffer
+{
+}
+
+
+/* Likewise. GL screenhacks control display with glXSwapBuffers(). */
+- (void) flushBackbuffer
{
- return NO;
}
+#ifndef USE_IPHONE
+
+- (NSOpenGLPixelFormat *) getGLPixelFormat
+{
+ NSOpenGLPixelFormatAttribute attrs[40];
+ int i = 0;
+ attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
+ attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
+ attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
+
+ if ([prefsReader getBooleanResource:"doubleBuffer"])
+ attrs[i++] = NSOpenGLPFADoubleBuffer;
+
+ Bool ms_p = [prefsReader getBooleanResource:"multiSample"];
+
+ /* Sometimes, turning on multisampling kills performance. At one point,
+ I thought the answer was, "only run multisampling on one screen, and
+ leave it turned off on other screens". That's what this code does,
+ but it turns out, that solution is insufficient. I can't really tell
+ what causes poor performance with multisampling, but it's not
+ predictable. Without changing the code, some times a given saver will
+ perform fine with multisampling on, and other times it will perform
+ very badly. Without multisampling, they always perform fine.
+ */
+ // if (ms_p && [[view window] screen] != [[NSScreen screens] objectAtIndex:0])
+ // ms_p = 0;
+
+ if (ms_p) {
+ attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
+ attrs[i++] = NSOpenGLPFASamples; attrs[i++] = 6;
+ // Don't really understand what this means:
+ // attrs[i++] = NSOpenGLPFANoRecovery;
+ }
+
+ attrs[i++] = NSOpenGLPFAWindow;
+# ifdef JWXYZ_GL
+ attrs[i++] = NSOpenGLPFAPixelBuffer;
+# endif
+
+ attrs[i] = 0;
+
+ NSOpenGLPixelFormat *result = [[NSOpenGLPixelFormat alloc]
+ initWithAttributes:attrs];
+
+ if (ms_p && !result) { // Retry without multisampling.
+ i -= 2;
+ attrs[i] = 0;
+ result = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+ }
+
+ return [result autorelease];
+}
+
+#else // !USE_IPHONE
+
+- (NSDictionary *)getGLProperties
+{
+ Bool dbuf_p = [prefsReader getBooleanResource:"doubleBuffer"];
+
+ /* There seems to be no way to actually turn off double-buffering in
+ EAGLContext (e.g., no way to draw to the front buffer directly)
+ but if we turn on "retained backing" for non-buffering apps like
+ "pipes", at least the back buffer isn't auto-cleared on them.
+ */
+
+ return [NSDictionary dictionaryWithObjectsAndKeys:
+ kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
+ [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking,
+ nil];
+}
+
+- (void)addExtraRenderbuffers:(CGSize)size
+{
+ int w = size.width;
+ int h = size.height;
+
+ if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer);
+
+ glGenRenderbuffersOES (1, &gl_depthbuffer);
+ // [EAGLContext renderbufferStorage:fromDrawable:] must be called before this.
+ glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer);
+ glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
+ w, h);
+ glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
+ GL_RENDERBUFFER_OES, gl_depthbuffer);
+}
+
+- (NSString *)getCAGravity
+{
+ return kCAGravityCenter;
+}
+
+- (void) startAnimation
+{
+ [super startAnimation];
+ if (ogl_ctx) /* Almost always true. */
+ _glesState = jwzgles_make_state ();
+}
+
+- (void) stopAnimation
+{
+ [super stopAnimation];
+#ifdef USE_IPHONE
+ if (_glesState) {
+ [EAGLContext setCurrentContext:ogl_ctx];
+ jwzgles_make_current (_glesState);
+ jwzgles_free_state ();
+ }
+#endif
+}
+
+- (void) prepareContext
+{
+ [super prepareContext];
+ jwzgles_make_current (_glesState);
+}
+
+#endif // !USE_IPHONE
+
+
- (void)dealloc {
// ogl_ctx
// gl_framebuffer
XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
@"wrong view class: %@", view);
- NSOpenGLContext *ctx = [view oglContext];
-
-# ifndef USE_IPHONE
-
- if (!ctx) {
-
- NSOpenGLPixelFormatAttribute attrs[40];
- int i = 0;
- attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
- attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
- attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 16;
-
- if (get_boolean_resource (mi->dpy, "doubleBuffer", "DoubleBuffer"))
- attrs[i++] = NSOpenGLPFADoubleBuffer;
-
- Bool ms_p = get_boolean_resource (mi->dpy, "multiSample", "MultiSample");
-
- /* Sometimes, turning on multisampling kills performance. At one point,
- I thought the answer was, "only run multisampling on one screen, and
- leave it turned off on other screens". That's what this code does,
- but it turns out, that solution is insufficient. I can't really tell
- what causes poor performance with multisampling, but it's not
- predictable. Without changing the code, some times a given saver will
- perform fine with multisampling on, and other times it will perform
- very badly. Without multisampling, they always perform fine.
- */
-// if (ms_p && [[view window] screen] != [[NSScreen screens] objectAtIndex:0])
-// ms_p = 0;
-
- if (ms_p) {
- attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
- attrs[i++] = NSOpenGLPFASamples; attrs[i++] = 6;
- // Don't really understand what this means:
- // attrs[i++] = NSOpenGLPFANoRecovery;
- }
-
- attrs[i] = 0;
-
- NSOpenGLPixelFormat *pixfmt =
- [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-
- if (ms_p && !pixfmt) { // Retry without multisampling.
- i -= 2;
- attrs[i] = 0;
- pixfmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
- }
-
- NSAssert (pixfmt, @"unable to create NSOpenGLPixelFormat");
-
- // #### Analyze says: "Potential leak of an object stored into pixfmt"
- ctx = [[NSOpenGLContext alloc]
- initWithFormat:pixfmt
- shareContext:nil];
-// [pixfmt release]; // #### ???
- }
-
- // Sync refreshes to the vertical blanking interval
- GLint r = 1;
- [ctx setValues:&r forParameter:NSOpenGLCPSwapInterval];
-// check_gl_error ("NSOpenGLCPSwapInterval"); // SEGV sometimes. Too early?
-
- // #### Analyze says: "Potential leak of an object stored into "ctx"
- // But makeCurrentContext retains it (right?)
- [ctx makeCurrentContext];
- check_gl_error ("makeCurrentContext");
-
- [view setOglContext:ctx];
-
- // Clear frame buffer ASAP, else there are bits left over from other apps.
- glClearColor (0, 0, 0, 1);
- glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-// glFinish ();
-// glXSwapBuffers (mi->dpy, mi->window);
-
-
- // Enable multi-threading, if possible. This runs most OpenGL commands
- // and GPU management on a second CPU.
- {
-# ifndef kCGLCEMPEngine
-# define kCGLCEMPEngine 313 // Added in MacOS 10.4.8 + XCode 2.4.
-# endif
- CGLContextObj cctx = CGLGetCurrentContext();
- CGLError err = CGLEnable (cctx, kCGLCEMPEngine);
- if (err != kCGLNoError) {
- NSLog (@"enabling multi-threaded OpenGL failed: %d", err);
- }
- }
-
- check_gl_error ("init_GL");
-
-# else // USE_IPHONE
-
- EAGLContext *ogl_ctx = ctx;
- if (!ogl_ctx) {
-
- Bool dbuf_p =
- get_boolean_resource (mi->dpy, "doubleBuffer", "DoubleBuffer");
-
- /* There seems to be no way to actually turn off double-buffering in
- EAGLContext (e.g., no way to draw to the front buffer directly)
- but if we turn on "retained backing" for non-buffering apps like
- "pipes", at least the back buffer isn't auto-cleared on them.
- */
- CAEAGLLayer *eagl_layer = (CAEAGLLayer *) view.layer;
- eagl_layer.opaque = TRUE;
- eagl_layer.drawableProperties =
- [NSDictionary dictionaryWithObjectsAndKeys:
- kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
- [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking,
- nil];
-
- // Without this, the GL frame buffer is half the screen resolution!
- eagl_layer.contentsScale = [UIScreen mainScreen].scale;
-
- ogl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
- }
-
- if (!ogl_ctx)
- return 0;
- [view setOglContext:ogl_ctx];
- // #### Analyze says "Potential leak of an object stored into ogl_ctx"
-
- check_gl_error ("OES_init");
-
- jwzgles_reset ();
-
-# endif // USE_IPHONE
+ // OpenGL initialization is in [XScreenSaverView startAnimation].
// I don't know why this is necessary, but it beats randomly having some
// textures be upside down.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Caller expects a pointer to an opaque struct... which it dereferences.
// Don't ask me, it's historical...
void
glXSwapBuffers (Display *dpy, Window window)
{
+ // This all is very much like what's in -[XScreenSaverView flushBackbuffer].
+#ifdef JWXYZ_GL
+ jwxyz_bind_drawable (window, window);
+#endif // JWXYZ_GL
+
XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
@"wrong view class: %@", view);
}
+#if defined GL_INVALID_FRAMEBUFFER_OPERATION_OES && \
+ !defined GL_INVALID_FRAMEBUFFER_OPERATION
+# define GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES
+#endif
+
+
/* report a GL error. */
void
check_gl_error (const char *type)
case GL_STACK_OVERFLOW: e = "stack overflow"; break;
case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
case GL_OUT_OF_MEMORY: e = "out of memory"; break;
+#ifdef GL_INVALID_FRAMEBUFFER_OPERATION
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ e = "invalid framebuffer operation";
+ break;
+#endif
#ifdef GL_TABLE_TOO_LARGE_EXT
case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
#endif