1 /* xscreensaver, Copyright (c) 2006-2017 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
44 /* With GL programs, drawing at full resolution isn't a problem.
46 - (CGFloat) hackedContentScaleFactor
49 return [self contentScaleFactor];
51 return self.window.backingScaleFactor;
57 - (BOOL)ignoreRotation
59 return FALSE; // Allow xwindow and the glViewport to change shape
62 - (BOOL) suppressRotationAnimation
64 return _suppressRotationAnimation; // per-hack setting, default FALSE
67 - (BOOL) rotateTouches
69 return TRUE; // We need the XY axes swapped in our events
76 GLint gl_renderbuffer = xwindow->gl_renderbuffer;
78 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_renderbuffer);
79 [ogl_ctx presentRenderbuffer:GL_RENDERBUFFER_OES];
84 - (void) animateOneFrame
86 # if defined USE_IPHONE && defined JWXYZ_QUARTZ
87 UIGraphicsPushContext (backbuffer);
92 # if defined USE_IPHONE && defined JWXYZ_QUARTZ
93 UIGraphicsPopContext();
98 /* GL screenhacks don't display a backbuffer, so this is a stub. */
99 - (void) enableBackbuffer:(CGSize)new_backbuffer_size
104 /* GL screenhacks set their own viewport and matrices. */
112 /* Keep the GL scene oriented into a portrait-mode View, regardless of
113 what the physical device orientation is.
119 glMatrixMode(GL_PROJECTION);
120 glRotatef (-current_device_rotation(), 0, 0, 1);
121 glMatrixMode(GL_MODELVIEW);
126 BOOL was_initted_p = initted_p;
129 if (! was_initted_p && xdpy)
130 _suppressRotationAnimation =
131 get_boolean_resource (xdpy,
132 "suppressRotationAnimation",
133 "SuppressRotationAnimation");
140 /* The backbuffer isn't actually used for GL programs, but it needs to
141 be there for X11 calls to not error out. However, nothing done with
142 X11 calls will ever show up! It all gets written into the backbuffer
143 and discarded. That's ok, though, because mostly it's just calls to
144 XClearWindow and housekeeping stuff like that. So we make a tiny one.
146 - (void) createBackbuffer:(CGSize)new_size
149 NSAssert (! backbuffer_texture,
150 @"backbuffer_texture shouldn't be used for GL hacks");
153 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
156 backbuffer = CGBitmapContextCreate (NULL, w, h, // yup, only 8px x 8px.
158 (kCGBitmapByteOrder32Little |
159 kCGImageAlphaNoneSkipLast));
160 CGColorSpaceRelease (cs);
162 #endif // JWXYZ_QUARTZ
166 /* Another stub for GL screenhacks. */
167 - (void) drawBackbuffer
172 /* Likewise. GL screenhacks control display with glXSwapBuffers(). */
173 - (void) flushBackbuffer
180 - (NSOpenGLPixelFormat *) getGLPixelFormat
182 NSOpenGLPixelFormatAttribute attrs[40];
184 attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24;
185 attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8;
186 attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24;
188 if ([prefsReader getBooleanResource:"doubleBuffer"])
189 attrs[i++] = NSOpenGLPFADoubleBuffer;
191 Bool ms_p = [prefsReader getBooleanResource:"multiSample"];
193 /* Sometimes, turning on multisampling kills performance. At one point,
194 I thought the answer was, "only run multisampling on one screen, and
195 leave it turned off on other screens". That's what this code does,
196 but it turns out, that solution is insufficient. I can't really tell
197 what causes poor performance with multisampling, but it's not
198 predictable. Without changing the code, some times a given saver will
199 perform fine with multisampling on, and other times it will perform
200 very badly. Without multisampling, they always perform fine.
202 // if (ms_p && [[view window] screen] != [[NSScreen screens] objectAtIndex:0])
206 attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1;
207 attrs[i++] = NSOpenGLPFASamples; attrs[i++] = 6;
208 // Don't really understand what this means:
209 // attrs[i++] = NSOpenGLPFANoRecovery;
212 attrs[i++] = NSOpenGLPFAWindow;
214 attrs[i++] = NSOpenGLPFAPixelBuffer;
219 NSOpenGLPixelFormat *result = [[NSOpenGLPixelFormat alloc]
220 initWithAttributes:attrs];
222 if (ms_p && !result) { // Retry without multisampling.
225 result = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
228 return [result autorelease];
233 - (NSDictionary *)getGLProperties
235 Bool dbuf_p = [prefsReader getBooleanResource:"doubleBuffer"];
237 /* There seems to be no way to actually turn off double-buffering in
238 EAGLContext (e.g., no way to draw to the front buffer directly)
239 but if we turn on "retained backing" for non-buffering apps like
240 "pipes", at least the back buffer isn't auto-cleared on them.
243 return [NSDictionary dictionaryWithObjectsAndKeys:
244 kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
245 [NSNumber numberWithBool:!dbuf_p], kEAGLDrawablePropertyRetainedBacking,
249 - (void)addExtraRenderbuffers:(CGSize)size
254 if (gl_depthbuffer) glDeleteRenderbuffersOES (1, &gl_depthbuffer);
256 glGenRenderbuffersOES (1, &gl_depthbuffer);
257 // [EAGLContext renderbufferStorage:fromDrawable:] must be called before this.
258 glBindRenderbufferOES (GL_RENDERBUFFER_OES, gl_depthbuffer);
259 glRenderbufferStorageOES (GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES,
261 glFramebufferRenderbufferOES (GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
262 GL_RENDERBUFFER_OES, gl_depthbuffer);
265 - (NSString *)getCAGravity
267 return kCAGravityCenter;
270 - (void) startAnimation
272 [super startAnimation];
273 if (ogl_ctx) /* Almost always true. */
274 _glesState = jwzgles_make_state ();
277 - (void) stopAnimation
279 [super stopAnimation];
282 [EAGLContext setCurrentContext:ogl_ctx];
283 jwzgles_make_current (_glesState);
284 jwzgles_free_state ();
289 - (void) prepareContext
291 [super prepareContext];
292 jwzgles_make_current (_glesState);
295 #endif // !USE_IPHONE
309 /* Utility functions...
313 // redefine NSAssert, etc. here since they don't work when not inside
320 jwxyz_abort ("%s", [(S) cStringUsingEncoding:NSUTF8StringEncoding])
321 #define NSAssert(CC,S) do { if (!(CC)) { NSASS((S)); }} while(0)
322 #define NSAssert1(CC,S,A) do { if (!(CC)) { \
323 NSASS(([NSString stringWithFormat: S, A])); }} while(0)
324 #define NSAssert2(CC,S,A,B) do { if (!(CC)) { \
325 NSASS(([NSString stringWithFormat: S, A, B])); }} while(0)
328 /* Called by OpenGL savers using the XLockmore API.
331 init_GL (ModeInfo *mi)
333 Window win = mi->window;
334 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
335 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
336 @"wrong view class: %@", view);
338 // OpenGL initialization is in [XScreenSaverView startAnimation].
340 // I don't know why this is necessary, but it beats randomly having some
341 // textures be upside down.
343 glMatrixMode(GL_TEXTURE);
345 glMatrixMode(GL_PROJECTION);
347 glMatrixMode(GL_MODELVIEW);
350 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
352 // Caller expects a pointer to an opaque struct... which it dereferences.
353 // Don't ask me, it's historical...
354 static int blort = -1;
355 return (void *) &blort;
359 /* Copy the back buffer to the front buffer.
362 glXSwapBuffers (Display *dpy, Window window)
364 // This all is very much like what's in -[XScreenSaverView flushBackbuffer].
366 jwxyz_bind_drawable (window, window);
369 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
370 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
371 @"wrong view class: %@", view);
373 NSOpenGLContext *ctx = [view oglContext];
374 if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
375 #else /* USE_IPHONE */
377 #endif /* USE_IPHONE */
380 /* Does nothing - prepareContext already did the work.
383 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
388 /* clear away any lingering error codes */
390 clear_gl_error (void)
392 while (glGetError() != GL_NO_ERROR)
397 #if defined GL_INVALID_FRAMEBUFFER_OPERATION_OES && \
398 !defined GL_INVALID_FRAMEBUFFER_OPERATION
399 # define GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES
403 /* report a GL error. */
405 check_gl_error (const char *type)
410 switch ((i = glGetError())) {
411 case GL_NO_ERROR: return;
412 case GL_INVALID_ENUM: e = "invalid enum"; break;
413 case GL_INVALID_VALUE: e = "invalid value"; break;
414 case GL_INVALID_OPERATION: e = "invalid operation"; break;
415 case GL_STACK_OVERFLOW: e = "stack overflow"; break;
416 case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
417 case GL_OUT_OF_MEMORY: e = "out of memory"; break;
418 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
419 case GL_INVALID_FRAMEBUFFER_OPERATION:
420 e = "invalid framebuffer operation";
423 #ifdef GL_TABLE_TOO_LARGE_EXT
424 case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
426 #ifdef GL_TEXTURE_TOO_LARGE_EXT
427 case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
430 e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
432 NSAssert2 (0, @"%s GL error: %s", type, e);