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;
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 #endif // !USE_IPHONE
279 /* Utility functions...
283 // redefine NSAssert, etc. here since they don't work when not inside
290 jwxyz_abort ("%s", [(S) cStringUsingEncoding:NSUTF8StringEncoding])
291 #define NSAssert(CC,S) do { if (!(CC)) { NSASS((S)); }} while(0)
292 #define NSAssert1(CC,S,A) do { if (!(CC)) { \
293 NSASS(([NSString stringWithFormat: S, A])); }} while(0)
294 #define NSAssert2(CC,S,A,B) do { if (!(CC)) { \
295 NSASS(([NSString stringWithFormat: S, A, B])); }} while(0)
298 /* Called by OpenGL savers using the XLockmore API.
301 init_GL (ModeInfo *mi)
303 Window win = mi->window;
304 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (win);
305 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
306 @"wrong view class: %@", view);
308 // OpenGL initialization is in [XScreenSaverView startAnimation].
312 # endif // USE_IPHONE
314 // I don't know why this is necessary, but it beats randomly having some
315 // textures be upside down.
317 glMatrixMode(GL_TEXTURE);
319 glMatrixMode(GL_PROJECTION);
321 glMatrixMode(GL_MODELVIEW);
324 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
326 // Caller expects a pointer to an opaque struct... which it dereferences.
327 // Don't ask me, it's historical...
328 static int blort = -1;
329 return (void *) &blort;
333 /* Copy the back buffer to the front buffer.
336 glXSwapBuffers (Display *dpy, Window window)
338 // This all is very much like what's in -[XScreenSaverView flushBackbuffer].
340 jwxyz_bind_drawable (window, window);
343 XScreenSaverGLView *view = (XScreenSaverGLView *) jwxyz_window_view (window);
344 NSAssert1 ([view isKindOfClass:[XScreenSaverGLView class]],
345 @"wrong view class: %@", view);
347 NSOpenGLContext *ctx = [view oglContext];
348 if (ctx) [ctx flushBuffer]; // despite name, this actually swaps
349 #else /* USE_IPHONE */
351 #endif /* USE_IPHONE */
354 /* Does nothing - prepareContext already did the work.
357 glXMakeCurrent (Display *dpy, Window window, GLXContext context)
362 /* clear away any lingering error codes */
364 clear_gl_error (void)
366 while (glGetError() != GL_NO_ERROR)
371 #if defined GL_INVALID_FRAMEBUFFER_OPERATION_OES && \
372 !defined GL_INVALID_FRAMEBUFFER_OPERATION
373 # define GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES
377 /* report a GL error. */
379 check_gl_error (const char *type)
384 switch ((i = glGetError())) {
385 case GL_NO_ERROR: return;
386 case GL_INVALID_ENUM: e = "invalid enum"; break;
387 case GL_INVALID_VALUE: e = "invalid value"; break;
388 case GL_INVALID_OPERATION: e = "invalid operation"; break;
389 case GL_STACK_OVERFLOW: e = "stack overflow"; break;
390 case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
391 case GL_OUT_OF_MEMORY: e = "out of memory"; break;
392 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
393 case GL_INVALID_FRAMEBUFFER_OPERATION:
394 e = "invalid framebuffer operation";
397 #ifdef GL_TABLE_TOO_LARGE_EXT
398 case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
400 #ifdef GL_TEXTURE_TOO_LARGE_EXT
401 case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
404 e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
406 NSAssert2 (0, @"%s GL error: %s", type, e);