-/* xscreensaver, Copyright (c) 2006-2017 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 2006-2018 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 "XScreenSaverConfigSheet.h"
#import "Updater.h"
#import "screenhackI.h"
-#import "xlockmoreI.h"
#import "pow2.h"
#import "jwxyzI.h"
#import "jwxyz-cocoa.h"
}
+#ifdef USE_TOUCHBAR
+- (id) initWithFrame:(NSRect)frame
+ saverName:(NSString *)saverName
+ isPreview:(BOOL)isPreview
+ isTouchbar:(BOOL)isTouchbar
+{
+ if (! (self = [self initWithFrame:frame saverName:saverName
+ isPreview:isPreview]))
+ return 0;
+ touchbar_p = isTouchbar;
+ return self;
+}
+#endif // USE_TOUCHBAR
+
+
#ifdef USE_IPHONE
+ (Class) layerClass
{
// from initWithFrame.
[ogl_ctx setView:self];
+ // Get device pixels instead of points.
+ self.wantsBestResolutionOpenGLSurface = YES;
+
// This may not be necessary if there's FBO support.
# ifdef JWXYZ_GL
xwindow->window.pixfmt = pixfmt;
new_backbuffer_size = NSSizeToCGSize ([self bounds].size);
+ // Scale factor for desktop retina displays
+ double s = [self hackedContentScaleFactor];
+ new_backbuffer_size.width *= s;
+ new_backbuffer_size.height *= s;
+
# else // USE_IPHONE
if (!ogl_ctx) {
CAEAGLLayer *eagl_layer = (CAEAGLLayer *) self.layer;
[self setViewport];
[self createBackbuffer:new_backbuffer_size];
+
+# ifdef USE_TOUCHBAR
+ if (touchbar_view) [touchbar_view startAnimation];
+# endif // USE_TOUCHBAR
}
- (void)stopAnimation
xsft->free_cb (xdpy, xwindow, xdata);
[self unlockFocus];
- jwxyz_free_display (xdpy);
+ jwxyz_quartz_free_display (xdpy);
xdpy = NULL;
# if defined JWXYZ_GL && !defined USE_IPHONE
CFRelease (xwindow->ogl_ctx);
backbuffer_data = NULL;
backbuffer_len = 0;
# endif
+
+# ifdef USE_TOUCHBAR
+ if (touchbar_view) {
+ [touchbar_view stopAnimation];
+ [touchbar_view release];
+ touchbar_view = nil;
+ }
+# endif
}
}
+#ifdef USE_TOUCHBAR
+
+static NSString *touchbar_cid = @"org.jwz.xscreensaver.touchbar";
+static NSString *touchbar_iid = @"org.jwz.xscreensaver.touchbar";
+
+- (NSTouchBar *) makeTouchBar
+{
+ NSTouchBar *t = [[NSTouchBar alloc] init];
+ t.delegate = self;
+ t.customizationIdentifier = touchbar_cid;
+ t.defaultItemIdentifiers = @[touchbar_iid,
+ NSTouchBarItemIdentifierOtherItemsProxy];
+ t.customizationAllowedItemIdentifiers = @[touchbar_iid];
+ t.principalItemIdentifier = touchbar_iid;
+ return t;
+}
+
+- (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
+ makeItemForIdentifier:(NSTouchBarItemIdentifier)id
+{
+ if ([id isEqualToString:touchbar_iid])
+ {
+ NSRect rect = [self frame];
+ // #### debugging
+ rect.origin.x = 0;
+ rect.origin.y = 0;
+ rect.size.width = 200;
+ rect.size.height = 40;
+ touchbar_view = [[[self class] alloc]
+ initWithFrame:rect
+ saverName:[NSString stringWithCString:xsft->progclass
+ encoding:NSISOLatin1StringEncoding]
+ isPreview:self.isPreview
+ isTouchbar:True];
+ [touchbar_view setAutoresizingMask:
+ NSViewWidthSizable|NSViewHeightSizable];
+ NSCustomTouchBarItem *item =
+ [[NSCustomTouchBarItem alloc] initWithIdentifier:id];
+ item.view = touchbar_view;
+ item.customizationLabel = touchbar_cid;
+
+ if ([self isAnimating])
+ // TouchBar was created after animation begun.
+ [touchbar_view startAnimation];
+ }
+ return nil;
+}
+
+#endif // USE_TOUCHBAR
+
+
static void
screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
{
}
-#ifdef USE_IPHONE
+/* Some of the older X11 savers look bad if a "pixel" is not a thing you can
+ see. They expect big, chunky, luxurious 1990s pixels, and if they use
+ "device" pixels on a Retina screen, everything just disappears.
-/* On iPhones with Retina displays, we can draw the savers in "real"
- pixels, and that works great. The 320x480 "point" screen is really
- a 640x960 *pixel* screen. However, Retina iPads have 768x1024
- point screens which are 1536x2048 pixels, and apparently that's
- enough pixels that copying those bits to the screen is slow. Like,
- drops us from 15fps to 7fps. So, on Retina iPads, we don't draw in
- real pixels. This will probably make the savers look better
- anyway, since that's a higher resolution than most desktop monitors
- have even today. (This is only true for X11 programs, not GL
- programs. Those are fine at full rez.)
+ Retina iPads have 768x1024 point screens which are 1536x2048 pixels,
+ 2017 iMac screens are 5120x2880 in device pixels.
This method is overridden in XScreenSaverGLView, since this kludge
isn't necessary for GL programs, being resolution independent by
*/
- (CGFloat) hackedContentScaleFactor
{
- NSSize bsize = [self bounds].size;
-
- CGFloat
- max_bsize = bsize.width > bsize.height ? bsize.width : bsize.height;
-
- // Ratio of screen size in pixels to view size in points.
+# ifdef USE_IPHONE
CGFloat s = self.contentScaleFactor;
+# else
+ CGFloat s = self.window.backingScaleFactor;
+# endif
- // Two constraints:
-
- // 1. Don't exceed -- let's say 1280 pixels in either direction.
- // (Otherwise the frame rate gets bad.)
- // Actually let's make that 1440 since iPhone 6 is natively 1334.
- CGFloat mag0 = ceil(max_bsize * s / 1440);
+ if (_lowrez_p) {
+ NSSize b = [self bounds].size;
+ CGFloat wh = b.width > b.height ? b.width : b.height;
- // 2. Don't let the pixel size get too small.
- // (Otherwise pixels in IFS and similar are too fine.)
- // So don't let the result be > 2 pixels per point.
- CGFloat mag1 = ceil(s / 2);
+ // Scale down to as close to 1024 as we can get without going under,
+ // while keeping an integral scale factor so that we don't get banding
+ // artifacts and moire patterns.
+ //
+ // Retina sizes: 2208 => 1104, 2224 => 1112, 2732 => 1366, 2880 => 1440.
+ //
+ int s2 = wh / 1024;
+ if (s2) s /= s2;
+ }
- // As of iPhone 6, mag0 is always >= mag1. This may not be true in the future.
- // (desired scale factor) = s / (desired magnification factor)
- return s / (mag0 > mag1 ? mag0 : mag1);
+ return s;
}
+#ifdef USE_IPHONE
+
double
current_device_rotation (void)
{
# ifdef USE_IPHONE
GLfloat s = self.contentScaleFactor;
- GLfloat hs = self.hackedContentScaleFactor;
# else // !USE_IPHONE
- const GLfloat s = 1;
- const GLfloat hs = s;
+ const GLfloat s = self.window.backingScaleFactor;
# endif
+ GLfloat hs = self.hackedContentScaleFactor;
// On OS X this almost isn't necessary, except for the ugly aliasing
// artifacts.
new_size.width = new_size.height;
new_size.height = swap;
}
+# endif // USE_IPHONE
double s = self.hackedContentScaleFactor;
new_size.width *= s;
new_size.height *= s;
-# endif // USE_IPHONE
[self prepareContext];
[self setViewport];
if (!initted_p) {
+ resized_p = NO;
+
if (! xdpy) {
# ifdef JWXYZ_QUARTZ
xwindow->cgc = backbuffer;
# endif // JWXYZ_QUARTZ
- xdpy = jwxyz_make_display (xwindow);
+ xdpy = jwxyz_quartz_make_display (xwindow);
# if defined USE_IPHONE
/* Some X11 hacks (fluidballs) want to ignore all rotation events. */
# endif // !JWXYZ_GL
# endif // USE_IPHONE
+ _lowrez_p = get_boolean_resource (xdpy, "lowrez", "Lowrez");
+ if (_lowrez_p) {
+ resized_p = YES;
+
+# if !defined __OPTIMIZE__ || TARGET_IPHONE_SIMULATOR
+ NSSize b = [self bounds].size;
+ CGFloat s = self.hackedContentScaleFactor;
+# ifdef USE_IPHONE
+ CGFloat o = self.contentScaleFactor;
+# else
+ CGFloat o = self.window.backingScaleFactor;
+# endif
+ if (o != s)
+ NSLog(@"lowrez: scaling %.0fx%.0f -> %.0fx%.0f (%.02f)",
+ b.width * o, b.height * o,
+ b.width * s, b.height * s, s);
+# endif
+ }
+
[self resize_x11];
}
xsft->setup_cb (xsft, xsft->setup_arg);
}
initted_p = YES;
- resized_p = NO;
NSAssert(!xdata, @"xdata already initialized");
{
// Render X11 into the backing store bitmap...
+# ifdef USE_TOUCHBAR
+ if (touchbar_p) return;
+# endif
+
# ifdef JWXYZ_QUARTZ
NSAssert (backbuffer, @"no back buffer");
# if defined USE_IPHONE && defined JWXYZ_QUARTZ
UIGraphicsPopContext();
# endif
+
+# ifdef USE_TOUCHBAR
+ if (touchbar_view) [touchbar_view animateOneFrame];
+# endif
}
NSPoint p = [[[e window] contentView] convertPoint:[e locationInWindow]
toView:self];
-# ifdef USE_IPHONE
double s = [self hackedContentScaleFactor];
-# else
- int s = 1;
-# endif
int x = s * p.x;
int y = s * ([self bounds].size.height - p.y);
[self addSubview:closeBox];
}
+ // Don't hide the buttons under the iPhone X bezel.
+ UIEdgeInsets is = { 0, };
+ if ([self respondsToSelector:@selector(safeAreaInsets)]) {
+# pragma clang diagnostic push // "only available on iOS 11.0 or newer"
+# pragma clang diagnostic ignored "-Wunguarded-availability-new"
+ is = [self safeAreaInsets];
+# pragma clang diagnostic pop
+ [closeBox setFrame:CGRectMake(is.left, is.top,
+ self.bounds.size.width - is.right - is.left,
+ ih + off)];
+ }
+
if (closeBox.layer.opacity <= 0) { // Fade in
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"opacity"];