X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=OSX%2FXScreenSaverView.m.orig;fp=OSX%2FXScreenSaverView.m.orig;h=0000000000000000000000000000000000000000;hp=ee8f3e6a0c6ffba3f1f0360d55488233e1ab120c;hb=8afc01a67be4fbf3f1cc0fce9adf01b5289a21c6;hpb=3f1091236d800c43a3124c44c7da54e53f205b13 diff --git a/OSX/XScreenSaverView.m.orig b/OSX/XScreenSaverView.m.orig deleted file mode 100644 index ee8f3e6a..00000000 --- a/OSX/XScreenSaverView.m.orig +++ /dev/null @@ -1,1804 +0,0 @@ -/* xscreensaver, Copyright (c) 2006-2013 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 -#import -#import "XScreenSaverView.h" -#import "XScreenSaverConfigSheet.h" -#import "screenhackI.h" -#import "xlockmoreI.h" -#import "jwxyz-timers.h" - - -/* Garbage collection only exists if we are being compiled against the - 10.6 SDK or newer, not if we are building against the 10.4 SDK. - */ -#ifndef MAC_OS_X_VERSION_10_6 -# define MAC_OS_X_VERSION_10_6 1060 /* undefined in 10.4 SDK, grr */ -#endif -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 /* 10.6 SDK */ -# import -# define DO_GC_HACKERY -#endif - -extern struct xscreensaver_function_table *xscreensaver_function_table; - -/* Global variables used by the screen savers - */ -const char *progname; -const char *progclass; -int mono_p = 0; - - -# ifdef USE_IPHONE - -extern NSDictionary *make_function_table_dict(void); // ios-function-table.m - -/* Stub definition of the superclass, for iPhone. - */ -@implementation ScreenSaverView -{ - NSTimeInterval anim_interval; - Bool animating_p; - NSTimer *anim_timer; -} - -- (id)initWithFrame:(NSRect)frame isPreview:(BOOL)isPreview { - self = [super initWithFrame:frame]; - if (! self) return 0; - anim_interval = 1.0/30; - return self; -} -- (NSTimeInterval)animationTimeInterval { return anim_interval; } -- (void)setAnimationTimeInterval:(NSTimeInterval)i { anim_interval = i; } -- (BOOL)hasConfigureSheet { return NO; } -- (NSWindow *)configureSheet { return nil; } -- (NSView *)configureView { return nil; } -- (BOOL)isPreview { return NO; } -- (BOOL)isAnimating { return animating_p; } -- (void)animateOneFrame { } - -- (void)startAnimation { - if (animating_p) return; - animating_p = YES; - anim_timer = [NSTimer scheduledTimerWithTimeInterval: anim_interval - target:self - selector:@selector(animateOneFrame) - userInfo:nil - repeats:YES]; -} - -- (void)stopAnimation { - if (anim_timer) { - [anim_timer invalidate]; - anim_timer = 0; - } - animating_p = NO; -} -@end - -# endif // !USE_IPHONE - - - -@interface XScreenSaverView (Private) -- (void) stopAndClose:(Bool)relaunch; -@end - -@implementation XScreenSaverView - -// Given a lower-cased saver name, returns the function table for it. -// If no name, guess the name from the class's bundle name. -// -- (struct xscreensaver_function_table *) findFunctionTable:(NSString *)name -{ - NSBundle *nsb = [NSBundle bundleForClass:[self class]]; - NSAssert1 (nsb, @"no bundle for class %@", [self class]); - - NSString *path = [nsb bundlePath]; - CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, - (CFStringRef) path, - kCFURLPOSIXPathStyle, - true); - CFBundleRef cfb = CFBundleCreate (kCFAllocatorDefault, url); - CFRelease (url); - NSAssert1 (cfb, @"no CFBundle for \"%@\"", path); - // #### Analyze says "Potential leak of an object stored into cfb" - - if (! name) - name = [[path lastPathComponent] stringByDeletingPathExtension]; - - name = [[name lowercaseString] - stringByReplacingOccurrencesOfString:@" " - withString:@""]; - -# ifndef USE_IPHONE - // CFBundleGetDataPointerForName doesn't work in "Archive" builds. - // I'm guessing that symbol-stripping is mandatory. Fuck. - NSString *table_name = [name stringByAppendingString: - @"_xscreensaver_function_table"]; - void *addr = CFBundleGetDataPointerForName (cfb, (CFStringRef) table_name); - CFRelease (cfb); - - if (! addr) - NSLog (@"no symbol \"%@\" for \"%@\"", table_name, path); - -# else // USE_IPHONE - // Remember: any time you add a new saver to the iOS app, - // manually run "make ios-function-table.m"! - if (! function_tables) - function_tables = [make_function_table_dict() retain]; - NSValue *v = [function_tables objectForKey: name]; - void *addr = v ? [v pointerValue] : 0; -# endif // USE_IPHONE - - return (struct xscreensaver_function_table *) addr; -} - - -// Add the "Contents/Resources/" subdirectory of this screen saver's .bundle -// to $PATH for the benefit of savers that include helper shell scripts. -// -- (void) setShellPath -{ - NSBundle *nsb = [NSBundle bundleForClass:[self class]]; - NSAssert1 (nsb, @"no bundle for class %@", [self class]); - - NSString *nsdir = [nsb resourcePath]; - NSAssert1 (nsdir, @"no resourcePath for class %@", [self class]); - const char *dir = [nsdir cStringUsingEncoding:NSUTF8StringEncoding]; - const char *opath = getenv ("PATH"); - if (!opath) opath = "/bin"; // $PATH is unset when running under Shark! - char *npath = (char *) malloc (strlen (opath) + strlen (dir) + 30); - strcpy (npath, "PATH="); - strcat (npath, dir); - strcat (npath, ":"); - strcat (npath, opath); - if (putenv (npath)) { - perror ("putenv"); - NSAssert1 (0, @"putenv \"%s\" failed", npath); - } - - /* Don't free (npath) -- MacOS's putenv() does not copy it. */ -} - - -// set an $XSCREENSAVER_CLASSPATH variable so that included shell scripts -// (e.g., "xscreensaver-text") know how to look up resources. -// -- (void) setResourcesEnv:(NSString *) name -{ - NSBundle *nsb = [NSBundle bundleForClass:[self class]]; - NSAssert1 (nsb, @"no bundle for class %@", [self class]); - - const char *s = [name cStringUsingEncoding:NSUTF8StringEncoding]; - char *env = (char *) malloc (strlen (s) + 40); - strcpy (env, "XSCREENSAVER_CLASSPATH="); - strcat (env, s); - if (putenv (env)) { - perror ("putenv"); - NSAssert1 (0, @"putenv \"%s\" failed", env); - } - /* Don't free (env) -- MacOS's putenv() does not copy it. */ -} - - -static void -add_default_options (const XrmOptionDescRec *opts, - const char * const *defs, - XrmOptionDescRec **opts_ret, - const char ***defs_ret) -{ - /* These aren't "real" command-line options (there are no actual command-line - options in the Cocoa version); but this is the somewhat kludgey way that - the and tags in the - ../hacks/config/\*.xml files communicate with the preferences database. - */ - static const XrmOptionDescRec default_options [] = { - { "-text-mode", ".textMode", XrmoptionSepArg, 0 }, - { "-text-literal", ".textLiteral", XrmoptionSepArg, 0 }, - { "-text-file", ".textFile", XrmoptionSepArg, 0 }, - { "-text-url", ".textURL", XrmoptionSepArg, 0 }, - { "-text-program", ".textProgram", XrmoptionSepArg, 0 }, - { "-grab-desktop", ".grabDesktopImages", XrmoptionNoArg, "True" }, - { "-no-grab-desktop", ".grabDesktopImages", XrmoptionNoArg, "False"}, - { "-choose-random-images", ".chooseRandomImages",XrmoptionNoArg, "True" }, - { "-no-choose-random-images",".chooseRandomImages",XrmoptionNoArg, "False"}, - { "-image-directory", ".imageDirectory", XrmoptionSepArg, 0 }, - { "-fps", ".doFPS", XrmoptionNoArg, "True" }, - { "-no-fps", ".doFPS", XrmoptionNoArg, "False"}, - { "-foreground", ".foreground", XrmoptionSepArg, 0 }, - { "-fg", ".foreground", XrmoptionSepArg, 0 }, - { "-background", ".background", XrmoptionSepArg, 0 }, - { "-bg", ".background", XrmoptionSepArg, 0 }, - { 0, 0, 0, 0 } - }; - static const char *default_defaults [] = { - ".doFPS: False", - ".doubleBuffer: True", - ".multiSample: False", -# ifndef USE_IPHONE - ".textMode: date", -# else - ".textMode: url", -# endif - // ".textLiteral: ", - // ".textFile: ", - ".textURL: http://en.wikipedia.org/w/index.php?title=Special:NewPages&feed=rss", - // ".textProgram: ", - ".grabDesktopImages: yes", -# ifndef USE_IPHONE - ".chooseRandomImages: no", -# else - ".chooseRandomImages: yes", -# endif - ".imageDirectory: ~/Pictures", - ".relaunchDelay: 2", - 0 - }; - - int count = 0, i, j; - for (i = 0; default_options[i].option; i++) - count++; - for (i = 0; opts[i].option; i++) - count++; - - XrmOptionDescRec *opts2 = (XrmOptionDescRec *) - calloc (count + 1, sizeof (*opts2)); - - i = 0; - j = 0; - while (default_options[j].option) { - opts2[i] = default_options[j]; - i++, j++; - } - j = 0; - while (opts[j].option) { - opts2[i] = opts[j]; - i++, j++; - } - - *opts_ret = opts2; - - - /* now the defaults - */ - count = 0; - for (i = 0; default_defaults[i]; i++) - count++; - for (i = 0; defs[i]; i++) - count++; - - const char **defs2 = (const char **) calloc (count + 1, sizeof (*defs2)); - - i = 0; - j = 0; - while (default_defaults[j]) { - defs2[i] = default_defaults[j]; - i++, j++; - } - j = 0; - while (defs[j]) { - defs2[i] = defs[j]; - i++, j++; - } - - *defs_ret = defs2; -} - - -#ifdef USE_IPHONE -/* Returns the current time in seconds as a double. - */ -static double -double_time (void) -{ - struct timeval now; -# ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; - gettimeofday(&now, &tzp); -# else - gettimeofday(&now); -# endif - - return (now.tv_sec + ((double) now.tv_usec * 0.000001)); -} -#endif // USE_IPHONE - - -- (id) initWithFrame:(NSRect)frame - saverName:(NSString *)saverName - isPreview:(BOOL)isPreview -{ -# ifdef USE_IPHONE - initial_bounds = frame.size; - rot_current_size = frame.size; // needs to be early, because - rot_from = rot_current_size; // [self setFrame] is called by - rot_to = rot_current_size; // [super initWithFrame]. - rotation_ratio = -1; -# endif - - if (! (self = [super initWithFrame:frame isPreview:isPreview])) - return 0; - - xsft = [self findFunctionTable: saverName]; - if (! xsft) { - [self release]; - return 0; - } - - [self setShellPath]; - -# ifdef USE_IPHONE - [self setMultipleTouchEnabled:YES]; - orientation = UIDeviceOrientationUnknown; - [self didRotate:nil]; -# endif // USE_IPHONE - - setup_p = YES; - if (xsft->setup_cb) - xsft->setup_cb (xsft, xsft->setup_arg); - - /* The plist files for these preferences show up in - $HOME/Library/Preferences/ByHost/ in a file named like - "org.jwz.xscreensaver...plist" - */ - NSString *name = [NSString stringWithCString:xsft->progclass - encoding:NSISOLatin1StringEncoding]; - name = [@"org.jwz.xscreensaver." stringByAppendingString:name]; - [self setResourcesEnv:name]; - - - XrmOptionDescRec *opts = 0; - const char **defs = 0; - add_default_options (xsft->options, xsft->defaults, &opts, &defs); - prefsReader = [[PrefsReader alloc] - initWithName:name xrmKeys:opts defaults:defs]; - free (defs); - // free (opts); // bah, we need these! #### leak! - xsft->options = opts; - - progname = progclass = xsft->progclass; - - next_frame_time = 0; - -# ifdef USE_BACKBUFFER - [self createBackbuffer]; - [self initLayer]; -# endif - -# ifdef USE_IPHONE - // So we can tell when we're docked. - [UIDevice currentDevice].batteryMonitoringEnabled = YES; -# endif // USE_IPHONE - - return self; -} - -- (void) initLayer -{ -# ifndef USE_IPHONE - [self setLayer: [CALayer layer]]; - self.layer.delegate = self; - self.layer.opaque = YES; - [self setWantsLayer: YES]; -# endif -} - - -- (id) initWithFrame:(NSRect)frame isPreview:(BOOL)p -{ - return [self initWithFrame:frame saverName:0 isPreview:p]; -} - - -- (void) dealloc -{ - NSAssert(![self isAnimating], @"still animating"); - NSAssert(!xdata, @"xdata not yet freed"); - if (xdpy) - jwxyz_free_display (xdpy); - -# ifdef USE_BACKBUFFER - if (backbuffer) - CGContextRelease (backbuffer); -# endif - - [prefsReader release]; - - // xsft - // fpst - - [super dealloc]; -} - -- (PrefsReader *) prefsReader -{ - return prefsReader; -} - - -#ifdef USE_IPHONE -- (void) lockFocus { } -- (void) unlockFocus { } -#endif // USE_IPHONE - - - -# ifdef USE_IPHONE -/* A few seconds after the saver launches, we store the "wasRunning" - preference. This is so that if the saver is crashing at startup, - we don't launch it again next time, getting stuck in a crash loop. - */ -- (void) allSystemsGo: (NSTimer *) timer -{ - NSAssert (timer == crash_timer, @"crash timer screwed up"); - crash_timer = 0; - - NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; - [prefs setBool:YES forKey:@"wasRunning"]; - [prefs synchronize]; -} -#endif // USE_IPHONE - - -- (void) startAnimation -{ - NSAssert(![self isAnimating], @"already animating"); - NSAssert(!initted_p && !xdata, @"already initialized"); - [super startAnimation]; - /* We can't draw on the window from this method, so we actually do the - initialization of the screen saver (xsft->init_cb) in the first call - to animateOneFrame() instead. - */ - -# ifdef USE_IPHONE - if (crash_timer) - [crash_timer invalidate]; - - NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; - [prefs removeObjectForKey:@"wasRunning"]; - [prefs synchronize]; - - crash_timer = [NSTimer scheduledTimerWithTimeInterval: 5 - target:self - selector:@selector(allSystemsGo:) - userInfo:nil - repeats:NO]; - -# endif // USE_IPHONE - - // Never automatically turn the screen off if we are docked, - // and an animation is running. - // -# ifdef USE_IPHONE - [UIApplication sharedApplication].idleTimerDisabled = - ([UIDevice currentDevice].batteryState != UIDeviceBatteryStateUnplugged); -# endif -} - - -- (void)stopAnimation -{ - NSAssert([self isAnimating], @"not animating"); - - if (initted_p) { - - [self lockFocus]; // in case something tries to draw from here - [self prepareContext]; - - /* I considered just not even calling the free callback at all... - But webcollage-cocoa needs it, to kill the inferior webcollage - processes (since the screen saver framework never generates a - SIGPIPE for them...) Instead, I turned off the free call in - xlockmore.c, which is where all of the bogus calls are anyway. - */ - xsft->free_cb (xdpy, xwindow, xdata); - [self unlockFocus]; - -// setup_p = NO; // #### wait, do we need this? - initted_p = NO; - xdata = 0; - } - -# ifdef USE_IPHONE - if (crash_timer) - [crash_timer invalidate]; - crash_timer = 0; - NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; - [prefs removeObjectForKey:@"wasRunning"]; - [prefs synchronize]; -# endif // USE_IPHONE - - [super stopAnimation]; - - // When an animation is no longer running (e.g., looking at the list) - // then it's ok to power off the screen when docked. - // -# ifdef USE_IPHONE - [UIApplication sharedApplication].idleTimerDisabled = NO; -# endif -} - - -/* Hook for the XScreenSaverGLView subclass - */ -- (void) prepareContext -{ -} - -/* Hook for the XScreenSaverGLView subclass - */ -- (void) resizeContext -{ -} - - -static void -screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure) -{ - fps_compute (fpst, 0, -1); - fps_draw (fpst); -} - - -#ifdef USE_IPHONE - -/* 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.) - - This method is overridden in XScreenSaverGLView, since this kludge - isn't necessary for GL programs, being resolution independent by - nature. - */ -- (CGFloat) hackedContentScaleFactor -{ - GLfloat s = [self contentScaleFactor]; - if (initial_bounds.width >= 1024 || - initial_bounds.height >= 1024) - s = 1; - return s; -} - - -static GLfloat _global_rot_current_angle_kludge; - -double current_device_rotation (void) -{ - return -_global_rot_current_angle_kludge; -} - - -- (void) hackRotation -{ - if (rotation_ratio >= 0) { // in the midst of a rotation animation - -# define CLAMP180(N) while (N < 0) N += 360; while (N > 180) N -= 360 - GLfloat f = angle_from; - GLfloat t = angle_to; - CLAMP180(f); - CLAMP180(t); - GLfloat dist = -(t-f); - CLAMP180(dist); - - // Intermediate angle. - rot_current_angle = f - rotation_ratio * dist; - - // Intermediate frame size. - rot_current_size.width = rot_from.width + - rotation_ratio * (rot_to.width - rot_from.width); - rot_current_size.height = rot_from.height + - rotation_ratio * (rot_to.height - rot_from.height); - - // Tick animation. Complete rotation in 1/6th sec. - double now = double_time(); - double duration = 1/6.0; - rotation_ratio = 1 - ((rot_start_time + duration - now) / duration); - - if (rotation_ratio > 1) { // Done animating. - orientation = new_orientation; - rot_current_angle = angle_to; - rot_current_size = rot_to; - rotation_ratio = -1; - - // Check orientation again in case we rotated again while rotating: - // this is a no-op if nothing has changed. - [self didRotate:nil]; - } - } else { // Not animating a rotation. - rot_current_angle = angle_to; - rot_current_size = rot_to; - } - - CLAMP180(rot_current_angle); - _global_rot_current_angle_kludge = rot_current_angle; - -# undef CLAMP180 - - double s = [self hackedContentScaleFactor]; - if (!ignore_rotation_p && - /* rotation_ratio && */ - ((int) backbuffer_size.width != (int) (s * rot_current_size.width) || - (int) backbuffer_size.height != (int) (s * rot_current_size.height))) - [self resize_x11]; -} - - -- (void)alertView:(UIAlertView *)av clickedButtonAtIndex:(NSInteger)i -{ - if (i == 0) exit (-1); // Cancel - [self stopAndClose:NO]; // Keep going -} - -- (void) handleException: (NSException *)e -{ - NSLog (@"Caught exception: %@", e); - [[[UIAlertView alloc] initWithTitle: - [NSString stringWithFormat: @"%s crashed!", - xsft->progclass] - message: - [NSString stringWithFormat: - @"The error message was:" - "\n\n%@\n\n" - "If it keeps crashing, try " - "resetting its options.", - e] - delegate: self - cancelButtonTitle: @"Exit" - otherButtonTitles: @"Keep going", nil] - show]; - [self stopAnimation]; -} - -#endif // USE_IPHONE - - -#ifdef USE_BACKBUFFER - -/* Create a bitmap context into which we render everything. - If the desired size has changed, re-created it. - */ -- (void) createBackbuffer -{ -# ifdef USE_IPHONE - double s = [self hackedContentScaleFactor]; - CGSize rotsize = ignore_rotation_p ? initial_bounds : rot_current_size; - int new_w = s * rotsize.width; - int new_h = s * rotsize.height; -# else - int new_w = [self bounds].size.width; - int new_h = [self bounds].size.height; -# endif - - if (backbuffer && - backbuffer_size.width == new_w && - backbuffer_size.height == new_h) - return; - - CGSize osize = backbuffer_size; - CGContextRef ob = backbuffer; - - backbuffer_size.width = new_w; - backbuffer_size.height = new_h; - - CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); - backbuffer = CGBitmapContextCreate (NULL, - backbuffer_size.width, - backbuffer_size.height, - 8, - backbuffer_size.width * 4, - cs, - // kCGImageAlphaPremultipliedLast - (kCGImageAlphaNoneSkipFirst | - kCGBitmapByteOrder32Host) - ); - CGColorSpaceRelease (cs); - NSAssert (backbuffer, @"unable to allocate back buffer"); - - // Clear it. - CGRect r; - r.origin.x = r.origin.y = 0; - r.size = backbuffer_size; - CGContextSetGrayFillColor (backbuffer, 0, 1); - CGContextFillRect (backbuffer, r); - - if (ob) { - // Restore old bits, as much as possible, to the X11 upper left origin. - CGRect rect; - rect.origin.x = 0; - rect.origin.y = (backbuffer_size.height - osize.height); - rect.size = osize; - CGImageRef img = CGBitmapContextCreateImage (ob); - CGContextDrawImage (backbuffer, rect, img); - CGImageRelease (img); - CGContextRelease (ob); - } -} - -#endif // USE_BACKBUFFER - - -/* Inform X11 that the size of our window has changed. - */ -- (void) resize_x11 -{ - if (!xwindow) return; // early - -# ifdef USE_BACKBUFFER - [self createBackbuffer]; - jwxyz_window_resized (xdpy, xwindow, - 0, 0, - backbuffer_size.width, backbuffer_size.height, - backbuffer); -# else // !USE_BACKBUFFER - NSRect r = [self frame]; // ignoring rotation is closer - r.size = [self bounds].size; // to what XGetGeometry expects. - jwxyz_window_resized (xdpy, xwindow, - r.origin.x, r.origin.y, - r.size.width, r.size.height, - 0); -# endif // !USE_BACKBUFFER - - // Next time render_x11 is called, run the saver's reshape_cb. - resized_p = YES; -} - - -- (void) render_x11 -{ -# ifdef USE_IPHONE - @try { - - if (orientation == UIDeviceOrientationUnknown) - [self didRotate:nil]; - [self hackRotation]; -# endif - - if (!initted_p) { - - if (! xdpy) { -# ifdef USE_BACKBUFFER - NSAssert (backbuffer, @"no back buffer"); - xdpy = jwxyz_make_display (self, backbuffer); -# else - xdpy = jwxyz_make_display (self, 0); -# endif - xwindow = XRootWindow (xdpy, 0); - -# ifdef USE_IPHONE - /* Some X11 hacks (fluidballs) want to ignore all rotation events. */ - ignore_rotation_p = - get_boolean_resource (xdpy, "ignoreRotation", "IgnoreRotation"); -# endif // USE_IPHONE - - [self resize_x11]; - } - - if (!setup_p) { - setup_p = YES; - if (xsft->setup_cb) - xsft->setup_cb (xsft, xsft->setup_arg); - } - initted_p = YES; - resized_p = NO; - NSAssert(!xdata, @"xdata already initialized"); - - -# undef ya_rand_init - ya_rand_init (0); - - XSetWindowBackground (xdpy, xwindow, - get_pixel_resource (xdpy, 0, - "background", "Background")); - XClearWindow (xdpy, xwindow); - -# ifndef USE_IPHONE - [[self window] setAcceptsMouseMovedEvents:YES]; -# endif - - /* In MacOS 10.5, this enables "QuartzGL", meaning that the Quartz - drawing primitives will run on the GPU instead of the CPU. - It seems like it might make things worse rather than better, - though... Plus it makes us binary-incompatible with 10.4. - -# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - [[self window] setPreferredBackingLocation: - NSWindowBackingLocationVideoMemory]; -# endif - */ - - /* Kludge: even though the init_cb functions are declared to take 2 args, - actually call them with 3, for the benefit of xlockmore_init() and - xlockmore_setup(). - */ - void *(*init_cb) (Display *, Window, void *) = - (void *(*) (Display *, Window, void *)) xsft->init_cb; - - xdata = init_cb (xdpy, xwindow, xsft->setup_arg); - - if (get_boolean_resource (xdpy, "doFPS", "DoFPS")) { - fpst = fps_init (xdpy, xwindow); - if (! xsft->fps_cb) xsft->fps_cb = screenhack_do_fps; - } - } - - - /* I don't understand why we have to do this *every frame*, but we do, - or else the cursor comes back on. - */ -# ifndef USE_IPHONE - if (![self isPreview]) - [NSCursor setHiddenUntilMouseMoves:YES]; -# endif - - - if (fpst) - { - /* This is just a guess, but the -fps code wants to know how long - we were sleeping between frames. - */ - long usecs = 1000000 * [self animationTimeInterval]; - usecs -= 200; // caller apparently sleeps for slightly less sometimes... - if (usecs < 0) usecs = 0; - fps_slept (fpst, usecs); - } - - - /* It turns out that [ScreenSaverView setAnimationTimeInterval] does nothing. - This is bad, because some of the screen hacks want to delay for long - periods (like 5 seconds or a minute!) between frames, and running them - all at 60 FPS is no good. - - So, we don't use setAnimationTimeInterval, and just let the framework call - us whenever. But, we only invoke the screen hack's "draw frame" method - when enough time has expired. - - This means two extra calls to gettimeofday() per frame. For fast-cycling - screen savers, that might actually slow them down. Oh well. - - #### Also, we do not run the draw callback faster than the system's - animationTimeInterval, so if any savers are pickier about timing - than that, this may slow them down too much. If that's a problem, - then we could call draw_cb in a loop here (with usleep) until the - next call would put us past animationTimeInterval... But a better - approach would probably be to just change the saver to not do that. - */ - struct timeval tv; - gettimeofday (&tv, 0); - double now = tv.tv_sec + (tv.tv_usec / 1000000.0); - if (now < next_frame_time) return; - - [self prepareContext]; - - if (resized_p) { - // We do this here instead of in setFrame so that all the - // Xlib drawing takes place under the animation timer. - [self resizeContext]; - NSRect r; -# ifndef USE_BACKBUFFER - r = [self bounds]; -# else // USE_BACKBUFFER - r.origin.x = 0; - r.origin.y = 0; - r.size.width = backbuffer_size.width; - r.size.height = backbuffer_size.height; -# endif // USE_BACKBUFFER - - xsft->reshape_cb (xdpy, xwindow, xdata, r.size.width, r.size.height); - resized_p = NO; - } - - // Run any XtAppAddInput callbacks now. - // (Note that XtAppAddTimeOut callbacks have already been run by - // the Cocoa event loop.) - // - jwxyz_sources_run (display_sources_data (xdpy)); - - - // And finally: - // -# ifndef USE_IPHONE - NSDisableScreenUpdates(); -# endif - unsigned long delay = xsft->draw_cb (xdpy, xwindow, xdata); - if (fpst) xsft->fps_cb (xdpy, xwindow, fpst, xdata); -# ifndef USE_IPHONE - NSEnableScreenUpdates(); -# endif - - gettimeofday (&tv, 0); - now = tv.tv_sec + (tv.tv_usec / 1000000.0); - next_frame_time = now + (delay / 1000000.0); - -# ifdef USE_IPHONE // Allow savers on the iPhone to run full-tilt. - if (delay < [self animationTimeInterval]) - [self setAnimationTimeInterval:(delay / 1000000.0)]; -# endif - -# ifdef DO_GC_HACKERY - /* Current theory is that the 10.6 garbage collector sucks in the - following way: - - It only does a collection when a threshold of outstanding - collectable allocations has been surpassed. However, CoreGraphics - creates lots of small collectable allocations that contain pointers - to very large non-collectable allocations: a small CG object that's - collectable referencing large malloc'd allocations (non-collectable) - containing bitmap data. So the large allocation doesn't get freed - until GC collects the small allocation, which triggers its finalizer - to run which frees the large allocation. So GC is deciding that it - doesn't really need to run, even though the process has gotten - enormous. GC eventually runs once pageouts have happened, but by - then it's too late, and the machine's resident set has been - sodomized. - - So, we force an exhaustive garbage collection in this process - approximately every 5 seconds whether the system thinks it needs - one or not. - */ - { - static int tick = 0; - if (++tick > 5*30) { - tick = 0; - objc_collect (OBJC_EXHAUSTIVE_COLLECTION); - } - } -# endif // DO_GC_HACKERY - -# ifdef USE_IPHONE - } - @catch (NSException *e) { - [self handleException: e]; - } -# endif // USE_IPHONE -} - - -/* drawRect always does nothing, and animateOneFrame renders bits to the - screen. This is (now) true of both X11 and GL on both MacOS and iOS. - */ - -- (void)drawRect:(NSRect)rect -{ - if (xwindow) // clear to the X window's bg color, not necessarily black. - XClearWindow (xdpy, xwindow); - else - [super drawRect:rect]; // early: black. -} - - -#ifndef USE_BACKBUFFER - -- (void) animateOneFrame -{ - [self render_x11]; -} - -#else // USE_BACKBUFFER - -- (void) animateOneFrame -{ - // Render X11 into the backing store bitmap... - - NSAssert (backbuffer, @"no back buffer"); - -# ifdef USE_IPHONE - UIGraphicsPushContext (backbuffer); -# endif - - [self render_x11]; - -# ifdef USE_IPHONE - UIGraphicsPopContext(); -# endif - -# ifdef USE_IPHONE - // Then compute the transformations for rotation. - double hs = [self hackedContentScaleFactor]; - double s = [self contentScaleFactor]; - - // The rotation origin for layer.affineTransform is in the center already. - CGAffineTransform t = ignore_rotation_p ? - CGAffineTransformIdentity : - CGAffineTransformMakeRotation (rot_current_angle / (180.0 / M_PI)); - - CGFloat f = s / hs; - self.layer.affineTransform = CGAffineTransformScale(t, f, f); - - CGRect bounds; - bounds.origin.x = 0; - bounds.origin.y = 0; - bounds.size.width = backbuffer_size.width / s; - bounds.size.height = backbuffer_size.height / s; - self.layer.bounds = bounds; -# endif // USE_IPHONE - - [self.layer setNeedsDisplay]; -} - -- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx -{ - // This "isn't safe" if NULL is passed to CGBitmapCreateContext before iOS 4. - char *dest_data = (char *)CGBitmapContextGetData (ctx); - - // The CGContext here is normally upside-down on iOS. - if (dest_data && - CGBitmapContextGetBitmapInfo (ctx) == - (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host) -#ifdef USE_IPHONE - && CGContextGetCTM (ctx).d < 0 -#endif - ) - { - size_t dest_height = CGBitmapContextGetHeight (ctx); - size_t dest_bpr = CGBitmapContextGetBytesPerRow (ctx); - size_t src_height = CGBitmapContextGetHeight (backbuffer); - size_t src_bpr = CGBitmapContextGetBytesPerRow (backbuffer); - char *src_data = (char *)CGBitmapContextGetData (backbuffer); - - size_t height = src_height < dest_height ? src_height : dest_height; - - if (src_bpr == dest_bpr) { - // iPad 1: 4.0 ms, iPad 2: 6.7 ms - memcpy (dest_data, src_data, src_bpr * height); - } else { - // iPad 1: 4.6 ms, iPad 2: 7.2 ms - size_t bpr = src_bpr < dest_bpr ? src_bpr : dest_bpr; - while (height) { - memcpy (dest_data, src_data, bpr); - --height; - src_data += src_bpr; - dest_data += dest_bpr; - } - } - } else { - - // iPad 1: 9.6 ms, iPad 2: 12.1 ms - -#ifdef USE_IPHONE - CGContextScaleCTM (ctx, 1, -1); - CGFloat s = [self contentScaleFactor]; - CGFloat hs = [self hackedContentScaleFactor]; - CGContextTranslateCTM (ctx, 0, -backbuffer_size.height * hs / s); -#endif - - CGImageRef img = CGBitmapContextCreateImage (backbuffer); - CGContextDrawImage (ctx, self.layer.bounds, img); - CGImageRelease (img); - } -} - -#endif // !USE_BACKBUFFER - - - -- (void) setFrame:(NSRect) newRect -{ - [super setFrame:newRect]; - - if (xwindow) // inform Xlib that the window has changed now. - [self resize_x11]; -} - - -# ifndef USE_IPHONE // Doesn't exist on iOS -- (void) setFrameSize:(NSSize) newSize -{ - [super setFrameSize:newSize]; - if (xwindow) - [self resize_x11]; -} -# endif // !USE_IPHONE - - -+(BOOL) performGammaFade -{ - return YES; -} - -- (BOOL) hasConfigureSheet -{ - return YES; -} - -+ (NSString *) decompressXML: (NSData *)data -{ - if (! data) return 0; - BOOL compressed_p = !!strncmp ((const char *) data.bytes, "")); - } - - return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; -} - - -#ifndef USE_IPHONE -- (NSWindow *) configureSheet -#else -- (UIViewController *) configureView -#endif -{ - NSBundle *bundle = [NSBundle bundleForClass:[self class]]; - NSString *file = [NSString stringWithCString:xsft->progclass - encoding:NSISOLatin1StringEncoding]; - file = [file lowercaseString]; - NSString *path = [bundle pathForResource:file ofType:@"xml"]; - if (!path) { - NSLog (@"%@.xml does not exist in the application bundle: %@/", - file, [bundle resourcePath]); - return nil; - } - -# ifdef USE_IPHONE - UIViewController *sheet; -# else // !USE_IPHONE - NSWindow *sheet; -# endif // !USE_IPHONE - - NSData *xmld = [NSData dataWithContentsOfFile:path]; - NSString *xml = [[self class] decompressXML: xmld]; - sheet = [[XScreenSaverConfigSheet alloc] - initWithXML:[xml dataUsingEncoding:NSUTF8StringEncoding] - options:xsft->options - controller:[prefsReader userDefaultsController] - defaults:[prefsReader defaultOptions]]; - - // #### am I expected to retain this, or not? wtf. - // I thought not, but if I don't do this, we (sometimes) crash. - // #### Analyze says "potential leak of an object stored into sheet" - [sheet retain]; - - return sheet; -} - - -- (NSUserDefaultsController *) userDefaultsController -{ - return [prefsReader userDefaultsController]; -} - - -/* Announce our willingness to accept keyboard input. -*/ -- (BOOL)acceptsFirstResponder -{ - return YES; -} - - -#ifndef USE_IPHONE - -/* Convert an NSEvent into an XEvent, and pass it along. - Returns YES if it was handled. - */ -- (BOOL) doEvent: (NSEvent *) e - type: (int) type -{ - if (![self isPreview] || // no event handling if actually screen-saving! - ![self isAnimating] || - !initted_p) - return NO; - - XEvent xe; - memset (&xe, 0, sizeof(xe)); - - int state = 0; - - int flags = [e modifierFlags]; - if (flags & NSAlphaShiftKeyMask) state |= LockMask; - if (flags & NSShiftKeyMask) state |= ShiftMask; - if (flags & NSControlKeyMask) state |= ControlMask; - if (flags & NSAlternateKeyMask) state |= Mod1Mask; - if (flags & NSCommandKeyMask) state |= Mod2Mask; - - 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); - - xe.xany.type = type; - switch (type) { - case ButtonPress: - case ButtonRelease: - xe.xbutton.x = x; - xe.xbutton.y = y; - xe.xbutton.state = state; - if ([e type] == NSScrollWheel) - xe.xbutton.button = ([e deltaY] > 0 ? Button4 : - [e deltaY] < 0 ? Button5 : - [e deltaX] > 0 ? Button6 : - [e deltaX] < 0 ? Button7 : - 0); - else - xe.xbutton.button = [e buttonNumber] + 1; - break; - case MotionNotify: - xe.xmotion.x = x; - xe.xmotion.y = y; - xe.xmotion.state = state; - break; - case KeyPress: - case KeyRelease: - { - NSString *ns = (([e type] == NSFlagsChanged) ? 0 : - [e charactersIgnoringModifiers]); - KeySym k = 0; - - if (!ns || [ns length] == 0) // dead key - { - // Cocoa hides the difference between left and right keys. - // Also we only get KeyPress events for these, no KeyRelease - // (unless we hack the mod state manually. Bleh.) - // - if (flags & NSAlphaShiftKeyMask) k = XK_Caps_Lock; - else if (flags & NSShiftKeyMask) k = XK_Shift_L; - else if (flags & NSControlKeyMask) k = XK_Control_L; - else if (flags & NSAlternateKeyMask) k = XK_Alt_L; - else if (flags & NSCommandKeyMask) k = XK_Meta_L; - } - else if ([ns length] == 1) // real key - { - switch ([ns characterAtIndex:0]) { - case NSLeftArrowFunctionKey: k = XK_Left; break; - case NSRightArrowFunctionKey: k = XK_Right; break; - case NSUpArrowFunctionKey: k = XK_Up; break; - case NSDownArrowFunctionKey: k = XK_Down; break; - case NSPageUpFunctionKey: k = XK_Page_Up; break; - case NSPageDownFunctionKey: k = XK_Page_Down; break; - case NSHomeFunctionKey: k = XK_Home; break; - case NSPrevFunctionKey: k = XK_Prior; break; - case NSNextFunctionKey: k = XK_Next; break; - case NSBeginFunctionKey: k = XK_Begin; break; - case NSEndFunctionKey: k = XK_End; break; - default: - { - const char *s = - [ns cStringUsingEncoding:NSISOLatin1StringEncoding]; - k = (s && *s ? *s : 0); - } - break; - } - } - - if (! k) return YES; // E.g., "KeyRelease XK_Shift_L" - - xe.xkey.keycode = k; - xe.xkey.state = state; - break; - } - default: - NSAssert1 (0, @"unknown X11 event type: %d", type); - break; - } - - [self lockFocus]; - [self prepareContext]; - BOOL result = xsft->event_cb (xdpy, xwindow, xdata, &xe); - [self unlockFocus]; - return result; -} - - -- (void) mouseDown: (NSEvent *) e -{ - if (! [self doEvent:e type:ButtonPress]) - [super mouseDown:e]; -} - -- (void) mouseUp: (NSEvent *) e -{ - if (! [self doEvent:e type:ButtonRelease]) - [super mouseUp:e]; -} - -- (void) otherMouseDown: (NSEvent *) e -{ - if (! [self doEvent:e type:ButtonPress]) - [super otherMouseDown:e]; -} - -- (void) otherMouseUp: (NSEvent *) e -{ - if (! [self doEvent:e type:ButtonRelease]) - [super otherMouseUp:e]; -} - -- (void) mouseMoved: (NSEvent *) e -{ - if (! [self doEvent:e type:MotionNotify]) - [super mouseMoved:e]; -} - -- (void) mouseDragged: (NSEvent *) e -{ - if (! [self doEvent:e type:MotionNotify]) - [super mouseDragged:e]; -} - -- (void) otherMouseDragged: (NSEvent *) e -{ - if (! [self doEvent:e type:MotionNotify]) - [super otherMouseDragged:e]; -} - -- (void) scrollWheel: (NSEvent *) e -{ - if (! [self doEvent:e type:ButtonPress]) - [super scrollWheel:e]; -} - -- (void) keyDown: (NSEvent *) e -{ - if (! [self doEvent:e type:KeyPress]) - [super keyDown:e]; -} - -- (void) keyUp: (NSEvent *) e -{ - if (! [self doEvent:e type:KeyRelease]) - [super keyUp:e]; -} - -- (void) flagsChanged: (NSEvent *) e -{ - if (! [self doEvent:e type:KeyPress]) - [super flagsChanged:e]; -} - -#else // USE_IPHONE - - -- (void) stopAndClose:(Bool)relaunch_p -{ - if ([self isAnimating]) - [self stopAnimation]; - - /* Need to make the SaverListController be the firstResponder again - so that it can continue to receive its own shake events. I - suppose that this abstraction-breakage means that I'm adding - XScreenSaverView to the UINavigationController wrong... - */ - UIViewController *v = [[self window] rootViewController]; - if ([v isKindOfClass: [UINavigationController class]]) { - UINavigationController *n = (UINavigationController *) v; - [[n topViewController] becomeFirstResponder]; - } - - UIView *fader = [self superview]; // the "backgroundView" view is our parent - - if (relaunch_p) { // Fake a shake on the SaverListController. - // Why is [self window] sometimes null here? - UIWindow *w = [[UIApplication sharedApplication] keyWindow]; - UIViewController *v = [w rootViewController]; - if ([v isKindOfClass: [UINavigationController class]]) { - UINavigationController *n = (UINavigationController *) v; - [[n topViewController] motionEnded: UIEventSubtypeMotionShake - withEvent: nil]; - } - } else { // Not launching another, animate our return to the list. - [UIView animateWithDuration: 0.5 - animations:^{ fader.alpha = 0.0; } - completion:^(BOOL finished) { - [fader removeFromSuperview]; - fader.alpha = 1.0; - }]; - } -} - - -/* Called after the device's orientation has changed. - - Note: we could include a subclass of UIViewController which - contains a shouldAutorotateToInterfaceOrientation method that - returns YES, in which case Core Animation would auto-rotate our - View for us in response to rotation events... but, that interacts - badly with the EAGLContext -- if you introduce Core Animation into - the path, the OpenGL pipeline probably falls back on software - rendering and performance goes to hell. Also, the scaling and - rotation that Core Animation does interacts incorrectly with the GL - context anyway. - - So, we have to hack the rotation animation manually, in the GL world. - - Possibly XScreenSaverView should use Core Animation, and - XScreenSaverGLView should override that. -*/ -- (void)didRotate:(NSNotification *)notification -{ - UIDeviceOrientation current = [[UIDevice currentDevice] orientation]; - - /* If the simulator starts up in the rotated position, sometimes - the UIDevice says we're in Portrait when we're not -- but it - turns out that the UINavigationController knows what's up! - So get it from there. - */ - if (current == UIDeviceOrientationUnknown) { - switch ([[[self window] rootViewController] interfaceOrientation]) { - case UIInterfaceOrientationPortrait: - current = UIDeviceOrientationPortrait; - break; - case UIInterfaceOrientationPortraitUpsideDown: - current = UIDeviceOrientationPortraitUpsideDown; - break; - case UIInterfaceOrientationLandscapeLeft: // It's opposite day - current = UIDeviceOrientationLandscapeRight; - break; - case UIInterfaceOrientationLandscapeRight: - current = UIDeviceOrientationLandscapeLeft; - break; - default: - break; - } - } - - /* On the iPad (but not iPhone 3GS, or the simulator) sometimes we get - an orientation change event with an unknown orientation. Those seem - to always be immediately followed by another orientation change with - a *real* orientation change, so let's try just ignoring those bogus - ones and hoping that the real one comes in shortly... - */ - if (current == UIDeviceOrientationUnknown) - return; - - if (rotation_ratio >= 0) return; // in the midst of rotation animation - if (orientation == current) return; // no change - - // When transitioning to FaceUp or FaceDown, pretend there was no change. - if (current == UIDeviceOrientationFaceUp || - current == UIDeviceOrientationFaceDown) - return; - - new_orientation = current; // current animation target - rotation_ratio = 0; // start animating - rot_start_time = double_time(); - - switch (orientation) { - case UIDeviceOrientationLandscapeLeft: angle_from = 90; break; - case UIDeviceOrientationLandscapeRight: angle_from = 270; break; - case UIDeviceOrientationPortraitUpsideDown: angle_from = 180; break; - default: angle_from = 0; break; - } - - switch (new_orientation) { - case UIDeviceOrientationLandscapeLeft: angle_to = 90; break; - case UIDeviceOrientationLandscapeRight: angle_to = 270; break; - case UIDeviceOrientationPortraitUpsideDown: angle_to = 180; break; - default: angle_to = 0; break; - } - - switch (orientation) { - case UIDeviceOrientationLandscapeRight: // from landscape - case UIDeviceOrientationLandscapeLeft: - rot_from.width = initial_bounds.height; - rot_from.height = initial_bounds.width; - break; - default: // from portrait - rot_from.width = initial_bounds.width; - rot_from.height = initial_bounds.height; - break; - } - - switch (new_orientation) { - case UIDeviceOrientationLandscapeRight: // to landscape - case UIDeviceOrientationLandscapeLeft: - rot_to.width = initial_bounds.height; - rot_to.height = initial_bounds.width; - break; - default: // to portrait - rot_to.width = initial_bounds.width; - rot_to.height = initial_bounds.height; - break; - } - - if (! initted_p) { - // If we've done a rotation but the saver hasn't been initialized yet, - // don't bother going through an X11 resize, but just do it now. - rot_start_time = 0; // dawn of time - [self hackRotation]; - } -} - - -/* I believe we can't use UIGestureRecognizer for tracking touches - because UIPanGestureRecognizer doesn't give us enough detail in its - callbacks. - - Currently we don't handle multi-touches (just the first touch) but - I'm leaving this comment here for future reference: - - In the simulator, multi-touch sequences look like this: - - touchesBegan [touchA, touchB] - touchesEnd [touchA, touchB] - - But on real devices, sometimes you get that, but sometimes you get: - - touchesBegan [touchA, touchB] - touchesEnd [touchB] - touchesEnd [touchA] - - Or even - - touchesBegan [touchA] - touchesBegan [touchB] - touchesEnd [touchA] - touchesEnd [touchB] - - So the only way to properly detect a "pinch" gesture is to remember - the start-point of each touch as it comes in; and the end-point of - each touch as those come in; and only process the gesture once the - number of touchEnds matches the number of touchBegins. - */ - -- (void) rotateMouse:(int)rot x:(int*)x y:(int *)y w:(int)w h:(int)h -{ - // This is a no-op unless contentScaleFactor != hackedContentScaleFactor. - // Currently, this is the iPad Retina only. - CGRect frame = [self bounds]; // Scale. - double s = [self hackedContentScaleFactor]; - *x *= (backbuffer_size.width / frame.size.width) / s; - *y *= (backbuffer_size.height / frame.size.height) / s; -} - - -#if 0 // AudioToolbox/AudioToolbox.h -- (void) beep -{ - // There's no way to play a standard system alert sound! - // We'd have to include our own WAV for that. Eh, fuck it. - AudioServicesPlaySystemSound (kSystemSoundID_Vibrate); -# if TARGET_IPHONE_SIMULATOR - NSLog(@"BEEP"); // The sim doesn't vibrate. -# endif -} -#endif - - -/* We distinguish between taps and drags. - - Drags (down, motion, up) are sent to the saver to handle. - - Single-taps exit the saver. - This means a saver cannot respond to a single-tap. Only a few try to. - */ - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - // If they are trying to pinch, just do nothing. - if ([[event allTouches] count] > 1) - return; - - tap_time = 0; - - if (xsft->event_cb && xwindow) { - double s = [self hackedContentScaleFactor]; - XEvent xe; - memset (&xe, 0, sizeof(xe)); - int i = 0; - // #### 'frame' here or 'bounds'? - int w = s * [self frame].size.width; - int h = s * [self frame].size.height; - for (UITouch *touch in touches) { - CGPoint p = [touch locationInView:self]; - xe.xany.type = ButtonPress; - xe.xbutton.button = i + 1; - xe.xbutton.button = i + 1; - xe.xbutton.x = s * p.x; - xe.xbutton.y = s * p.y; - [self rotateMouse: rot_current_angle - x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h]; - jwxyz_mouse_moved (xdpy, xwindow, xe.xbutton.x, xe.xbutton.y); - - // Ignore return code: don't care whether the hack handled it. - xsft->event_cb (xdpy, xwindow, xdata, &xe); - - // Remember when/where this was, to determine tap versus drag or hold. - tap_time = double_time(); - tap_point = p; - - i++; - break; // No pinches: only look at the first touch. - } - } -} - - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - // If they are trying to pinch, just do nothing. - if ([[event allTouches] count] > 1) - return; - - if (xsft->event_cb && xwindow) { - double s = [self hackedContentScaleFactor]; - XEvent xe; - memset (&xe, 0, sizeof(xe)); - int i = 0; - // #### 'frame' here or 'bounds'? - int w = s * [self frame].size.width; - int h = s * [self frame].size.height; - for (UITouch *touch in touches) { - CGPoint p = [touch locationInView:self]; - - // If the ButtonRelease came less than half a second after ButtonPress, - // and didn't move far, then this was a tap, not a drag or a hold. - // Interpret it as "exit". - // - double dist = sqrt (((p.x - tap_point.x) * (p.x - tap_point.x)) + - ((p.y - tap_point.y) * (p.y - tap_point.y))); - if (tap_time + 0.5 >= double_time() && dist < 20) { - [self stopAndClose:NO]; - return; - } - - xe.xany.type = ButtonRelease; - xe.xbutton.button = i + 1; - xe.xbutton.x = s * p.x; - xe.xbutton.y = s * p.y; - [self rotateMouse: rot_current_angle - x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h]; - jwxyz_mouse_moved (xdpy, xwindow, xe.xbutton.x, xe.xbutton.y); - xsft->event_cb (xdpy, xwindow, xdata, &xe); - i++; - break; // No pinches: only look at the first touch. - } - } -} - - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ - // If they are trying to pinch, just do nothing. - if ([[event allTouches] count] > 1) - return; - - if (xsft->event_cb && xwindow) { - double s = [self hackedContentScaleFactor]; - XEvent xe; - memset (&xe, 0, sizeof(xe)); - int i = 0; - // #### 'frame' here or 'bounds'? - int w = s * [self frame].size.width; - int h = s * [self frame].size.height; - for (UITouch *touch in touches) { - CGPoint p = [touch locationInView:self]; - xe.xany.type = MotionNotify; - xe.xmotion.x = s * p.x; - xe.xmotion.y = s * p.y; - [self rotateMouse: rot_current_angle - x: &xe.xbutton.x y: &xe.xbutton.y w: w h: h]; - jwxyz_mouse_moved (xdpy, xwindow, xe.xmotion.x, xe.xmotion.y); - xsft->event_cb (xdpy, xwindow, xdata, &xe); - i++; - break; // No pinches: only look at the first touch. - } - } -} - - -/* We need this to respond to "shake" gestures - */ -- (BOOL)canBecomeFirstResponder -{ - return YES; -} - -- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event -{ -} - - -- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event -{ -} - -/* Shake means exit and launch a new saver. - */ -- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event -{ - [self stopAndClose:YES]; -} - - -- (void)setScreenLocked:(BOOL)locked -{ - if (screenLocked == locked) return; - screenLocked = locked; - if (locked) { - if ([self isAnimating]) - [self stopAnimation]; - } else { - if (! [self isAnimating]) - [self startAnimation]; - } -} - - -#endif // USE_IPHONE - - -@end - -/* Utility functions... - */ - -static PrefsReader * -get_prefsReader (Display *dpy) -{ - XScreenSaverView *view = jwxyz_window_view (XRootWindow (dpy, 0)); - if (!view) return 0; - return [view prefsReader]; -} - - -char * -get_string_resource (Display *dpy, char *name, char *class) -{ - return [get_prefsReader(dpy) getStringResource:name]; -} - -Bool -get_boolean_resource (Display *dpy, char *name, char *class) -{ - return [get_prefsReader(dpy) getBooleanResource:name]; -} - -int -get_integer_resource (Display *dpy, char *name, char *class) -{ - return [get_prefsReader(dpy) getIntegerResource:name]; -} - -double -get_float_resource (Display *dpy, char *name, char *class) -{ - return [get_prefsReader(dpy) getFloatResource:name]; -}