summaryrefslogtreecommitdiffstats
path: root/OSX/XScreenSaverGLView.m
diff options
context:
space:
mode:
authorSimon Rettberg2018-10-16 10:08:48 +0200
committerSimon Rettberg2018-10-16 10:08:48 +0200
commitd3a98cf6cbc3bd0b9efc570f58e8812c03931c18 (patch)
treecbddf8e50f35a9c6e878a5bfe3c6d625d99e12ba /OSX/XScreenSaverGLView.m
downloadxscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.gz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.tar.xz
xscreensaver-d3a98cf6cbc3bd0b9efc570f58e8812c03931c18.zip
Original 5.40
Diffstat (limited to 'OSX/XScreenSaverGLView.m')
-rw-r--r--OSX/XScreenSaverGLView.m433
1 files changed, 433 insertions, 0 deletions
diff --git a/OSX/XScreenSaverGLView.m b/OSX/XScreenSaverGLView.m
new file mode 100644
index 0000000..57b0c7c
--- /dev/null
+++ b/OSX/XScreenSaverGLView.m
@@ -0,0 +1,433 @@
+/* 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
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* This is a subclass of Apple's ScreenSaverView that knows how to run
+ xscreensaver programs without X11 via the dark magic of the "jwxyz"
+ library. In xscreensaver terminology, this is the replacement for
+ the "screenhack.c" module.
+ */
+
+#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
+
+/* used by the OpenGL screen savers
+ */
+extern GLXContext *init_GL (ModeInfo *);
+extern void glXSwapBuffers (Display *, Window);
+extern void glXMakeCurrent (Display *, Window, GLXContext);
+extern void clear_gl_error (void);
+extern void check_gl_error (const char *type);
+
+
+@implementation XScreenSaverGLView
+
+
+/* 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
+
+- (BOOL)ignoreRotation
+{
+ return FALSE; // Allow xwindow and the glViewport to change shape
+}
+
+- (BOOL) suppressRotationAnimation
+{
+ return _suppressRotationAnimation; // per-hack setting, default FALSE
+}
+
+- (BOOL) rotateTouches
+{
+ return TRUE; // We need the XY axes swapped in our events
+}
+
+
+- (void) swapBuffers
+{
+# 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) animateOneFrame
+{
+# if defined USE_IPHONE && defined JWXYZ_QUARTZ
+ UIGraphicsPushContext (backbuffer);
+# endif
+
+ [self render_x11];
+
+# if defined USE_IPHONE && defined JWXYZ_QUARTZ
+ UIGraphicsPopContext();
+# endif
+}
+
+
+/* GL screenhacks don't display a backbuffer, so this is a stub. */
+- (void) enableBackbuffer:(CGSize)new_backbuffer_size
+{
+}
+
+
+/* GL screenhacks set their own viewport and matrices. */
+- (void) setViewport
+{
+}
+
+
+#ifdef USE_IPHONE
+
+/* Keep the GL scene oriented into a portrait-mode View, regardless of
+ what the physical device orientation is.
+ */
+- (void) reshape_x11
+{
+ [super reshape_x11];
+
+ glMatrixMode(GL_PROJECTION);
+ glRotatef (-current_device_rotation(), 0, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+}
+
+- (void) render_x11
+{
+ BOOL was_initted_p = initted_p;
+ [super render_x11];
+
+ if (! was_initted_p && xdpy)
+ _suppressRotationAnimation =
+ get_boolean_resource (xdpy,
+ "suppressRotationAnimation",
+ "SuppressRotationAnimation");
+}
+
+#endif // USE_IPHONE
+
+
+
+/* The backbuffer isn't actually used for GL programs, but it needs to
+ be there for X11 calls to not error out. However, nothing done with
+ X11 calls will ever show up! It all gets written into the backbuffer
+ and discarded. That's ok, though, because mostly it's just calls to
+ XClearWindow and housekeeping stuff like that. So we make a tiny one.
+ */
+- (void) createBackbuffer:(CGSize)new_size
+{
+#ifdef JWXYZ_QUARTZ
+ NSAssert (! backbuffer_texture,
+ @"backbuffer_texture shouldn't be used for GL hacks");
+
+ if (! backbuffer) {
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ int w = 8;
+ int h = 8;
+ backbuffer = CGBitmapContextCreate (NULL, w, h, // yup, only 8px x 8px.
+ 8, w*4, cs,
+ (kCGBitmapByteOrder32Little |
+ kCGImageAlphaNoneSkipLast));
+ CGColorSpaceRelease (cs);
+ }
+#endif // JWXYZ_QUARTZ
+}
+
+
+/* Another stub for GL screenhacks. */
+- (void) drawBackbuffer
+{
+}
+
+
+/* Likewise. GL screenhacks control display with glXSwapBuffers(). */
+- (void) flushBackbuffer
+{
+}
+
+
+#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
+ // gl_renderbuffer
+ // gl_depthbuffer
+ [super dealloc];
+}
+
+@end
+
+
+/* Utility functions...
+ */
+
+
+// redefine NSAssert, etc. here since they don't work when not inside
+// an ObjC method.
+
+#undef NSAssert
+#undef NSAssert1
+#undef NSAssert2
+#define NSASS(S) \
+ jwxyz_abort ("%s", [(S) cStringUsingEncoding:NSUTF8StringEncoding])
+#define NSAssert(CC,S) do { if (!(CC)) { NSASS((S)); }} while(0)
+#define NSAssert1(CC,S,A) do { if (!(CC)) { \
+ NSASS(([NSString stringWithFormat: S, A])); }} while(0)
+#define NSAssert2(CC,S,A,B) do { if (!(CC)) { \
+ NSASS(([NSString stringWithFormat: S, A, B])); }} while(0)
+
+
+/* Called by OpenGL savers using the XLockmore API.
+ */
+GLXContext *
+init_GL (ModeInfo *mi)
+{
+ Window win = mi->window;
+ XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
+ NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
+ @"wrong view class: %@", view);
+
+ // 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_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ 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...
+ static int blort = -1;
+ return (void *) &blort;
+}
+
+
+/* Copy the back buffer to the front buffer.
+ */
+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);
+#ifndef USE_IPHONE
+ NSOpenGLContext *ctx = [view oglContext];
+ if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
+#else /* USE_IPHONE */
+ [view swapBuffers];
+#endif /* USE_IPHONE */
+}
+
+/* Does nothing - prepareContext already did the work.
+ */
+void
+glXMakeCurrent (Display *dpy, Window window, GLXContext context)
+{
+}
+
+
+/* clear away any lingering error codes */
+void
+clear_gl_error (void)
+{
+ while (glGetError() != GL_NO_ERROR)
+ ;
+}
+
+
+#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)
+{
+ char buf[100];
+ GLenum i;
+ const char *e;
+ switch ((i = glGetError())) {
+ case GL_NO_ERROR: return;
+ case GL_INVALID_ENUM: e = "invalid enum"; break;
+ case GL_INVALID_VALUE: e = "invalid value"; break;
+ case GL_INVALID_OPERATION: e = "invalid operation"; break;
+ 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
+#ifdef GL_TEXTURE_TOO_LARGE_EXT
+ case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
+#endif
+ default:
+ e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
+ }
+ NSAssert2 (0, @"%s GL error: %s", type, e);
+}