From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 16 Oct 2018 10:08:48 +0200 Subject: Original 5.40 --- OSX/XScreenSaverGLView.m | 433 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 OSX/XScreenSaverGLView.m (limited to 'OSX/XScreenSaverGLView.m') 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 + * + * 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 +#else +# import +#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); +} -- cgit v1.2.3-55-g7522