├── .gitignore ├── ActivateSwitcher.h ├── ActivateSwitcher.xmi ├── BaseGesture.h ├── BaseGesture.xmi ├── CHANGELOG ├── CPDistributedMessagingCenter.h ├── Common.h ├── Common.xmi ├── GestureEnabler.xmi ├── Grabber.h ├── Grabber.xmi ├── LICENSE ├── Lockscreen.xmi ├── Makefile ├── NSTimer+Blocks.h ├── NSTimer+Blocks.m ├── Notification.h ├── Notification.xmi ├── OffscreenGesture.h ├── OffscreenGesture.xmi ├── Preferences.h ├── Preferences.xmi ├── SwitchApp.h ├── SwitchApp.xmi ├── Switcher.h ├── Switcher.xmi ├── TODO ├── Zephyr.plist ├── Zephyr@2x.pxm ├── ZephyrKeyboardProxy.plist ├── iPhonePrivate.h ├── layersnapshotter.h └── layout ├── DEBIAN └── control └── Library └── PreferenceLoader └── Preferences ├── Zephyr.plist ├── Zephyr.png └── Zephyr@2x.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | obj 4 | *.deb 5 | _ 6 | 7 | theos 8 | .theos 9 | -------------------------------------------------------------------------------- /ActivateSwitcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | extern "C" void ZephyrActivateSwitcherFromToDuration(CGFloat from, CGFloat to, CGFloat duration); 27 | 28 | -------------------------------------------------------------------------------- /ActivateSwitcher.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "iPhonePrivate.h" 28 | #import "ActivateSwitcher.h" 29 | 30 | static BOOL activateSwitcherCustom = NO; 31 | static CGFloat activateSwitcherFrom = 0.0; 32 | static CGFloat activateSwitcherTo = 0.0; 33 | 34 | %group ActivateSwitcher 35 | 36 | %hook SBUIController 37 | 38 | - (BOOL)_canActivateShowcaseIgnoringTouches:(BOOL)touches { 39 | if (activateSwitcherCustom) { 40 | return YES; 41 | } 42 | 43 | return %orig; 44 | } 45 | 46 | - (SBShowcaseContext *)_showcaseContextForOffset:(CGFloat)offset { 47 | if (activateSwitcherCustom) { 48 | if (offset == 0.0) { 49 | offset = activateSwitcherFrom; 50 | } else { 51 | offset = activateSwitcherTo; 52 | } 53 | } 54 | 55 | SBShowcaseContext *context = %orig; 56 | 57 | if (activateSwitcherCustom) { 58 | [context setShowcaseOrientation:ZephyrCurrentInterfaceOrientation()]; 59 | } 60 | 61 | return context; 62 | } 63 | 64 | %end 65 | 66 | %end 67 | 68 | %ctor { 69 | %init(ActivateSwitcher); 70 | } 71 | 72 | @interface SBMusicController : NSObject 73 | - (void) switcherDidShow:(double)duration orientation:(UIInterfaceOrientation)orientation; 74 | @end 75 | 76 | static void OpenMusicControlsTaskSwitcherIfNecessaryWithDuration(double duration) { 77 | Class $SBMusicController = objc_getClass("SBMusicController"); 78 | if ($SBMusicController) { 79 | bool defaultValue = YES; 80 | CFPreferencesSynchronize((CFStringRef)@"com.phoenix.musiccontrols.task", kCFPreferencesCurrentUser, kCFPreferencesCurrentHost); 81 | CFBooleanRef val = (CFBooleanRef) CFPreferencesCopyAppValue((CFStringRef) @"Controls", (CFStringRef) @"com.phoenix.musiccontrols.task"); 82 | 83 | if (val) { 84 | defaultValue = [(NSNumber*) val boolValue]; 85 | CFRelease(val); 86 | } 87 | 88 | if (defaultValue) { 89 | [[$SBMusicController sharedInstance] switcherDidShow:duration orientation:ZephyrCurrentInterfaceOrientation()]; 90 | } 91 | } 92 | } 93 | 94 | void ZephyrActivateSwitcherFromToDuration(CGFloat from, CGFloat to, CGFloat duration) { 95 | SBUIController *uic = [objc_getClass("SBUIController") sharedInstance]; 96 | SBAppSwitcherController *switcher = [objc_getClass("SBAppSwitcherController") sharedInstance]; 97 | 98 | if (!ZephyrMultitaskingSupported()) return; 99 | if ([switcher printViewIsShowing]) return; 100 | if ([uic isSwitcherShowing]) return; 101 | 102 | activateSwitcherCustom = YES; 103 | activateSwitcherFrom = from; 104 | activateSwitcherTo = to; 105 | 106 | [uic _activateSwitcher:duration]; 107 | 108 | activateSwitcherTo = 0.0; 109 | activateSwitcherFrom = 0.0; 110 | activateSwitcherCustom = NO; 111 | 112 | // Support Music Controls Pro, code by phoenixdev. 113 | OpenMusicControlsTaskSwitcherIfNecessaryWithDuration(duration); 114 | } 115 | -------------------------------------------------------------------------------- /BaseGesture.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "iPhonePrivate.h" 27 | 28 | @interface ZephyrBaseGesture : NSObject { 29 | NSMutableArray *gestureRecognizers; 30 | BOOL isActive; 31 | } 32 | 33 | @property (nonatomic, readonly) BOOL isActive; 34 | @property (nonatomic, readonly) NSMutableArray *gestureRecognizers; 35 | 36 | - (void)addOffscreenEdge:(SBOffscreenEdge)edge minimumTouchCount:(int)count edgeMargin:(CGFloat)edgeMargin; 37 | - (BOOL)currentOrientationIsSupported; 38 | 39 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge; 40 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge atPoint:(CGPoint)point; // only if shouldActivateAtEdge: too. 41 | - (BOOL)shouldUseGrabberAtEdge:(SBOffscreenEdge)edge; 42 | 43 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location; 44 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity; 45 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type; 46 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer; 47 | - (void)cancelGesture; 48 | 49 | @end 50 | 51 | -------------------------------------------------------------------------------- /BaseGesture.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "BaseGesture.h" 27 | #import "OffscreenGesture.h" 28 | #import "Preferences.h" 29 | #import "Grabber.h" 30 | 31 | static NSMutableArray *allGestures = nil; 32 | 33 | %ctor { 34 | // XXX: memory leak? 35 | allGestures = [[NSMutableArray alloc] init]; 36 | } 37 | 38 | @implementation ZephyrBaseGesture 39 | @synthesize isActive, gestureRecognizers; 40 | 41 | + (NSArray *)allGestures { 42 | return allGestures; 43 | } 44 | 45 | + (BOOL)isAnyGestureActive { 46 | BOOL isActive = NO; 47 | 48 | for (ZephyrBaseGesture *gesture in allGestures) { 49 | if ([gesture isActive]) { 50 | isActive = YES; 51 | } 52 | } 53 | 54 | return isActive; 55 | } 56 | 57 | - (id)init { 58 | if ((self = [super init])) { 59 | gestureRecognizers = [[NSMutableArray alloc] init]; 60 | [allGestures addObject:self]; 61 | } 62 | 63 | return self; 64 | } 65 | 66 | - (void)cancelGesture { 67 | isActive = NO; 68 | } 69 | 70 | - (void)addOffscreenEdge:(SBOffscreenEdge)edge minimumTouchCount:(int)touchCount edgeMargin:(CGFloat)sensitivity { 71 | SBOffscreenSwipeGestureRecognizer *gestureRecognizer = [[objc_getClass("SBOffscreenSwipeGestureRecognizer") alloc] initForOffscreenEdge:edge]; 72 | [gestureRecognizer setAllowableDistanceFromEdgeCenter:CGFLOAT_MAX]; 73 | [gestureRecognizer setSendsTouchesCancelledToApplication:YES]; 74 | [gestureRecognizer setRequiresSecondTouchInRange:NO]; 75 | [gestureRecognizer setMinTouches:touchCount]; 76 | [gestureRecognizer setEdgeMargin:sensitivity]; 77 | // [gestureRecognizer setFalseEdge:-1.0f]; // what is this? 78 | [gestureRecognizer setTypes:0x10]; 79 | 80 | [gestureRecognizer setHandler:^{ 81 | CGFloat distance = [gestureRecognizer cumulativePercentage]; 82 | CGPoint velocity = [gestureRecognizer movementVelocityInPointsPerSecond]; 83 | 84 | switch ([gestureRecognizer state]) { 85 | case 0: 86 | break; 87 | case 1: 88 | if (![self isActive] && ![ZephyrBaseGesture isAnyGestureActive]) { 89 | if (![self shouldUseGrabberAtEdge:edge] || [[ZephyrGrabberController sharedInstance] visibleAtEdge:edge]) { 90 | [[ZephyrGrabberController sharedInstance] hideAtEdge:edge animated:YES]; 91 | 92 | [self handleGestureBegan:gestureRecognizer withLocation:distance]; 93 | isActive = YES; 94 | } else { 95 | [[ZephyrGrabberController sharedInstance] showAtEdge:edge animated:YES]; 96 | } 97 | } 98 | break; 99 | case 2: 100 | if ([self isActive]) { 101 | [self handleGestureChanged:gestureRecognizer withLocation:distance velocity:velocity]; 102 | } 103 | break; 104 | case 3: 105 | if ([self isActive]) { 106 | [self handleGestureEnded:gestureRecognizer withLocation:distance velocity:velocity completionType:[gestureRecognizer completionTypeProjectingMomentumForInterval:5.0f]]; 107 | isActive = NO; 108 | } 109 | break; 110 | case 4: 111 | // XXX: this is disabled because it is called even if the gesture was never actually "started": 112 | // there is no way (currently) to tell if a gesture was started before it got cancelled, so this 113 | // gives us spurious cancelations that break any gesture with two possible recognizers. :( 114 | // XXX: the above text is not true. if both gestures are enabled then the active gesture is somehow 115 | // automatically canceled. I do not know why this is, but it requires us to sadly ignore cancels now. 116 | // [self handleGestureCanceled:gestureRecognizer]; 117 | // isActive = NO; 118 | break; 119 | default: 120 | if ([self isActive]) { 121 | NSLog(@"[Zephyr] BUG: Weird recognizer state %d in %@.", [gestureRecognizer state], self); 122 | isActive = NO; 123 | } 124 | break; 125 | } 126 | }]; 127 | 128 | [gestureRecognizer setCanBeginCondition:^BOOL { 129 | BOOL should = [self shouldActivateAtEdge:edge]; 130 | if (!should) return NO; 131 | 132 | return YES; 133 | }]; 134 | 135 | [gestureRecognizers addObject:gestureRecognizer]; 136 | SBGestureRecognizerRegister(gestureRecognizer); 137 | } 138 | 139 | - (void)dealloc { 140 | for (SBGestureRecognizer *gestureRecognizer in gestureRecognizers) { 141 | [gestureRecognizers removeObject:gestureRecognizer]; 142 | SBGestureRecognizerUnregister(gestureRecognizer); 143 | 144 | [gestureRecognizer release]; 145 | } 146 | 147 | [super dealloc]; 148 | } 149 | 150 | - (BOOL)currentOrientationIsSupported { 151 | return YES; 152 | } 153 | 154 | - (BOOL)shouldUseGrabberAtEdge:(SBOffscreenEdge)edge { 155 | return NO; 156 | } 157 | 158 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge { 159 | return NO; 160 | } 161 | 162 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge atPoint:(CGPoint)point { 163 | return [self shouldActivateAtEdge:edge]; 164 | } 165 | 166 | - (BOOL)gestureRecognizer:(SBGestureRecognizer *)recognizer shouldActivateAtEdge:(SBOffscreenEdge)edge atPoint:(CGPoint)point { 167 | NSUInteger minTouches = 1; 168 | 169 | if ([recognizer isKindOfClass:objc_getClass("SBFluidSlideGestureRecognizer")]) { 170 | // only SBFluidSlideGestureRecognizer subclasses have a minTouches method 171 | minTouches = [(SBFluidSlideGestureRecognizer *) recognizer minTouches]; 172 | } 173 | 174 | // If we have only one touch required and a grabber, force you to be inside the grabber to grab it. 175 | // (We only do this with one touch as more fingers can't all fit inside the grabber at once...) 176 | if (minTouches == 1 && [[ZephyrGrabberController sharedInstance] visibleAtEdge:edge]) { 177 | BOOL grabber = [[ZephyrGrabberController sharedInstance] pointInside:point atEdge:edge]; 178 | 179 | if (!grabber) { 180 | // If we don't grab the grabber, hide it. 181 | [[ZephyrGrabberController sharedInstance] hideAtEdge:edge animated:YES]; 182 | } 183 | 184 | // Don't let other point detection logic (e.g. top third, or keyboard) block the grabber. 185 | return grabber; 186 | } 187 | 188 | return [self shouldActivateAtEdge:edge atPoint:point]; 189 | } 190 | 191 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location { 192 | 193 | } 194 | 195 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity { 196 | 197 | } 198 | 199 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type { 200 | 201 | } 202 | 203 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer { 204 | 205 | } 206 | 207 | @end 208 | 209 | static ZephyrBaseGesture *GestureForRecognizer(SBGestureRecognizer *recognizer) { 210 | for (ZephyrBaseGesture *gesture in allGestures) { 211 | if ([[gesture gestureRecognizers] containsObject:recognizer]) { 212 | return gesture; 213 | } 214 | } 215 | 216 | return nil; 217 | } 218 | 219 | %group SwipeDetectionRegion 220 | %hook SBOffscreenSwipeGestureRecognizer 221 | 222 | - (BOOL)firstTouchInRange:(CGPoint)point { 223 | BOOL range = %orig; 224 | if (range) { 225 | ZephyrBaseGesture *gesture = GestureForRecognizer(self); 226 | 227 | if (gesture != nil) { 228 | SBOffscreenEdge edge = MSHookIvar(self, "m_offscreenEdge"); 229 | return [gesture gestureRecognizer:self shouldActivateAtEdge:edge atPoint:point]; 230 | } else { 231 | return YES; 232 | } 233 | } else { 234 | return NO; 235 | } 236 | } 237 | 238 | %end 239 | %end 240 | 241 | %group DisableHardwareButtons 242 | %hook SpringBoard 243 | 244 | - (void)lockButtonDown:(GSEventRef)event { 245 | if ([ZephyrBaseGesture isAnyGestureActive]) return; 246 | 247 | %orig; 248 | } 249 | 250 | - (void)lockButtonUp:(GSEventRef)event { 251 | if ([ZephyrBaseGesture isAnyGestureActive]) return; 252 | 253 | %orig; 254 | } 255 | 256 | - (void)menuButtonDown:(GSEventRef)event { 257 | if ([ZephyrBaseGesture isAnyGestureActive]) return; 258 | 259 | %orig; 260 | } 261 | 262 | - (void)menuButtonUp:(GSEventRef)event { 263 | if ([ZephyrBaseGesture isAnyGestureActive]) return; 264 | 265 | %orig; 266 | } 267 | 268 | - (void)_handleMenuButtonEvent { 269 | if ([ZephyrBaseGesture isAnyGestureActive]) return; 270 | 271 | %orig; 272 | } 273 | 274 | %end 275 | %end 276 | 277 | %ctor { 278 | %init(SwipeDetectionRegion); 279 | %init(DisableHardwareButtons); 280 | } 281 | 282 | 283 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | ## 1.0.2 3 | - Select which apps to disable Zephyr in. (thanks @rpetrich!) 4 | - Move all preferences to be per-gesture, rather than global. 5 | 6 | ## 1.0.1 7 | - Option to require more than one finger to use a gesture. 8 | - Option to disable when keyboard is showing. 9 | - Option to disable in Cut the Rope, Words with Friends. 10 | - (Remember, it's disabled in landscape so most games (Fruit Ninja, Flight Control, etc) already work great!) 11 | - Fix visual issues with app switcher swipe gesture. 12 | - Fix various bugs with Notification Center. 13 | - Fix low-res bounce rendering. (thanks @rpetrich!) 14 | 15 | 16 | ## 1.0 17 | - Initial release. 18 | -------------------------------------------------------------------------------- /CPDistributedMessagingCenter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | @interface CPDistributedMessagingCenter : NSObject 27 | + (CPDistributedMessagingCenter *)centerNamed:(NSString *)serverName; 28 | - (NSString *)name; 29 | 30 | - (BOOL)sendMessageName:(NSString *)name userInfo:(NSDictionary *)info; 31 | - (NSDictionary *)sendMessageAndReceiveReplyName:(NSString *)name userInfo:(NSDictionary *)info; 32 | - (NSDictionary *)sendMessageAndReceiveReplyName:(NSString *)name userInfo:(NSDictionary *)info error:(NSError * *)error; 33 | - (void)sendMessageAndReceiveReplyName:(NSString *)name userInfo:(NSDictionary *)info toTarget:(id)target selector:(SEL)selector context:(void *)context; 34 | 35 | - (void)runServerOnCurrentThread; 36 | - (void)runServerOnCurrentThreadProtectedByEntitlement:(id)entitlement; 37 | - (void)stopServer; 38 | 39 | - (void)registerForMessageName:(NSString *)messageName target:(id)target selector:(SEL)selector; 40 | - (void)unregisterForMessageName:(NSString *)messageName; 41 | @end 42 | 43 | -------------------------------------------------------------------------------- /Common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "iPhonePrivate.h" 27 | 28 | CGFloat ZephyrHeightForOrientation(UIInterfaceOrientation orientation); 29 | CGFloat ZephyrHeightFromScreenPercentage(CGFloat percentage, UIInterfaceOrientation orientation); 30 | CGFloat ZephyrWidthForOrientation(UIInterfaceOrientation orientation); 31 | 32 | void ZephyrRotateViewFromOrientationToOrientation(UIView *view, UIInterfaceOrientation fromOrientation, UIInterfaceOrientation toOrientation, BOOL inPlace); 33 | 34 | CGFloat ZephyrScreenRounded(CGFloat value); 35 | 36 | UIView *ZephyrViewForApplication(SBApplication *app); 37 | UIView *ZephyrViewWithScreenshotOfHomeScreen(); 38 | UIView *ZephyrViewWithScreenshotOfView(UIView *view); 39 | 40 | UIInterfaceOrientation ZephyrOrientationFlip(UIInterfaceOrientation orientation); 41 | NSString *ZephyrDescriptionForOrientation(UIInterfaceOrientation orientation); 42 | 43 | UIInterfaceOrientation ZephyrHomeInterfaceOrientation(); 44 | BOOL ZephyrHomeShouldRotateToInterfaceOrientation(UIInterfaceOrientation orientation); 45 | UIInterfaceOrientation ZephyrDeviceInterfaceOrientation(); 46 | UIInterfaceOrientation ZephyrCurrentInterfaceOrientation(); 47 | 48 | void ZephyrDeactivateFrontmostApplication(); 49 | CGFloat ZephyrAppSwitcherHeight(); 50 | 51 | // Compatibility 52 | BOOL ZephyrAssistantIsVisible(); 53 | BOOL ZephyrMultitaskingSupported(); 54 | 55 | -------------------------------------------------------------------------------- /Common.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "LayerSnapshotter.h" 28 | 29 | NSString *ZephyrDescriptionForOrientation(UIInterfaceOrientation orientation) { 30 | switch (orientation) { 31 | case UIInterfaceOrientationPortrait: 32 | return @"portrait"; 33 | break; 34 | case UIInterfaceOrientationPortraitUpsideDown: 35 | return @"portrait upside-down"; 36 | break; 37 | case UIInterfaceOrientationLandscapeLeft: 38 | return @"landscape left"; 39 | break; 40 | case UIInterfaceOrientationLandscapeRight: 41 | return @"landscape right"; 42 | break; 43 | } 44 | 45 | return @"invalid orientation"; 46 | } 47 | 48 | UIInterfaceOrientation ZephyrOrientationFlip(UIInterfaceOrientation orientation) { 49 | switch (orientation) { 50 | case UIInterfaceOrientationPortrait: 51 | return UIInterfaceOrientationPortraitUpsideDown; 52 | case UIInterfaceOrientationPortraitUpsideDown: 53 | return UIInterfaceOrientationPortrait; 54 | case UIInterfaceOrientationLandscapeLeft: 55 | return UIInterfaceOrientationLandscapeRight; 56 | case UIInterfaceOrientationLandscapeRight: 57 | return UIInterfaceOrientationLandscapeLeft; 58 | } 59 | 60 | return orientation; 61 | } 62 | 63 | void ZephyrRotateViewFromOrientationToOrientation(UIView *view, UIInterfaceOrientation fromOrientation, UIInterfaceOrientation toOrientation, BOOL inPlace) { 64 | CGAffineTransform transform = [view transform]; 65 | CGRect frame = [view frame]; 66 | 67 | CGFloat rotation = 0.0f; 68 | BOOL perpendicular = NO; 69 | BOOL clockwise = NO; 70 | 71 | switch (fromOrientation) { 72 | case UIInterfaceOrientationPortrait: 73 | switch (toOrientation) { 74 | case UIInterfaceOrientationPortrait: 75 | rotation = 0.0f; 76 | break; 77 | case UIInterfaceOrientationPortraitUpsideDown: 78 | rotation = M_PI; 79 | break; 80 | case UIInterfaceOrientationLandscapeLeft: 81 | rotation = -(M_PI / 2.0f); 82 | clockwise = YES; 83 | perpendicular = YES; 84 | break; 85 | case UIInterfaceOrientationLandscapeRight: 86 | rotation = (M_PI / 2.0f); 87 | perpendicular = YES; 88 | break; 89 | } 90 | break; 91 | case UIInterfaceOrientationPortraitUpsideDown: 92 | switch (toOrientation) { 93 | case UIInterfaceOrientationPortrait: 94 | rotation = M_PI; 95 | break; 96 | case UIInterfaceOrientationPortraitUpsideDown: 97 | rotation = 0.0f; 98 | break; 99 | case UIInterfaceOrientationLandscapeLeft: 100 | rotation = (M_PI / 2.0f); 101 | perpendicular = YES; 102 | break; 103 | case UIInterfaceOrientationLandscapeRight: 104 | rotation = -(M_PI / 2.0f); 105 | clockwise = YES; 106 | perpendicular = YES; 107 | break; 108 | } 109 | break; 110 | case UIInterfaceOrientationLandscapeLeft: 111 | switch (toOrientation) { 112 | case UIInterfaceOrientationPortrait: 113 | rotation = (M_PI / 2.0f); 114 | perpendicular = YES; 115 | break; 116 | case UIInterfaceOrientationPortraitUpsideDown: 117 | rotation = -(M_PI / 2.0f); 118 | clockwise = YES; 119 | perpendicular = YES; 120 | break; 121 | case UIInterfaceOrientationLandscapeLeft: 122 | rotation = 0.0f; 123 | break; 124 | case UIInterfaceOrientationLandscapeRight: 125 | rotation = M_PI; 126 | break; 127 | } 128 | break; 129 | case UIInterfaceOrientationLandscapeRight: 130 | switch (toOrientation) { 131 | case UIInterfaceOrientationPortrait: 132 | rotation = -(M_PI / 2.0f); 133 | clockwise = YES; 134 | perpendicular = YES; 135 | break; 136 | case UIInterfaceOrientationPortraitUpsideDown: 137 | rotation = (M_PI / 2.0f); 138 | perpendicular = YES; 139 | break; 140 | case UIInterfaceOrientationLandscapeLeft: 141 | rotation = M_PI; 142 | break; 143 | case UIInterfaceOrientationLandscapeRight: 144 | rotation = 0.0f; 145 | break; 146 | } 147 | break; 148 | } 149 | 150 | transform = CGAffineTransformRotate(transform, rotation); 151 | 152 | if (perpendicular) { 153 | if (inPlace) { 154 | CGRect frame = [view frame]; 155 | frame.size.width = [view frame].size.height; 156 | frame.size.height = [view frame].size.width; 157 | [view setFrame:frame]; 158 | } 159 | 160 | if (clockwise) { 161 | transform = CGAffineTransformTranslate(transform, (frame.size.width - frame.size.height) / 2.0f, (frame.size.height - frame.size.width) / -2.0f); 162 | } else { 163 | transform = CGAffineTransformTranslate(transform, (frame.size.width - frame.size.height) / -2.0f, (frame.size.height - frame.size.width) / 2.0f); 164 | } 165 | } 166 | 167 | [view setTransform:transform]; 168 | } 169 | 170 | UIView *ZephyrViewForApplication(SBApplication *app) { 171 | SBGestureViewVendor *vendor = [objc_getClass("SBGestureViewVendor") sharedInstance]; 172 | 173 | [vendor clearCacheForApp:app reason:@"SwitchApp"]; 174 | return [vendor viewForApp:app gestureType:kSBGestureTypeSwitchApp includeStatusBar:YES]; 175 | } 176 | 177 | %group GestureViewStatusBar 178 | 179 | // Switch app orientation depends on gestures flag, so hook it and apply it ourselves. 180 | static BOOL switchAppOrientationFlag = NO; 181 | 182 | %hook SBApplication 183 | 184 | - (UIInterfaceOrientation)launchingInterfaceOrientationForCurrentOrientation { 185 | if (switchAppOrientationFlag) { 186 | return ZephyrCurrentInterfaceOrientation(); 187 | } else { 188 | return %orig; 189 | } 190 | } 191 | 192 | %end 193 | 194 | %hook SBUIController 195 | 196 | - (id)systemGestureSnapshotForApp:(SBApplication *)app includeStatusBar:(BOOL)include decodeImage:(BOOL)decode { 197 | switchAppOrientationFlag = YES; 198 | id result = %orig; 199 | switchAppOrientationFlag = NO; 200 | return result; 201 | } 202 | 203 | %end 204 | 205 | %hook SBGestureViewVendor 206 | 207 | - (void)maskViewIfNeeded:(UIView *)view gestureType:(SBGestureType)type viewType:(int)viewType contextHostViewRequester:(NSString *)requester app:(SBApplication *)app { 208 | switchAppOrientationFlag = YES; 209 | %orig; 210 | switchAppOrientationFlag = NO; 211 | } 212 | 213 | %end 214 | 215 | %end 216 | 217 | %ctor { 218 | %init(GestureViewStatusBar); 219 | } 220 | 221 | CGFloat ZephyrScreenRounded(CGFloat value) { 222 | CGFloat scale = [UIScreen mainScreen].scale; 223 | return roundf(value * scale) / scale; 224 | } 225 | 226 | UIView *ZephyrViewWithScreenshotOfView(UIView *view) { 227 | dlopen("/usr/lib/liblayersnapshotter.dylib", RTLD_LAZY); 228 | 229 | if ([view respondsToSelector:@selector(renderSnapshot)]) { 230 | UIImage *viewImage = [view renderSnapshot]; 231 | UIImageView *screenshotView = [[UIImageView alloc] initWithImage:viewImage]; 232 | [screenshotView setFrame:[view frame]]; 233 | return [screenshotView autorelease]; 234 | } else { 235 | return [[[UIView alloc] initWithFrame:view.frame] autorelease]; 236 | } 237 | } 238 | 239 | UIView *ZephyrViewWithScreenshotOfHomeScreen() { 240 | // XXX: using contentView here isn't necessarily the best option, but it works. 241 | UIView *homeView = ZephyrViewWithScreenshotOfView([[objc_getClass("SBUIController") sharedInstance] contentView]); 242 | [homeView setBackgroundColor:[UIColor blackColor]]; 243 | 244 | CGRect statusFrame = [homeView bounds]; 245 | statusFrame.size.height = UIStatusBarHeight; 246 | UIStatusBar *statusView = [[objc_getClass("UIStatusBar") alloc] initWithFrame:statusFrame]; 247 | [statusView requestStyle:UIStatusBarStyleBlackTranslucent animated:NO]; 248 | [homeView addSubview:statusView]; 249 | [statusView release]; 250 | 251 | return homeView; 252 | } 253 | 254 | CGFloat ZephyrHeightForOrientation(UIInterfaceOrientation orientation) { 255 | if (UIInterfaceOrientationIsLandscape(orientation)) { 256 | return [[UIScreen mainScreen] bounds].size.width; 257 | } else { 258 | return [[UIScreen mainScreen] bounds].size.height; 259 | } 260 | } 261 | 262 | CGFloat ZephyrWidthForOrientation(UIInterfaceOrientation orientation) { 263 | if (UIInterfaceOrientationIsLandscape(orientation)) { 264 | return [[UIScreen mainScreen] bounds].size.height; 265 | } else { 266 | return [[UIScreen mainScreen] bounds].size.width; 267 | } 268 | } 269 | 270 | CGFloat ZephyrHeightFromScreenPercentage(CGFloat percentage, UIInterfaceOrientation orientation) { 271 | return ZephyrHeightForOrientation(orientation) * percentage; 272 | } 273 | 274 | UIInterfaceOrientation ZephyrCurrentInterfaceOrientation() { 275 | UIInterfaceOrientation orientation = [SBApp _frontMostAppOrientation]; 276 | return orientation; 277 | } 278 | 279 | UIInterfaceOrientation ZephyrDeviceInterfaceOrientation() { 280 | return [SBApp interfaceOrientationForCurrentDeviceOrientation]; 281 | } 282 | 283 | UIInterfaceOrientation ZephyrHomeInterfaceOrientation() { 284 | UIInterfaceOrientation orientation = ZephyrCurrentInterfaceOrientation(); 285 | 286 | // Often, the Current interface orientation is different than the home screen's. 287 | // In that case, we prefer what the user is seeing: the Current one. However, 288 | // if that isn't a valid home orientation, then return what we know is valid. 289 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(orientation)) { 290 | orientation = [SBApp interfaceOrientation]; 291 | } 292 | 293 | return orientation; 294 | } 295 | 296 | BOOL ZephyrHomeShouldRotateToInterfaceOrientation(UIInterfaceOrientation orientation) { 297 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 298 | return [controller window:[controller window] shouldAutorotateToInterfaceOrientation:orientation]; 299 | } 300 | 301 | BOOL ZephyrAssistantIsVisible() { 302 | Class $SBAssistantController = objc_getClass("SBAssistantController"); 303 | 304 | if ([$SBAssistantController respondsToSelector:@selector(isAssistantVisible)]) { 305 | return [$SBAssistantController isAssistantVisible]; 306 | } else if ([[$SBAssistantController sharedInstance] respondsToSelector:@selector(isAssistantVisible)]) { 307 | return [[$SBAssistantController sharedInstance] isAssistantVisible]; 308 | } else { 309 | return NO; 310 | } 311 | } 312 | 313 | BOOL ZephyrMultitaskingSupported() { 314 | Class $SBApplication = objc_getClass("SBApplication"); 315 | 316 | if ([$SBApplication respondsToSelector:@selector(multitaskingIsSupported)]) { 317 | return [$SBApplication multitaskingIsSupported]; 318 | } 319 | 320 | return YES; 321 | 322 | } 323 | static BOOL disableUnscatterFlag = NO; 324 | 325 | void ZephyrDeactivateFrontmostApplication() { 326 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 327 | 328 | if (objc_getClass("SBDisplayStack") != nil) { 329 | // XXX: the "disableIconUnscatter" display flag there does not work, so: 330 | disableUnscatterFlag = YES; 331 | 332 | // Stolen from SwitchApp.xmi; this is a bad decompliation of 333 | // what a different function does than the one I am reversing. 334 | [app setDeactivationSetting:24 flag:YES]; 335 | [app setDeactivationSetting:18 flag:YES]; 336 | 337 | /* sub_2C330 */ { 338 | if ([SBWActiveDisplayStack containsDisplay:app] || [SBWSuspendedEventOnlyDisplayStack containsDisplay:app]) { 339 | 340 | /* sub_2B9B8 */ { 341 | // this is a crude approximation of sorta kinda what this gigantic 342 | // function does, since I'm waaaaay too lazy to reverse the whole 343 | // thing: even just the graph in IDA is quite scary to look at! :( 344 | [SBWActiveDisplayStack popDisplay:app]; 345 | [SBWSuspendingDisplayStack pushDisplay:app]; 346 | } 347 | } else if (![SBWSuspendingDisplayStack containsDisplay:app]) { 348 | [app clearDeactivationSettings]; 349 | } 350 | } 351 | } else { 352 | // Force iPhone devices to portrait for the home screen. 353 | // Should be generalized; the iPhone might be hacked for more orientations. 354 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 355 | UIInterfaceOrientation currentOrientation = ZephyrHomeInterfaceOrientation(); 356 | [[UIDevice currentDevice] setOrientation:currentOrientation animated:NO]; 357 | [SBApp noteInterfaceOrientationChanged:currentOrientation]; 358 | } 359 | 360 | [app setDeactivationSetting:0x15 flag:YES]; 361 | [app setDeactivationSetting:0x16 flag:YES]; 362 | 363 | NSString *label = @"ActivateSpringBoard"; 364 | SBWorkspaceEvent *event = [objc_getClass("SBWorkspaceEvent") eventWithLabel:label handler:^{ 365 | BKSWorkspace *workspace = [SBWSharedWorkspace bksWorkspace]; 366 | SBAppToAppWorkspaceTransaction *transaction = [[objc_getClass("SBAppToAppWorkspaceTransaction") alloc] initWithWorkspace:workspace alertManager:nil from:app to:nil]; 367 | [SBWSharedWorkspace setCurrentTransaction:transaction]; 368 | [transaction release]; 369 | }]; 370 | 371 | [[objc_getClass("SBWorkspaceEventQueue") sharedInstance] executeOrAppendEvent:event]; 372 | } 373 | } 374 | 375 | %group ForceDisableAnimatedRestore 376 | 377 | %hook SBUIController 378 | 379 | - (void)restoreIconListAnimated:(BOOL)animated animateWallpaper:(BOOL)wallpaper keepSwitcher:(BOOL)switcher { 380 | if (disableUnscatterFlag) { 381 | disableUnscatterFlag = NO; 382 | animated = NO; 383 | } 384 | 385 | %orig(animated, wallpaper, switcher); 386 | } 387 | 388 | %end 389 | 390 | %end 391 | 392 | %ctor { 393 | %init(ForceDisableAnimatedRestore); 394 | } 395 | 396 | CGFloat ZephyrAppSwitcherHeight() { 397 | CGFloat bottomBarHeight = [[objc_getClass("SBAppSwitcherController") sharedInstance] bottomBarHeight]; 398 | return bottomBarHeight; 399 | } 400 | 401 | 402 | -------------------------------------------------------------------------------- /GestureEnabler.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | 28 | // The purpose of this file is to re-enable the iPad system gestures on the iPhone, 29 | // as well as the methods that implement them, so other parts of Zephyr can use that 30 | // code. To do so, it needs to hook a C function that determines if gesture recognition 31 | // is allowed. Unfortunately, there is no way to get a reference to that function. 32 | // Instead, we hook the places that function is called, and then call it again in 33 | // a context we control in order to force the gestures enabled, if just disabled. 34 | 35 | static NSInteger disableNextCheck = 0; 36 | static NSInteger fakeNextScrollingCheck = 0; 37 | 38 | static void EnsureGesturesEnabled() { 39 | disableNextCheck += 1; 40 | 41 | // This function calls through to the gesture setup function. Since we can't get the address of that function, 42 | // we have to call through to it though something we do have access to. This is a rarely-used method that works. 43 | [[objc_getClass("SBLockdownManager") sharedInstance] _developerDeviceStateChanged]; 44 | 45 | disableNextCheck -= 1; 46 | } 47 | 48 | // -[SBLockdownManager _developerDeviceStateChanged] calls CFPreferencesSynchronize, but in my testing, I've seen 49 | // that crash badly. Since we don't have other options for what to call on iOS 5, just neuter the synchronize call. 50 | static Boolean (*orig_CFPreferencesSynchronize)(CFStringRef, CFStringRef, CFStringRef); 51 | static Boolean replaced_CFPreferencesSynchronize(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName) { 52 | if (disableNextCheck > 0) { 53 | return NO; 54 | } 55 | 56 | return orig_CFPreferencesSynchronize(applicationID, userName, hostName); 57 | } 58 | 59 | %group GestureEnabler 60 | 61 | %hook UIDevice 62 | 63 | // The gesture recognition for system gestures is only setup when the device 64 | // is an iPad. When we are faking it (see above), pretend to be on an iPad. 65 | - (UIUserInterfaceIdiom)userInterfaceIdiom { 66 | if (disableNextCheck > 0) { 67 | return UIUserInterfaceIdiomPad; 68 | } 69 | 70 | return %orig; 71 | } 72 | 73 | %end 74 | 75 | %hook SBIconController 76 | 77 | // On iOS 6 and later, the home screen being scrolling blocks system gestures. 78 | // This apparently bugged people (quite a bit of support email), so force it back. 79 | - (BOOL)isScrolling { 80 | if (fakeNextScrollingCheck > 0) { 81 | fakeNextScrollingCheck -= 1; 82 | 83 | return NO; 84 | } 85 | 86 | return %orig; 87 | } 88 | 89 | %end 90 | 91 | %hook SBPlatformController 92 | 93 | // The gesture setup will only be called if we have the capability. 94 | - (BOOL)hasCapability:(NSString *)capability { 95 | if ([capability isEqual:kGSMultitaskingGesturesCapability]) { 96 | return YES; 97 | } 98 | 99 | return %orig; 100 | } 101 | 102 | %end 103 | 104 | %end 105 | 106 | // One of the common call sites for "the function" is SBSystemGesturesChangeGestureAndRecognitionState, 107 | // another C function, called from too many C functions and methods for us to hook them all. However, 108 | // there is one way to detect SBSystemGesturesChangeGestureAndRecognitionState: if logging is enabled, 109 | // it will print out a log message that we can detect. Then, we can force the gestures enabled again. 110 | 111 | static void (*orig_NSLogv)(NSString *, va_list); 112 | static void replaced_NSLogv(NSString *what, va_list args) { 113 | if ([what hasPrefix:@"SBSystemGesturesChangeGestureAndRecognitionState"]) { 114 | // Only iOS 6 checks for home screen scrolling (see above). But rather 115 | // than hardcoding a set of versions to do this on, check dynamically: 116 | if ([what rangeOfString:@"home-screen scrolling"].location != NSNotFound) { 117 | fakeNextScrollingCheck++; 118 | } 119 | 120 | // This is called in the middle of the method, but we need to force it 121 | // re-enabled after a later portion. To do that, just async the call. 122 | dispatch_async(dispatch_get_main_queue(), ^{ 123 | EnsureGesturesEnabled(); 124 | }); 125 | 126 | return; 127 | } 128 | 129 | orig_NSLogv(what, args); 130 | } 131 | 132 | static Boolean (*orig_CFPreferencesGetAppBooleanValue)(CFStringRef, CFStringRef, Boolean *); 133 | static Boolean replaced_CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat) { 134 | if ([(NSString *)key isEqual:@"SBSystemGesturesLogging"]) { 135 | // The above logging message that we depend on to get notified when 136 | // SBSystemGesturesChangeGestureAndRecognitionState is called is only 137 | // logged when this preferences key is enabled. Let's hope it doesn't 138 | // go away, since there doesn't seem to be a clear need for it in SB. 139 | return YES; 140 | } else { 141 | return orig_CFPreferencesGetAppBooleanValue(key, applicationID, keyExistsAndHasValidFormat); 142 | } 143 | } 144 | 145 | static CFPropertyListRef (*orig_CFPreferencesCopyAppValue)(CFStringRef, CFStringRef); 146 | static CFPropertyListRef replaced_CFPreferencesCopyAppValue(CFStringRef key, CFStringRef applicationID) { 147 | if ([(NSString *)key isEqual:@"SBUseSystemGestures"]) { 148 | // The iPad has this set by default, but the iPhone doesn't (and has no 149 | // way to set it in the UI), so we need to enable gestures manually here. 150 | // (But even on the iPad, we need it set for our gestures to work at all.) 151 | return [[NSNumber numberWithInt:1] copy]; 152 | } else { 153 | return orig_CFPreferencesCopyAppValue(key, applicationID); 154 | } 155 | } 156 | 157 | // Besides SBSystemGesturesChangeGestureAndRecognitionState, there are a few other 158 | // functions and methods that call the gesture setup function. We need to make sure 159 | // to reset the enabled state after they are called. (We can't just pretend to be 160 | // an iPad for the entirety of these methods, as that would cause lots of issues.) 161 | // NOTE: When new iOS versions are released, be sure to check this list in IDA! 162 | 163 | %group GestureEnablerFixes 164 | 165 | %hook SBUIController 166 | 167 | - (void)_reloadDemoAndDebuggingDefaultsAndCapabilities { // iOS 6 168 | %orig; 169 | EnsureGesturesEnabled(); 170 | } 171 | 172 | - (void)noteInterfaceOrientationChanged:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration updateMirroredDisplays:(BOOL)mirror force:(BOOL)force { 173 | %orig; 174 | EnsureGesturesEnabled(); 175 | } 176 | 177 | - (id)init { 178 | self = %orig; 179 | EnsureGesturesEnabled(); 180 | return self; 181 | } 182 | 183 | %end 184 | 185 | %hook SpringBoard 186 | 187 | - (void)userDefaultsDidChange:(id)something { // iOS 5 188 | %orig; 189 | EnsureGesturesEnabled(); 190 | } 191 | 192 | - (void)applicationDidFinishLaunching:(SpringBoard *)app { 193 | %orig; 194 | EnsureGesturesEnabled(); 195 | } 196 | 197 | %end 198 | 199 | %end 200 | 201 | %ctor { 202 | %init(GestureEnabler); 203 | %init(GestureEnablerFixes); 204 | 205 | MSHookFunction(CFPreferencesGetAppBooleanValue, replaced_CFPreferencesGetAppBooleanValue, &orig_CFPreferencesGetAppBooleanValue); 206 | MSHookFunction(CFPreferencesCopyAppValue, replaced_CFPreferencesCopyAppValue, &orig_CFPreferencesCopyAppValue); 207 | MSHookFunction(CFPreferencesSynchronize, replaced_CFPreferencesSynchronize, &orig_CFPreferencesSynchronize); 208 | MSHookFunction((void *)NSLogv, (void *)replaced_NSLogv, (void **)&orig_NSLogv); 209 | } 210 | 211 | -------------------------------------------------------------------------------- /Grabber.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "iPhonePrivate.h" 27 | 28 | @interface ZephyrGrabberController : NSObject { 29 | NSMutableDictionary *_grabberViews; 30 | NSMutableDictionary *_grabberWindows; 31 | } 32 | 33 | + (id)sharedInstance; 34 | 35 | - (BOOL)visibleAtAnyEdge; 36 | - (BOOL)visibleAtEdge:(SBOffscreenEdge)edge; 37 | - (void)showAtEdge:(SBOffscreenEdge)edge animated:(BOOL)animated; 38 | - (void)hideAtEdge:(SBOffscreenEdge)edge animated:(BOOL)animated; 39 | 40 | - (BOOL)pointInside:(CGPoint)point atEdge:(SBOffscreenEdge)edge; 41 | 42 | @end 43 | 44 | -------------------------------------------------------------------------------- /Grabber.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "Grabber.h" 28 | 29 | static CGFloat kGrabberControllerHideTemporalOffset = 2.0f; 30 | static CGFloat kGrabberControllerAnimationDuration = 0.3f; 31 | 32 | static NSNumber *SBOffscreenEdgeKey(SBOffscreenEdge edge) { 33 | return [NSNumber numberWithInt:edge]; 34 | } 35 | 36 | @implementation ZephyrGrabberController 37 | 38 | + (id)sharedInstance { 39 | static ZephyrGrabberController *controller = nil; 40 | 41 | if (controller == nil) { 42 | controller = [[ZephyrGrabberController alloc] init]; 43 | } 44 | 45 | return controller; 46 | } 47 | 48 | - (id)init { 49 | if ((self = [super init])) { 50 | _grabberViews = [[NSMutableDictionary alloc] init]; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (void)dealloc { 57 | [_grabberViews release]; 58 | 59 | [super dealloc]; 60 | } 61 | 62 | - (void)transformGrabberView:(SBBulletinListTabView *)grabberView offscreen:(BOOL)offscreen atEdge:(SBOffscreenEdge)edge { 63 | CGAffineTransform transform = CGAffineTransformIdentity; 64 | CGPoint anchorPoint = CGPointMake(0.5, 0.5); 65 | 66 | if (offscreen) { 67 | CGSize size = [grabberView bounds].size; 68 | 69 | if (edge == kSBOffscreenEdgeTop) { 70 | transform = CGAffineTransformTranslate(transform, 0, -size.height); 71 | } else if (edge == kSBOffscreenEdgeLeft) { 72 | transform = CGAffineTransformTranslate(transform, -size.width, 0); 73 | } else if (edge == kSBOffscreenEdgeBottom) { 74 | transform = CGAffineTransformTranslate(transform, 0, +size.height); 75 | } else if (edge == kSBOffscreenEdgeRight) { 76 | transform = CGAffineTransformTranslate(transform, +size.width, 0); 77 | } 78 | } 79 | 80 | if (edge == kSBOffscreenEdgeTop) { 81 | transform = CGAffineTransformRotate(transform, 0); 82 | } else if (edge == kSBOffscreenEdgeLeft) { 83 | anchorPoint = CGPointMake(0.5, 1.11); // XXX: magic 84 | transform = CGAffineTransformRotate(transform, M_PI / (2.0 / 3.0)); 85 | } else if (edge == kSBOffscreenEdgeBottom) { 86 | transform = CGAffineTransformRotate(transform, M_PI); 87 | } else if (edge == kSBOffscreenEdgeRight) { 88 | anchorPoint = CGPointMake(0.5, 1.11); // XXX: magic 89 | transform = CGAffineTransformRotate(transform, M_PI / 2.0); 90 | } 91 | 92 | [grabberView.layer setAnchorPoint:anchorPoint]; 93 | [grabberView setTransform:transform]; 94 | } 95 | 96 | - (BOOL)visibleAtAnyEdge { 97 | return [_grabberViews count] != 0; 98 | } 99 | 100 | - (BOOL)visibleAtEdge:(SBOffscreenEdge)edge { 101 | return [_grabberViews objectForKey:SBOffscreenEdgeKey(edge)] != nil; 102 | } 103 | 104 | - (void)showAtEdge:(SBOffscreenEdge)edge animated:(BOOL)animated { 105 | SBBulletinListTabView *grabberView = [[objc_getClass("SBBulletinListTabView") alloc] init]; 106 | 107 | UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 108 | [window setWindowLevel:999999.0]; 109 | [window setUserInteractionEnabled:NO]; 110 | [window setHidden:NO]; 111 | 112 | UIView *rotationView = [[UIView alloc] initWithFrame:[window bounds]]; 113 | [rotationView setUserInteractionEnabled:NO]; 114 | ZephyrRotateViewFromOrientationToOrientation(rotationView, UIInterfaceOrientationPortrait, ZephyrCurrentInterfaceOrientation(), YES); 115 | [rotationView addSubview:grabberView]; 116 | [window addSubview:rotationView]; 117 | [rotationView release]; 118 | 119 | CGRect windowFrame = [rotationView bounds]; 120 | CGRect frame = [grabberView frame]; 121 | if (edge == kSBOffscreenEdgeTop) { 122 | frame = CGRectMake(roundf((windowFrame.size.width - frame.size.width) / 2), 0, frame.size.width, frame.size.height); 123 | } else if (edge == kSBOffscreenEdgeLeft) { 124 | frame = CGRectMake(0, roundf((windowFrame.size.height - frame.size.height) / 2), frame.size.width, frame.size.height); 125 | } else if (edge == kSBOffscreenEdgeBottom) { 126 | frame = CGRectMake(roundf((windowFrame.size.width - frame.size.width) / 2), windowFrame.size.height - frame.size.height, frame.size.width, frame.size.height); 127 | } else if (edge == kSBOffscreenEdgeRight) { 128 | frame = CGRectMake(windowFrame.size.width - frame.size.width, roundf((windowFrame.size.height - frame.size.height) / 2), frame.size.width, frame.size.height); 129 | } 130 | [grabberView setFrame:frame]; 131 | 132 | [self transformGrabberView:grabberView offscreen:YES atEdge:edge]; 133 | [UIView animateWithDuration:kGrabberControllerAnimationDuration animations:^{ 134 | [self transformGrabberView:grabberView offscreen:NO atEdge:edge]; 135 | }]; 136 | 137 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, kGrabberControllerHideTemporalOffset * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ 138 | [self hideAtEdge:edge animated:animated]; 139 | }); 140 | 141 | [_grabberViews setObject:grabberView forKey:SBOffscreenEdgeKey(edge)]; 142 | [_grabberWindows setObject:window forKey:SBOffscreenEdgeKey(edge)]; 143 | } 144 | 145 | - (void)hideAtEdge:(SBOffscreenEdge)edge animated:(BOOL)animated { 146 | SBBulletinListTabView *grabberView = [_grabberViews objectForKey:SBOffscreenEdgeKey(edge)]; 147 | UIWindow *window = [_grabberWindows objectForKey:SBOffscreenEdgeKey(edge)]; 148 | 149 | if (grabberView == nil) { 150 | return; 151 | } 152 | 153 | [UIView animateWithDuration:kGrabberControllerAnimationDuration animations:^{ 154 | [self transformGrabberView:grabberView offscreen:YES atEdge:edge]; 155 | } completion:^(BOOL completed) { 156 | [grabberView removeFromSuperview]; 157 | [grabberView release]; 158 | 159 | [window setHidden:YES]; 160 | [window release]; 161 | }]; 162 | 163 | [_grabberViews removeObjectForKey:SBOffscreenEdgeKey(edge)]; 164 | [_grabberWindows removeObjectForKey:SBOffscreenEdgeKey(edge)]; 165 | } 166 | 167 | - (BOOL)pointInside:(CGPoint)point atEdge:(SBOffscreenEdge)edge { 168 | SBBulletinListTabView *grabberView = [_grabberViews objectForKey:SBOffscreenEdgeKey(edge)]; 169 | 170 | if (grabberView == nil) { 171 | return NO; 172 | } 173 | 174 | CGRect grabberFrame = [grabberView frame]; 175 | grabberFrame = CGRectInset(grabberFrame, -10.0, -10.0); 176 | 177 | if (edge == kSBOffscreenEdgeLeft || edge == kSBOffscreenEdgeRight) { 178 | return point.y > CGRectGetMinY(grabberFrame) && point.y < CGRectGetMaxY(grabberFrame); 179 | } else if (edge == kSBOffscreenEdgeTop || edge == kSBOffscreenEdgeBottom) { 180 | return point.x > CGRectGetMinX(grabberFrame) && point.x < CGRectGetMaxX(grabberFrame); 181 | } else { 182 | return NO; 183 | } 184 | } 185 | 186 | @end 187 | 188 | 189 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, Xuzz Productions, LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Lockscreen.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "BaseGesture.h" 28 | 29 | // When we pretend to be an iPad in GestureEnabler.xmi, we break the Lock Screen camera 30 | // close gesture, since it things we are on an iPad and doesn't set one up. Rather than 31 | // trying to add more hacks into that to get the built in gesture, we can make our own. 32 | @interface ZephyrCameraCloseGesture : ZephyrBaseGesture 33 | @end 34 | 35 | @implementation ZephyrCameraCloseGesture : ZephyrBaseGesture 36 | 37 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge { 38 | return [[objc_getClass("SBAwayController") sharedAwayControllerIfExists] allowDismissCameraSystemGesture]; 39 | } 40 | 41 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location { 42 | [[objc_getClass("SBAwayController") sharedAwayControllerIfExists] handleDismissCameraSystemGesture:recognizer]; 43 | } 44 | 45 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity { 46 | [[objc_getClass("SBAwayController") sharedAwayControllerIfExists] handleDismissCameraSystemGesture:recognizer]; 47 | } 48 | 49 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type { 50 | [[objc_getClass("SBAwayController") sharedAwayControllerIfExists] handleDismissCameraSystemGesture:recognizer]; 51 | } 52 | 53 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer { 54 | [[objc_getClass("SBAwayController") sharedAwayControllerIfExists] handleDismissCameraSystemGesture:recognizer]; 55 | } 56 | 57 | @end 58 | 59 | static ZephyrCameraCloseGesture *gesture = nil; 60 | 61 | %group Camera 62 | 63 | %hook SBUIController 64 | 65 | - (void)finishLaunching { 66 | %orig; 67 | 68 | // This was added in iOS 5.1. 69 | if ([objc_getClass("SBAwayController") instancesRespondToSelector:@selector(allowDismissCameraSystemGesture)]) { 70 | gesture = [[ZephyrCameraCloseGesture alloc] init]; 71 | [gesture addOffscreenEdge:kSBOffscreenEdgeTop minimumTouchCount:1 edgeMargin:30.0f]; 72 | } 73 | } 74 | 75 | %end 76 | 77 | %end 78 | 79 | %ctor { 80 | %init(Camera); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | export GO_EASY_ON_ME = 1 2 | 3 | export TARGET_CXX = xcrun -sdk iphoneos clang++ 4 | export TARGET_LD = xcrun -sdk iphoneos clang++ 5 | export TARGET = iphone:latest:5.0 6 | export ARCHS = armv7 # pending theos bug: armv7s 7 | 8 | include theos/makefiles/common.mk 9 | 10 | TWEAK_NAME = Zephyr 11 | 12 | Zephyr_FILES = Preferences.xmi Switcher.xmi SwitchApp.xmi BaseGesture.xmi OffscreenGesture.xmi NSTimer+Blocks.m ActivateSwitcher.xmi Common.xmi Notification.xmi Grabber.xmi GestureEnabler.xmi Lockscreen.xmi 13 | Zephyr_FRAMEWORKS = UIKit QuartzCore CoreGraphics CoreTelephony 14 | Zephyr_PRIVATE_FRAMEWORKS = AppSupport GraphicsServices 15 | 16 | include $(THEOS_MAKE_PATH)/tweak.mk 17 | -------------------------------------------------------------------------------- /NSTimer+Blocks.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | typedef void (^NSTimerBlock)(NSTimer *timer); 27 | 28 | @interface NSTimer (Blocks) 29 | 30 | + (NSTimer *)zephyrTimerWithTimeInterval:(NSTimeInterval)seconds block:(NSTimerBlock)block repeats:(BOOL)repeats; 31 | + (NSTimer *)zephyrScheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(NSTimerBlock)block repeats:(BOOL)repeats; 32 | 33 | @end 34 | 35 | -------------------------------------------------------------------------------- /NSTimer+Blocks.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "NSTimer+Blocks.h" 27 | 28 | @implementation NSTimer (Blocks) 29 | 30 | + (NSTimer *)zephyrTimerWithTimeInterval:(NSTimeInterval)seconds block:(NSTimerBlock)block repeats:(BOOL)repeats { 31 | NSTimerBlock heapBlock = [block copy]; 32 | NSTimer *timer = [self timerWithTimeInterval:seconds target:self selector:@selector(zephyrFireBlockTimer:) userInfo:heapBlock repeats:repeats]; 33 | [heapBlock release]; 34 | return timer; 35 | } 36 | 37 | + (NSTimer *)zephyrScheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(NSTimerBlock)block repeats:(BOOL)repeats { 38 | NSTimerBlock heapBlock = [block copy]; 39 | NSTimer *timer = [self scheduledTimerWithTimeInterval:seconds target:self selector:@selector(zephyrFireBlockTimer:) userInfo:heapBlock repeats:repeats]; 40 | [heapBlock release]; 41 | return timer; 42 | } 43 | 44 | + (void)zephyrFireBlockTimer:(NSTimer *)timer { 45 | NSTimerBlock block = (NSTimerBlock) [timer userInfo]; 46 | 47 | block(timer); 48 | } 49 | 50 | @end 51 | 52 | -------------------------------------------------------------------------------- /Notification.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "BaseGesture.h" 27 | #import "OffscreenGesture.h" 28 | 29 | 30 | -------------------------------------------------------------------------------- /Notification.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "Notification.h" 28 | #import "Preferences.h" 29 | #import "NSTimer+Blocks.h" 30 | 31 | #import "SwitchApp.h" 32 | 33 | #include 34 | 35 | %group Notification 36 | 37 | @interface SBBulletinListController (Zephyr) 38 | - (void)zephyrCleanupViews; 39 | - (void)zephyrPrepareAtLocation:(CGFloat)location; 40 | @end 41 | 42 | static BOOL ZephyrNotificationShouldActivate() { 43 | BOOL locked = [[objc_getClass("SBAwayController") sharedAwayController] isLocked]; 44 | BOOL preference = [ZephyrPreferencesGet(@"NotificationEnabled", [NSNumber numberWithBool:YES]) boolValue]; 45 | BOOL pad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad; 46 | 47 | return preference && !pad && !locked; 48 | } 49 | 50 | %hook SBBulletinListController 51 | 52 | // XXX: these should be associated objects, not statics 53 | static UIView *appView = nil; 54 | static SBBulletinListView *centerView = nil; 55 | static UIImageView *shadowView = nil; 56 | static CGFloat grabLocation = NAN; 57 | 58 | %new(v@:) 59 | - (void)zephyrCleanupViews { 60 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 61 | 62 | if (app != nil) { 63 | [app disableContextHostingForRequester:CFSTR("SwitchApp")]; 64 | } 65 | 66 | centerView = nil; 67 | 68 | [shadowView release]; 69 | [shadowView removeFromSuperview]; 70 | shadowView = nil; 71 | 72 | [appView release]; 73 | [appView removeFromSuperview]; 74 | appView = nil; 75 | } 76 | 77 | - (void)_cleanupAfterShowListView { 78 | if (!ZephyrNotificationShouldActivate()) return %orig; 79 | 80 | %orig; 81 | grabLocation = NAN; 82 | [self zephyrCleanupViews]; 83 | } 84 | 85 | - (void)_cleanupAfterHideListViewKeepingWindow:(BOOL)window { 86 | if (!ZephyrNotificationShouldActivate()) return %orig; 87 | 88 | %orig; 89 | grabLocation = NAN; 90 | [self zephyrCleanupViews]; 91 | } 92 | 93 | - (void)_cleanupAfterHideListView { 94 | if (!ZephyrNotificationShouldActivate()) return %orig; 95 | 96 | %orig; 97 | grabLocation = NAN; 98 | [self zephyrCleanupViews]; 99 | } 100 | 101 | - (void)prepareToShowListViewAnimated:(BOOL)showListViewAnimated aboveBanner:(BOOL)banner { 102 | if (!ZephyrNotificationShouldActivate()) return %orig; 103 | 104 | %orig; 105 | [self zephyrPrepareAtLocation:0.0f]; 106 | } 107 | 108 | - (void)prepareToHideListViewAnimated:(BOOL)animated { 109 | if (!ZephyrNotificationShouldActivate()) return %orig; 110 | 111 | %orig; 112 | 113 | if (!isnan(grabLocation)) { 114 | [self zephyrPrepareAtLocation:grabLocation]; 115 | } else { 116 | [self zephyrPrepareAtLocation:ZephyrHeightForOrientation(ZephyrCurrentInterfaceOrientation())]; 117 | } 118 | } 119 | 120 | %new(v@:f) 121 | - (void)zephyrPrepareAtLocation:(CGFloat)location { 122 | [self zephyrCleanupViews]; 123 | grabLocation = location; 124 | 125 | centerView = [self listView]; 126 | [centerView positionSlidingViewAtY:ZephyrHeightForOrientation(ZephyrCurrentInterfaceOrientation())]; 127 | [MSHookIvar([self listView], "_grabber") setHidden:ZephyrNotificationShouldActivate()]; 128 | 129 | appView = [[UIView alloc] initWithFrame:[centerView bounds]]; 130 | [appView setClipsToBounds:NO]; 131 | [centerView addSubview:appView]; 132 | 133 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 134 | 135 | if (app != nil) { 136 | UIView *gestureView = ZephyrViewForApplication(app); 137 | 138 | UIView *gestureWrapperView = [[UIView alloc] initWithFrame:[appView bounds]]; 139 | [gestureWrapperView addSubview:gestureView]; 140 | [appView addSubview:gestureWrapperView]; 141 | 142 | if (UIInterfaceOrientationIsLandscape(ZephyrCurrentInterfaceOrientation())) { 143 | ZephyrRotateViewFromOrientationToOrientation(gestureWrapperView, UIInterfaceOrientationPortrait, ZephyrOrientationFlip(ZephyrCurrentInterfaceOrientation()), YES); 144 | } else if (ZephyrCurrentInterfaceOrientation() == UIInterfaceOrientationPortraitUpsideDown) { 145 | ZephyrRotateViewFromOrientationToOrientation(gestureWrapperView, UIInterfaceOrientationPortrait, ZephyrCurrentInterfaceOrientation(), YES); 146 | } 147 | } else { 148 | UIView *homeView = ZephyrViewWithScreenshotOfHomeScreen(); 149 | [appView addSubview:homeView]; 150 | } 151 | 152 | shadowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SwitcherShadowTop.png"]]; 153 | CGRect shadowFrame = [shadowView frame]; 154 | shadowFrame.size.height = [[shadowView image] size].height; 155 | shadowFrame.size.width = [appView bounds].size.width; 156 | shadowFrame.origin.y = location - shadowFrame.size.height; 157 | [shadowView setFrame:shadowFrame]; 158 | [shadowView setTransform:CGAffineTransformMakeScale(1, -1)]; 159 | [centerView addSubview:shadowView]; 160 | 161 | CGRect appFrame = [appView frame]; 162 | appFrame.origin.y = location; 163 | [appView setFrame:appFrame]; 164 | } 165 | 166 | - (void)positionListViewAtY:(CGFloat)location { 167 | if (!ZephyrNotificationShouldActivate()) return %orig; 168 | 169 | grabLocation = location; 170 | 171 | if (location < 0) location = 0; 172 | 173 | CGRect wrapperFrame = [appView frame]; 174 | wrapperFrame.origin.y = location; 175 | [appView setFrame:wrapperFrame]; 176 | 177 | CGRect shadowFrame = [shadowView frame]; 178 | shadowFrame.origin.y = wrapperFrame.origin.y - shadowFrame.size.height; 179 | [shadowView setFrame:shadowFrame]; 180 | } 181 | 182 | %end 183 | 184 | %hook SBBulletinListView 185 | 186 | // we fake the location in order to show the notification UI 187 | // while we are sliding down, so re-fake back the current Y 188 | - (CGFloat)currentY { 189 | if (!ZephyrNotificationShouldActivate()) return %orig; 190 | 191 | if (!isnan(grabLocation)) { 192 | return grabLocation; 193 | } else { 194 | return %orig; 195 | } 196 | } 197 | 198 | %end 199 | 200 | // When we pretend to be an iPad in GestureEnabler.xmi, we break the Notification Center 201 | // close gesture, since it things we are on an iPad and doesn't set one up. Rather than 202 | // trying to add more hacks into that to get the built in gesture, we can make our own. 203 | @interface ZephyrNotificationCloseGesture : ZephyrBaseGesture 204 | @end 205 | 206 | @implementation ZephyrNotificationCloseGesture : ZephyrBaseGesture 207 | 208 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge { 209 | return [[objc_getClass("SBBulletinWindowController") sharedInstance] allowsHideNotificationsGesture]; 210 | } 211 | 212 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location { 213 | [[objc_getClass("SBUIController") sharedInstance] handleHideNotificationsSystemGesture:recognizer]; 214 | } 215 | 216 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity { 217 | [[objc_getClass("SBUIController") sharedInstance] handleHideNotificationsSystemGesture:recognizer]; 218 | } 219 | 220 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type { 221 | [[objc_getClass("SBUIController") sharedInstance] handleHideNotificationsSystemGesture:recognizer]; 222 | } 223 | 224 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer { 225 | [[objc_getClass("SBUIController") sharedInstance] handleHideNotificationsSystemGesture:recognizer]; 226 | } 227 | 228 | @end 229 | 230 | static ZephyrNotificationCloseGesture *gesture = nil; 231 | 232 | %hook SBUIController 233 | 234 | - (void)finishLaunching { 235 | %orig; 236 | 237 | gesture = [[ZephyrNotificationCloseGesture alloc] init]; 238 | [gesture addOffscreenEdge:kSBOffscreenEdgeBottom minimumTouchCount:1 edgeMargin:30.0f]; 239 | } 240 | 241 | // we break if the switcher is also showing, so don't let it be 242 | - (void)handleHideNotificationsSystemGesture:(id)gesture { 243 | if (!ZephyrNotificationShouldActivate()) return %orig; 244 | 245 | if ([self isSwitcherShowing]) return; 246 | 247 | %orig; 248 | } 249 | 250 | %end 251 | 252 | %end 253 | 254 | %ctor { 255 | %init(Notification); 256 | } 257 | 258 | -------------------------------------------------------------------------------- /OffscreenGesture.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "iPhonePrivate.h" 27 | 28 | extern "C" void SBGestureRecognizerRegister(SBGestureRecognizer *recognizer); 29 | extern "C" void SBGestureRecognizerUnregister(SBGestureRecognizer *recognizer); 30 | 31 | -------------------------------------------------------------------------------- /OffscreenGesture.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "OffscreenGesture.h" 27 | 28 | static NSMutableArray *allGestureRecognizers = nil; 29 | 30 | %ctor { 31 | allGestureRecognizers = [[NSMutableArray alloc] init]; 32 | } 33 | 34 | void SBGestureRecognizerRegister(SBGestureRecognizer *recognizer) { 35 | [allGestureRecognizers addObject:recognizer]; 36 | } 37 | 38 | void SBGestureRecognizerUnregister(SBGestureRecognizer *recognizer) { 39 | [allGestureRecognizers removeObject:recognizer]; 40 | } 41 | 42 | 43 | %group OffscreenSwipes 44 | 45 | static void (*BKSHIDServicesSystemGestureIsPossible)(BOOL) = NULL; 46 | static void (*BKSHIDServicesSystemGestureIsStealingEvents)(BOOL) = NULL; 47 | 48 | %hook SBHandMotionExtractor 49 | 50 | - (void)extractHandMotionForActiveTouches:(SBTouchInfo *)activeTouches count:(unsigned int)count centroid:(CGPoint)centroid { 51 | %orig; 52 | 53 | SBTouchInfo *touchBuffer = (SBTouchInfo *) malloc(sizeof(SBTouchInfo) * count); 54 | memcpy(touchBuffer, activeTouches, sizeof(SBTouchInfo) * count); 55 | 56 | dispatch_async(dispatch_get_main_queue(), ^{ 57 | for (SBGestureRecognizer *gu in allGestureRecognizers) { 58 | SBGestureContext *gestureContext = (SBGestureContext *) malloc(sizeof(SBGestureContext)); 59 | gestureContext->pixelDeltas = [self pixelDeltas]; 60 | gestureContext->averageVelocities = [self averageVelocities]; 61 | gestureContext->averageTranslation = [self averageTranslation]; 62 | gestureContext->movementVelocityInPointsPerSecond = [self movementVelocityInPointsPerSecond]; 63 | gestureContext->farthestFingerSeparation = [self farthestFingerSeparation]; 64 | gestureContext->activeTouchCount = count; 65 | memcpy(gestureContext->activeTouches, touchBuffer, sizeof(SBTouchInfo) * count); 66 | 67 | gestureContext->unk1 = 0; 68 | gestureContext->unk2 = 0; 69 | gestureContext->unk3 = 0; 70 | gestureContext->unk4 = 0; 71 | 72 | SBGestureRecognizerState state = [gu state]; 73 | 74 | BOOL shouldReceiveTouches = [gu shouldReceiveTouches]; 75 | BOOL hasTouches = (count > 0); 76 | 77 | if (state == SBGestureRecognizerStatePossible) { 78 | // Only start with enough touches for multi-touch gestures. 79 | BOOL numberOfTouchesIsValid = hasTouches; 80 | 81 | if ([gu isKindOfClass:objc_getClass("SBFluidSlideGestureRecognizer")]) { 82 | SBFluidSlideGestureRecognizer *swipeRecognizer = (SBFluidSlideGestureRecognizer *) gu; 83 | numberOfTouchesIsValid = (count >= (unsigned int) [swipeRecognizer minTouches]); 84 | } 85 | 86 | if (shouldReceiveTouches) { 87 | if (numberOfTouchesIsValid) { 88 | // If state is "ready" and there are enough touches, start. 89 | [gu touchesBegan:gestureContext]; 90 | } else { 91 | // Otherwise, reset: could still start if finger added. 92 | [gu reset]; 93 | } 94 | } else { 95 | // Don't begin if doesn't want touches. 96 | } 97 | } else if (state == SBGestureRecognizerStateBegan || state == SBGestureRecognizerStateChanged) { 98 | // Allow removing a finger after starting: don't check minTouches for moved. 99 | 100 | if (shouldReceiveTouches) { 101 | if (hasTouches) { 102 | // Continue if still valid. 103 | [gu touchesMoved:gestureContext]; 104 | } else { 105 | // End if no longer enough touches. 106 | [gu touchesEnded:gestureContext]; 107 | [gu reset]; 108 | } 109 | } else { 110 | // Kill if no longer interested in touches. 111 | [gu reset]; 112 | } 113 | } else if (state == SBGestureRecognizerStateEnded || state == SBGestureRecognizerStateCancelled) { 114 | if (hasTouches) { 115 | // Do nothing to preserve ended/cancelled state to the end of the gesture. 116 | } else { 117 | // When the gesture ends, reset. 118 | [gu reset]; 119 | } 120 | } else { 121 | // Unknown state. 122 | NSLog(@"[Zephyr] Unknown SBGestureRecognizerState %d.", state); 123 | } 124 | 125 | free(gestureContext); 126 | } 127 | 128 | free(touchBuffer); 129 | }); 130 | } 131 | 132 | %end 133 | 134 | %hook SBGestureRecognizer 135 | 136 | - (void)setState:(SBGestureRecognizerState)state { 137 | if (![allGestureRecognizers containsObject:self]) { 138 | %orig; 139 | return; 140 | } 141 | 142 | // iOS 6. On iOS 6, we need to tell BackBoard when a gesture is taking over touch events, 143 | // or it will believe the touch never ends while the gesture is running (causing all sorts 144 | // of issues). This may really belong above, but for simplicity, it's inserted here. 145 | 146 | // Transitioning to Began: mark us as stealing touches. 147 | if (state == SBGestureRecognizerStateBegan) { 148 | if (BKSHIDServicesSystemGestureIsStealingEvents != NULL) { 149 | BKSHIDServicesSystemGestureIsStealingEvents(YES); 150 | } 151 | } 152 | 153 | %orig; 154 | 155 | // Transitioning to Ended/Cancelled: mark us as not stealing touches any more. 156 | if (state == SBGestureRecognizerStateEnded || state == SBGestureRecognizerStateCancelled) { 157 | if (BKSHIDServicesSystemGestureIsStealingEvents != NULL) { 158 | BKSHIDServicesSystemGestureIsStealingEvents(NO); 159 | } 160 | } 161 | } 162 | 163 | %end 164 | 165 | %end 166 | 167 | %ctor { 168 | %init(OffscreenSwipes); 169 | 170 | void *handle = dlopen("/System/Library/PrivateFrameworks/BackBoardServices.framework/BackBoardServices", RTLD_LAZY); 171 | BKSHIDServicesSystemGestureIsPossible = (void (*)(BOOL)) dlsym(handle, "BKSHIDServicesSystemGestureIsPossible"); 172 | BKSHIDServicesSystemGestureIsStealingEvents = (void (*)(BOOL)) dlsym(handle, "BKSHIDServicesSystemGestureIsStealingEvents"); 173 | } 174 | 175 | -------------------------------------------------------------------------------- /Preferences.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #define kZephyrPreferencesBundleIdentifier @"com.chpwn.zephyr" 27 | typedef void (^ZephyrPreferencesApplyAction)(); 28 | 29 | extern "C" void ZephyrPreferencesApplyActionRegister(ZephyrPreferencesApplyAction block); 30 | extern "C" id ZephyrPreferencesGet(NSString *key, id defaultValue); 31 | extern "C" void ZephyrPreferencesSet(NSString *key, id value); 32 | extern "C" NSDictionary *ZephyrPreferences(); 33 | 34 | -------------------------------------------------------------------------------- /Preferences.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Preferences.h" 27 | #import "iPhonePrivate.h" 28 | 29 | #define kZephyrPreferencesFileName [NSString stringWithFormat:@"/var/mobile/Library/Preferences/%@.plist", kZephyrPreferencesBundleIdentifier] 30 | static NSDictionary *preferences = nil; 31 | static NSMutableSet *applyActions = nil; 32 | 33 | static void ZephyrPreferencesApply() { 34 | for (ZephyrPreferencesApplyAction action in applyActions) { 35 | action(); 36 | } 37 | } 38 | 39 | void ZephyrPreferencesApplyActionRegister(ZephyrPreferencesApplyAction block) { 40 | [applyActions addObject:[block copy]]; 41 | } 42 | 43 | static void ZephyrPreferencesLoad() { 44 | [preferences release]; 45 | preferences = [[NSDictionary dictionaryWithContentsOfFile:kZephyrPreferencesFileName] retain]; 46 | } 47 | 48 | id ZephyrPreferencesGet(NSString *key, id defaultValue) { 49 | id value = [preferences objectForKey:key]; 50 | if (value == nil) value = defaultValue; 51 | return value; 52 | } 53 | 54 | void ZephyrPreferencesSet(NSString *key, id value) { 55 | NSMutableDictionary *mutablePreferences = [preferences mutableCopy]; 56 | [mutablePreferences setObject:value forKey:key]; 57 | [mutablePreferences writeToFile:kZephyrPreferencesFileName atomically:NO]; 58 | [mutablePreferences release]; 59 | 60 | ZephyrPreferencesLoad(); 61 | ZephyrPreferencesApply(); 62 | } 63 | 64 | NSDictionary *ZephyrPreferences() { 65 | return [[preferences copy] autorelease]; 66 | } 67 | 68 | static void ZephyrPreferencesChangedCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { 69 | ZephyrPreferencesLoad(); 70 | ZephyrPreferencesApply(); 71 | } 72 | 73 | %ctor { 74 | applyActions = [[NSMutableSet alloc] init]; 75 | 76 | ZephyrPreferencesLoad(); 77 | 78 | CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, ZephyrPreferencesChangedCallback, (CFStringRef) [NSString stringWithFormat:@"%@.preferences-changed", kZephyrPreferencesBundleIdentifier], NULL, CFNotificationSuspensionBehaviorCoalesce); 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /SwitchApp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "BaseGesture.h" 27 | #import "OffscreenGesture.h" 28 | 29 | @interface ZephyrSwitchAppGesture : ZephyrBaseGesture 30 | @end 31 | 32 | -------------------------------------------------------------------------------- /SwitchApp.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "SwitchApp.h" 27 | #import "Common.h" 28 | #import "Preferences.h" 29 | 30 | @implementation ZephyrSwitchAppGesture 31 | 32 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge { 33 | SBApplication *topApplication = [SBApp _accessibilityFrontMostApplication]; 34 | 35 | if (topApplication != nil) { 36 | BOOL disableIdentifier = [(NSNumber *) ZephyrPreferencesGet([NSString stringWithFormat:@"SideDisable-%@", [topApplication displayIdentifier]], (NSNumber *) kCFBooleanFalse) boolValue]; 37 | if (disableIdentifier) return NO; 38 | } 39 | 40 | // Put this after the above check so you can quickly page through disabled apps. 41 | if (topApplication == nil) topApplication = MSHookIvar([objc_getClass("SBUIController") sharedInstance], "_pendingAppActivatedByGesture"); 42 | 43 | return topApplication != nil 44 | && [self currentOrientationIsSupported] 45 | && ![SBApp _accessibilityIsSystemGestureActive] 46 | && ![[objc_getClass("SBUIController") sharedInstance] isSwitcherShowing] 47 | && ![[objc_getClass("SBBulletinWindowController") sharedInstance] isBusy] 48 | && !ZephyrAssistantIsVisible() 49 | && ![[objc_getClass("SBBulletinListController") sharedInstanceIfExists] listViewIsActive] 50 | && [(NSNumber *) ZephyrPreferencesGet(@"SwipeSideEnabled", [NSNumber numberWithBool:YES]) boolValue]; 51 | } 52 | 53 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge atPoint:(CGPoint)point { 54 | CGFloat area = [ZephyrPreferencesGet(@"SideScreenArea", [NSNumber numberWithFloat:1.0f]) floatValue]; 55 | 56 | UIInterfaceOrientation orientation = ZephyrCurrentInterfaceOrientation(); 57 | CGFloat height = ZephyrHeightForOrientation(orientation); 58 | 59 | if (area > 0 && point.y > height * area) { 60 | return NO; 61 | } else if (area < 0 && point.y < height + height * area) { 62 | return NO; 63 | } 64 | 65 | if ([UIKeyboard isOnScreen]) { 66 | CGSize size = [UIKeyboard defaultSizeForInterfaceOrientation:orientation]; 67 | 68 | if (point.y > height - size.height) { 69 | return NO; 70 | } 71 | } 72 | 73 | return YES; 74 | } 75 | 76 | - (BOOL)shouldUseGrabberAtEdge:(SBOffscreenEdge)edge { 77 | BOOL enabled = [(NSNumber *) ZephyrPreferencesGet(@"SideGrabberEnabled", [NSNumber numberWithBool:NO]) boolValue]; 78 | 79 | if (enabled) { 80 | SBApplication *topApplication = [SBApp _accessibilityFrontMostApplication]; 81 | SBApplication *pendingApplication = MSHookIvar([objc_getClass("SBUIController") sharedInstance], "_pendingAppActivatedByGesture"); 82 | 83 | // When quickly paging between apps, don't require the grabber again until an app launches. 84 | if (topApplication == nil && pendingApplication != nil) { 85 | return NO; 86 | } else { 87 | return YES; 88 | } 89 | } else { 90 | return NO; 91 | } 92 | } 93 | 94 | - (BOOL)currentOrientationIsSupported { 95 | return YES; 96 | } 97 | 98 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location { 99 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 100 | [controller handleFluidHorizontalSystemGesture:recognizer]; 101 | } 102 | 103 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity { 104 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 105 | [controller handleFluidHorizontalSystemGesture:recognizer]; 106 | } 107 | 108 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type { 109 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 110 | [controller handleFluidHorizontalSystemGesture:recognizer]; 111 | } 112 | 113 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer { 114 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 115 | [controller handleFluidHorizontalSystemGesture:recognizer]; 116 | } 117 | 118 | @end 119 | 120 | %group SwitchApp 121 | 122 | %hook SBUIController 123 | 124 | static ZephyrSwitchAppGesture *gesture = nil; 125 | 126 | - (void)finishLaunching { 127 | %orig; 128 | 129 | NSInteger touches = [(NSNumber *) ZephyrPreferencesGet(@"SideMinimumTouchCount", ZephyrPreferencesGet(@"MinimumTouchCount", [NSNumber numberWithInt:1])) intValue]; 130 | CGFloat edgeMargin = [(NSNumber *) ZephyrPreferencesGet(@"SideSensitivityDistance", [NSNumber numberWithFloat:30.0f]) floatValue]; 131 | 132 | gesture = [[ZephyrSwitchAppGesture alloc] init]; 133 | [gesture addOffscreenEdge:kSBOffscreenEdgeRight minimumTouchCount:touches edgeMargin:edgeMargin]; 134 | [gesture addOffscreenEdge:kSBOffscreenEdgeLeft minimumTouchCount:touches edgeMargin:edgeMargin]; 135 | 136 | ZephyrPreferencesApplyActionRegister(^{ 137 | NSInteger touches = [(NSNumber *) ZephyrPreferencesGet(@"SideMinimumTouchCount", ZephyrPreferencesGet(@"MinimumTouchCount", [NSNumber numberWithInt:1])) intValue]; 138 | CGFloat edgeMargin = [(NSNumber *) ZephyrPreferencesGet(@"SideSensitivityDistance", [NSNumber numberWithFloat:30.0f]) floatValue]; 139 | 140 | for (SBOffscreenSwipeGestureRecognizer *recognizer in [gesture gestureRecognizers]) { 141 | [recognizer setMinTouches:touches]; 142 | [recognizer setEdgeMargin:edgeMargin]; 143 | } 144 | }); 145 | } 146 | 147 | static SBGestureRecognizer *activeHorizontalRecognizer = nil; 148 | 149 | // avoid conflict between native gesture and custom gestures 150 | - (void)handleFluidHorizontalSystemGesture:(SBGestureRecognizer *)recognizer { 151 | // This setting only controls the default gesture recognizer, not ours (offscreen swipe recognizers). 152 | if (![recognizer isKindOfClass:objc_getClass("SBOffscreenSwipeGestureRecognizer")]) { 153 | BOOL enabled = [ZephyrPreferencesGet(@"SystemSwitchAppEnabled", [NSNumber numberWithBool:YES]) boolValue]; 154 | if (!enabled) return; 155 | } 156 | 157 | 158 | if (recognizer == activeHorizontalRecognizer) { 159 | if ([recognizer state] == SBGestureRecognizerStateEnded || [recognizer state] == SBGestureRecognizerStateCancelled) { 160 | activeHorizontalRecognizer = nil; 161 | } 162 | 163 | %orig; 164 | } else if (activeHorizontalRecognizer == nil && [recognizer state] == SBGestureRecognizerStateBegan) { 165 | activeHorizontalRecognizer = recognizer; 166 | %orig; 167 | } 168 | } 169 | 170 | %end 171 | 172 | // The gesture, when starting, tries to rotate the screen (since they are only designed 173 | // for the iPad). Since the iPhone's home screen can't rotate, disable that part here. 174 | 175 | static NSInteger switchAppGestureBeganDisableRotation = 0; 176 | 177 | %hook SpringBoard 178 | 179 | - (void)noteInterfaceOrientationChanged:(UIInterfaceOrientation)orientation { 180 | if (switchAppGestureBeganDisableRotation) return; 181 | 182 | %orig; 183 | } 184 | 185 | %end 186 | 187 | %hook UIDevice 188 | 189 | - (void)setOrientation:(UIDeviceOrientation)orientation animated:(BOOL)animated { 190 | if (switchAppGestureBeganDisableRotation) return; 191 | 192 | %orig; 193 | } 194 | 195 | %end 196 | 197 | %hook SBUIController 198 | 199 | - (void)_switchAppGestureBegan { 200 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 201 | switchAppGestureBeganDisableRotation += 1; 202 | } 203 | 204 | %orig; 205 | 206 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 207 | switchAppGestureBeganDisableRotation -= 1; 208 | } 209 | } 210 | 211 | - (void)_switchAppGestureBegan:(CGFloat)location { 212 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 213 | switchAppGestureBeganDisableRotation += 1; 214 | } 215 | 216 | %orig; 217 | 218 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 219 | switchAppGestureBeganDisableRotation -= 1; 220 | } 221 | } 222 | 223 | %end 224 | 225 | %hook SBSwitchAppGestureView 226 | 227 | - (id)initWithInterfaceOrientation:(UIInterfaceOrientation)orientation startingApp:(SBApplication *)startingApp leftwardApp:(SBApplication *)leftApp rightwardApp:(SBApplication *)rightApp { 228 | if ((self = %orig) != nil) { 229 | // iOS 5.0 doesn't set this by default, so make sure to ourselves. 230 | [self setContentScaleFactor:[[UIScreen mainScreen] scale]]; 231 | 232 | return self; 233 | } 234 | 235 | return self; 236 | } 237 | 238 | // Rotation support. By default, we can't rotate the SBSwitchAppGestureView, and 239 | // since we no longer replace the _switchAppGestureBegan method, we can't add a 240 | // different container view there. Instead, just hack that support in here instead. 241 | 242 | - (void)willMoveToSuperview:(UIView *)newSuperview { 243 | if (newSuperview == nil) { 244 | UIView *superview = [self superview]; 245 | [superview removeFromSuperview]; 246 | } 247 | } 248 | 249 | - (void)didMoveToSuperview { 250 | UIView *superview = [self superview]; 251 | UIView *contentView = MSHookIvar([objc_getClass("SBUIController") sharedInstance], "_contentView"); 252 | 253 | if (superview == contentView) { 254 | UIView *containerView = [[UIView alloc] initWithFrame:[contentView bounds]]; 255 | ZephyrRotateViewFromOrientationToOrientation(containerView, ZephyrHomeInterfaceOrientation(), ZephyrCurrentInterfaceOrientation(), YES); 256 | 257 | [containerView addSubview:self]; 258 | [superview addSubview:containerView]; 259 | [containerView release]; 260 | } 261 | } 262 | 263 | %end 264 | 265 | %end 266 | 267 | %ctor { 268 | %init(SwitchApp); 269 | } 270 | 271 | -------------------------------------------------------------------------------- /Switcher.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "BaseGesture.h" 27 | #import "OffscreenGesture.h" 28 | 29 | typedef enum { 30 | kSwipeUpActionDisabled, 31 | kSwipeUpActionCloseApp, 32 | kSwipeUpActionSwitcher 33 | } SwipeUpAction; 34 | 35 | typedef enum { 36 | kSwipeUpKeyboardActionEnabled, 37 | kSwipeUpKeyboardActionDisabled, 38 | kSwipeUpKeyboardActionGrabber 39 | } SwipeUpKeyboardAction; 40 | 41 | @interface ZephyrSwitcherGesture : ZephyrBaseGesture { 42 | UIView *wrapperView; 43 | 44 | UIView *appView; 45 | UIView *switcherView; 46 | UIImageView *shadowView; 47 | 48 | NSMutableSet *pendingSwitcherActivations; 49 | } 50 | 51 | @end 52 | 53 | -------------------------------------------------------------------------------- /Switcher.xmi: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import "Common.h" 27 | #import "Switcher.h" 28 | #import "ActivateSwitcher.h" 29 | #import "Preferences.h" 30 | #import "NSTimer+Blocks.h" 31 | 32 | #import "SwitchApp.h" 33 | 34 | #include 35 | 36 | static SwipeUpKeyboardAction KeyboardAction() { 37 | SwipeUpKeyboardAction keyboardAction = (SwipeUpKeyboardAction) [(NSNumber *) ZephyrPreferencesGet(@"BottomDisableKeyboard", [NSNumber numberWithInt:kSwipeUpKeyboardActionDisabled]) intValue]; 38 | return keyboardAction; 39 | } 40 | 41 | static BOOL UseGrabber() { 42 | return [(NSNumber *) ZephyrPreferencesGet(@"BottomGrabberEnabled", [NSNumber numberWithBool:NO]) boolValue]; 43 | } 44 | 45 | @implementation ZephyrSwitcherGesture 46 | 47 | - (BOOL)shouldActivateAtEdge:(SBOffscreenEdge)edge { 48 | SBApplication *topApplication = [SBApp _accessibilityFrontMostApplication]; 49 | if (topApplication == nil) topApplication = MSHookIvar([objc_getClass("SBUIController") sharedInstance], "_pendingAppActivatedByGesture"); 50 | 51 | if (KeyboardAction() == kSwipeUpKeyboardActionDisabled && [UIKeyboard isOnScreen]) return NO; 52 | 53 | BOOL displayFlagsAllowed = YES; 54 | 55 | // Ensure topApplication is properly launched and ready. 56 | if (topApplication != nil) { 57 | if ([topApplication displayFlag:0x3]) displayFlagsAllowed = NO; 58 | if (![topApplication displayFlag:0x1]) displayFlagsAllowed = NO; 59 | } 60 | 61 | NSString *topIdentifier = [topApplication displayIdentifier]; 62 | BOOL disableIdentifier = [(NSNumber *) ZephyrPreferencesGet([NSString stringWithFormat:@"BottomDisable-%@", topIdentifier], (id)kCFBooleanFalse) boolValue]; 63 | if (disableIdentifier) return NO; 64 | 65 | SwipeUpAction action = (SwipeUpAction) [(NSNumber *) ZephyrPreferencesGet(@"SwipeUpAction", [NSNumber numberWithInt:kSwipeUpActionCloseApp]) intValue]; 66 | 67 | // This is needed to prevent re-activation before the animation completes, crashing. 68 | BOOL currentlyPendingSwitcherActivationsExist = (pendingSwitcherActivations != nil); 69 | 70 | return YES 71 | // Our configuration allows this. 72 | && (action == kSwipeUpActionSwitcher || action == kSwipeUpActionCloseApp) 73 | && [self currentOrientationIsSupported] 74 | // No other gesture is in the way (including our switch app). 75 | && ![SBApp _accessibilityIsSystemGestureActive] 76 | && !currentlyPendingSwitcherActivationsExist 77 | // We aren't in some disallowed state. 78 | && !ZephyrAssistantIsVisible() 79 | && (topApplication == nil || displayFlagsAllowed) 80 | && ![[objc_getClass("SBIconController") sharedInstance] isEditing] 81 | && ![[objc_getClass("SBAwayController") sharedAwayController] isLocked] 82 | && ![[objc_getClass("SBUIController") sharedInstance] isSwitcherShowing] 83 | && ![[objc_getClass("SBBulletinWindowController") sharedInstance] isBusy] 84 | && ![[objc_getClass("SBBulletinListController") sharedInstanceIfExists] listViewIsActive]; 85 | } 86 | 87 | - (BOOL)shouldUseGrabberAtEdge:(SBOffscreenEdge)edge { 88 | BOOL grabberAlways = (UseGrabber() && [SBApp _accessibilityFrontMostApplication] != nil); 89 | BOOL grabberKeyboard = (KeyboardAction() == kSwipeUpKeyboardActionGrabber && [UIKeyboard isOnScreen]); 90 | 91 | return grabberAlways || grabberKeyboard; 92 | } 93 | 94 | - (BOOL)currentOrientationIsSupported { 95 | return YES; 96 | } 97 | 98 | - (SwipeUpAction)effectiveSwipeUpAction { 99 | SwipeUpAction action = (SwipeUpAction) [(NSNumber *) ZephyrPreferencesGet(@"SwipeUpAction", [NSNumber numberWithInt:kSwipeUpActionCloseApp]) intValue]; 100 | SBApplication *topApplication = [SBApp _accessibilityFrontMostApplication]; 101 | 102 | if (action == kSwipeUpActionCloseApp && topApplication == nil) { 103 | action = kSwipeUpActionSwitcher; 104 | } 105 | 106 | return action; 107 | } 108 | 109 | - (void)cleanupViews { 110 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 111 | 112 | if (app != nil) { 113 | [app disableContextHostingForRequester:(CFStringRef) @"SwitchApp"]; 114 | } else { 115 | [SBApp showSpringBoardStatusBar]; 116 | } 117 | 118 | 119 | [switcherView removeFromSuperview]; 120 | [switcherView release]; 121 | switcherView = nil; 122 | 123 | [shadowView removeFromSuperview]; 124 | [shadowView release]; 125 | shadowView = nil; 126 | 127 | [appView removeFromSuperview]; 128 | [appView release]; 129 | appView = nil; 130 | 131 | [wrapperView removeFromSuperview]; 132 | [wrapperView release]; 133 | wrapperView = nil; 134 | 135 | 136 | for (NSArray *locationAndTimer in pendingSwitcherActivations) { 137 | NSTimer *timer = [locationAndTimer objectAtIndex:1]; 138 | [timer invalidate]; 139 | } 140 | 141 | [pendingSwitcherActivations release]; 142 | pendingSwitcherActivations = nil; 143 | 144 | 145 | [[objc_getClass("SBOrientationLockManager") sharedInstance] setLockOverrideEnabled:NO forReason:@"ZephyrSwitcher"]; 146 | if ([SBApp _accessibilityFrontMostApplication] != nil) [[[objc_getClass("SBUIController") sharedInstance] rootView] setAlpha:0.0f]; 147 | } 148 | 149 | - (void)handleGestureCanceled:(SBGestureRecognizer *)recognizer { 150 | // XXX: implement this correctly 151 | } 152 | 153 | - (void)positionAtLocation:(CGFloat)location { 154 | CGRect appFrame = [appView frame]; 155 | CGRect shadowFrame = [shadowView frame]; 156 | appFrame.origin.y = location; 157 | shadowFrame.origin.y = appFrame.size.height + location; 158 | [appView setFrame:appFrame]; 159 | [shadowView setFrame:shadowFrame]; 160 | } 161 | 162 | - (void)handleGestureBegan:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location { 163 | pendingSwitcherActivations = [[NSMutableSet alloc] init]; 164 | [[objc_getClass("SBOrientationLockManager") sharedInstance] setLockOverrideEnabled:YES forReason:@"ZephyrSwitcher"]; 165 | if ([SBApp _accessibilityFrontMostApplication] != nil) [[[objc_getClass("SBUIController") sharedInstance] rootView] setAlpha:1.0f]; 166 | 167 | if (ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 168 | UIInterfaceOrientation currentOrientation = ZephyrCurrentInterfaceOrientation(); 169 | [[UIDevice currentDevice] setOrientation:currentOrientation animated:NO]; 170 | [SBApp noteInterfaceOrientationChanged:currentOrientation]; 171 | } 172 | 173 | location = ZephyrHeightFromScreenPercentage(location, ZephyrCurrentInterfaceOrientation()); 174 | 175 | UIView *rootView = [[objc_getClass("SBUIController") sharedInstance] contentView]; 176 | wrapperView = [[UIView alloc] initWithFrame:[rootView bounds]]; 177 | ZephyrRotateViewFromOrientationToOrientation(wrapperView, ZephyrHomeInterfaceOrientation(), ZephyrCurrentInterfaceOrientation(), YES); 178 | [rootView addSubview:wrapperView]; 179 | 180 | appView = [[UIView alloc] initWithFrame:[wrapperView bounds]]; 181 | [appView setClipsToBounds:NO]; 182 | [wrapperView addSubview:appView]; 183 | 184 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 185 | 186 | if (app != nil) { 187 | UIView *gestureView = ZephyrViewForApplication(app); 188 | 189 | UIView *gestureWrapperView = [[UIView alloc] initWithFrame:[appView bounds]]; 190 | [gestureWrapperView addSubview:gestureView]; 191 | [appView addSubview:gestureWrapperView]; 192 | 193 | if (UIInterfaceOrientationIsLandscape(ZephyrCurrentInterfaceOrientation())) { 194 | ZephyrRotateViewFromOrientationToOrientation(gestureWrapperView, UIInterfaceOrientationPortrait, ZephyrOrientationFlip(ZephyrCurrentInterfaceOrientation()), YES); 195 | } else if (ZephyrCurrentInterfaceOrientation() == UIInterfaceOrientationPortraitUpsideDown) { 196 | ZephyrRotateViewFromOrientationToOrientation(gestureWrapperView, UIInterfaceOrientationPortrait, ZephyrCurrentInterfaceOrientation(), YES); 197 | } 198 | } else { 199 | UIView *homeView = ZephyrViewWithScreenshotOfHomeScreen(); 200 | [appView addSubview:homeView]; 201 | 202 | [SBApp hideSpringBoardStatusBar]; 203 | 204 | // Call private UIKit API to cancel touches on the icon lists while swiping. 205 | // This fixes the bug where swiping up on an icon would start editing mode. 206 | if ([SBApp respondsToSelector:@selector(_cancelAllTouches)]) 207 | [SBApp performSelector:@selector(_cancelAllTouches)]; 208 | } 209 | 210 | shadowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SwitcherShadowTop.png"]]; 211 | CGRect shadowFrame = [shadowView frame]; 212 | shadowFrame.size = [[shadowView image] size]; 213 | shadowFrame.origin.y = [appView bounds].size.height; 214 | shadowFrame.size.width = [appView bounds].size.width; 215 | [shadowView setFrame:shadowFrame]; 216 | [wrapperView addSubview:shadowView]; 217 | 218 | [[objc_getClass("SBAppSwitcherController") sharedInstance] setupForApp:app orientation:ZephyrCurrentInterfaceOrientation()]; 219 | switcherView = [[objc_getClass("SBAppSwitcherController") sharedInstance] view]; 220 | CGFloat bottomBarHeight = ZephyrAppSwitcherHeight(); 221 | CGRect switcherFrame = [switcherView frame]; 222 | switcherFrame.origin.y = [appView bounds].size.height - bottomBarHeight; 223 | [switcherView setFrame:switcherFrame]; 224 | [switcherView retain]; 225 | 226 | if ([self effectiveSwipeUpAction] == kSwipeUpActionCloseApp) { 227 | [[objc_getClass("SBUIController") sharedInstance] restoreIconListAnimated:NO animateWallpaper:NO keepSwitcher:NO]; 228 | [[objc_getClass("SBUIController") sharedInstance] notifyAppResignActive:app]; 229 | } else if ([self effectiveSwipeUpAction] == kSwipeUpActionSwitcher) { 230 | [wrapperView addSubview:switcherView]; 231 | [wrapperView sendSubviewToBack:switcherView]; 232 | } 233 | 234 | [UIView animateWithDuration:0.08f animations:^{ 235 | CGFloat loc = ZephyrHeightFromScreenPercentage(location, ZephyrCurrentInterfaceOrientation()); 236 | [self positionAtLocation:loc]; 237 | }]; 238 | } 239 | 240 | - (void)handleGestureChanged:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity { 241 | location = [self screenLocationFromLocation:location]; 242 | 243 | [self positionAtLocation:location]; 244 | 245 | // If we are closing the app, support switcher activation. 246 | CGFloat delay = [ZephyrPreferencesGet(@"BottomSwitcherActivationDelay", [NSNumber numberWithFloat:0.8f]) floatValue]; 247 | if ([self effectiveSwipeUpAction] == kSwipeUpActionCloseApp && delay != 0.0) { 248 | [self cancelTimersForMovementToLocation:location]; 249 | 250 | // No need to clear this from the timer list when done because the pendingSwitcherActiavtions is cleared when it fires. 251 | [pendingSwitcherActivations addObject:[NSArray arrayWithObjects:[NSNumber numberWithFloat:location], [NSTimer zephyrScheduledTimerWithTimeInterval:delay block:^(NSTimer *timer) { 252 | if (![self isActive]) return; // prevent double activations 253 | 254 | [self animateSwitcherFromDismissGesture]; 255 | [self cancelGesture]; 256 | } repeats:NO], nil]]; 257 | } 258 | } 259 | 260 | - (void)resetAfterCancelDismissGesture { 261 | SBApplication *app = [SBApp _accessibilityFrontMostApplication]; 262 | SBUIController *controller = [objc_getClass("SBUIController") sharedInstance]; 263 | [controller notifyAppResumeActive:app]; 264 | [controller stopRestoringIconList]; 265 | [controller tearDownIconListAndBar]; 266 | } 267 | 268 | - (void)handleGestureEnded:(SBGestureRecognizer *)recognizer withLocation:(CGFloat)location velocity:(CGPoint)velocity completionType:(int)type { 269 | location = [self screenLocationFromLocation:location]; 270 | 271 | if ([self effectiveSwipeUpAction] == kSwipeUpActionSwitcher) { 272 | if (velocity.y < 0) { 273 | [self animateSwitcherFromSwitcherGestureLocation:location]; 274 | } else { 275 | [self animateResetWithCompletion:NULL]; 276 | } 277 | } else { 278 | if (type == 1) { 279 | [self animateDismissFromDismissGestureLocation:location]; 280 | } else { 281 | [self animateResetWithCompletion:^{ 282 | [self resetAfterCancelDismissGesture]; 283 | }]; 284 | } 285 | } 286 | } 287 | 288 | - (void)cancelTimersForMovementToLocation:(CGFloat)location { 289 | // Remove pending switcher activations outside range. 290 | NSMutableSet *pendingSwitcherActivationsToRemove = [NSMutableSet set]; 291 | 292 | for (NSArray *locationAndTimer in pendingSwitcherActivations) { 293 | CGFloat originalLocation = [[locationAndTimer objectAtIndex:0] floatValue]; 294 | NSTimer *timer = [locationAndTimer objectAtIndex:1]; 295 | 296 | CGFloat allowableMovementDistance = 5.0f; 297 | if (fabsf(originalLocation - location) > allowableMovementDistance) { 298 | [timer invalidate]; 299 | [pendingSwitcherActivationsToRemove addObject:locationAndTimer]; 300 | } 301 | } 302 | 303 | [pendingSwitcherActivations minusSet:pendingSwitcherActivationsToRemove]; 304 | } 305 | 306 | - (CGFloat)screenLocationFromLocation:(CGFloat)location { 307 | location = ZephyrHeightFromScreenPercentage(location, ZephyrCurrentInterfaceOrientation()); 308 | 309 | // constrain to not going way off the screen 310 | if (location > 20.0f) location = 20.0f; 311 | 312 | // constrain to top of switcher 313 | if ([self effectiveSwipeUpAction] == kSwipeUpActionSwitcher) { 314 | CGFloat bottomBarHeight = ZephyrAppSwitcherHeight(); 315 | 316 | if (-location > bottomBarHeight) { 317 | location = ZephyrScreenRounded((location + bottomBarHeight) * 0.2f - bottomBarHeight); 318 | } 319 | } 320 | 321 | return location; 322 | } 323 | 324 | - (void)animateResetWithCompletion:(void (^)())completion { 325 | [UIView animateWithDuration:0.3f animations:^{ 326 | [self positionAtLocation:0]; 327 | } completion:^(BOOL completed) { 328 | [self cleanupViews]; 329 | 330 | if (completion != NULL) { 331 | completion(); 332 | } 333 | }]; 334 | } 335 | 336 | - (void)animateDismissFromDismissGestureLocation:(CGFloat)location { 337 | [SBApp showSpringBoardStatusBar]; 338 | 339 | UIViewAnimationOptions options = (UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState); 340 | [UIView animateWithDuration:0.3f delay:0.0f options:options animations:^{ 341 | CGFloat completedLocation = -[wrapperView bounds].size.height; 342 | [self positionAtLocation:completedLocation]; 343 | } completion:^(BOOL completed) { 344 | [self cleanupViews]; 345 | 346 | // In this case, we definitely do not want to hide this. 347 | [[[objc_getClass("SBUIController") sharedInstance] rootView] setAlpha:1.0f]; 348 | 349 | ZephyrDeactivateFrontmostApplication(); 350 | }]; 351 | } 352 | 353 | - (void)animateSwitcherFromSwitcherGestureLocation:(CGFloat)location { 354 | CGFloat bottomBarHeight = ZephyrAppSwitcherHeight(); 355 | 356 | CGFloat from = -location; 357 | CGFloat to = bottomBarHeight; 358 | CGFloat duration = 0.3f; 359 | 360 | if ([[objc_getClass("SBIconController") sharedInstance] openFolder] != nil) { 361 | [UIView animateWithDuration:duration animations:^{ 362 | [self positionAtLocation:-to]; 363 | } completion:^(BOOL completed) { 364 | [self cleanupViews]; 365 | 366 | ZephyrActivateSwitcherFromToDuration(to, to, duration); 367 | }]; 368 | } else { 369 | dispatch_async(dispatch_get_main_queue(), ^{ 370 | [self cleanupViews]; 371 | 372 | ZephyrActivateSwitcherFromToDuration(from, to, duration); 373 | }); 374 | } 375 | } 376 | 377 | - (void)animateSwitcherFromDismissGesture { 378 | CGFloat bottomBarHeight = ZephyrAppSwitcherHeight(); 379 | 380 | UIView *homeView = ZephyrViewWithScreenshotOfHomeScreen(); 381 | [homeView setFrame:[wrapperView bounds]]; 382 | 383 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 384 | // Disable rotation on the iPad as the view behind is already rotated, so rotating it again is unnecessary. 385 | if (UIInterfaceOrientationIsLandscape(ZephyrCurrentInterfaceOrientation())) { 386 | ZephyrRotateViewFromOrientationToOrientation(homeView, UIInterfaceOrientationPortrait, ZephyrOrientationFlip(ZephyrCurrentInterfaceOrientation()), YES); 387 | } else if (ZephyrCurrentInterfaceOrientation() == UIInterfaceOrientationPortraitUpsideDown) { 388 | ZephyrRotateViewFromOrientationToOrientation(homeView, UIInterfaceOrientationPortrait, ZephyrCurrentInterfaceOrientation(), YES); 389 | } 390 | } 391 | 392 | // XXX: this is a horrible hack 393 | UIImageView *switcherShadowView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SwitcherShadowTop.png"]]; 394 | CGRect shadowFrame = [switcherShadowView frame]; 395 | shadowFrame.size = [[switcherShadowView image] size]; 396 | shadowFrame.size.width = [wrapperView bounds].size.width; 397 | shadowFrame.origin.y = [wrapperView bounds].size.height; 398 | [switcherShadowView setFrame:shadowFrame]; 399 | [switcherShadowView autorelease]; 400 | 401 | CGRect switcherFrame = [switcherView frame]; 402 | switcherFrame.origin.y = [wrapperView bounds].size.height - bottomBarHeight; 403 | [switcherView setFrame:switcherFrame]; 404 | 405 | [wrapperView addSubview:switcherView]; 406 | [wrapperView addSubview:switcherShadowView]; 407 | [wrapperView addSubview:homeView]; 408 | [wrapperView addSubview:shadowView]; 409 | [wrapperView addSubview:appView]; 410 | 411 | [UIView animateWithDuration:0.4f animations:^{ 412 | [self positionAtLocation:-bottomBarHeight]; 413 | 414 | [shadowView setAlpha:0.0f]; 415 | 416 | CGRect switcherShadowFrame = [switcherShadowView frame]; 417 | CGRect homeFrame = [homeView frame]; 418 | switcherShadowFrame.origin.y = [wrapperView bounds].size.height + -bottomBarHeight; 419 | homeFrame.origin.y = -bottomBarHeight; 420 | [switcherShadowView setFrame:switcherShadowFrame]; 421 | [homeView setFrame:homeFrame]; 422 | } completion:^(BOOL finished) { 423 | [self cleanupViews]; 424 | 425 | [self resetAfterCancelDismissGesture]; 426 | 427 | [homeView removeFromSuperview]; 428 | [switcherShadowView removeFromSuperview]; 429 | 430 | ZephyrActivateSwitcherFromToDuration(bottomBarHeight, bottomBarHeight, 0.0f); 431 | }]; 432 | } 433 | 434 | @end 435 | 436 | %group Switcher 437 | 438 | %hook SBAppSwitcherBarView 439 | 440 | - (id)initWithFrame:(CGRect)frame { 441 | if ((self = %orig)) { 442 | // this stupidiy is necessary because Apple limits SBLinenView to 320px wide on iPhone 443 | SBLinenView *linen = nil; 444 | 445 | CGFloat height = 320.0f; 446 | CGFloat width = ZephyrWidthForOrientation(UIInterfaceOrientationPortrait); 447 | 448 | UIView *backgroundView = MSHookIvar(self, "_backgroundView"); 449 | ZephyrRotateViewFromOrientationToOrientation(backgroundView, UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, YES); 450 | 451 | linen = [[objc_getClass("SBLinenView") alloc] initWithFrame:CGRectMake(0, [self bounds].size.height - height, width, height)]; 452 | [linen setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin]; 453 | ZephyrRotateViewFromOrientationToOrientation(linen, UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, YES); 454 | [self addSubview:linen]; 455 | [self sendSubviewToBack:linen]; 456 | [linen release]; 457 | 458 | linen = [[objc_getClass("SBLinenView") alloc] initWithFrame:CGRectMake(ZephyrScreenRounded(([self bounds].size.width - width) / 2.0f), [self bounds].size.height - height, width, height)]; 459 | [linen setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin]; 460 | ZephyrRotateViewFromOrientationToOrientation(linen, UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, YES); 461 | [self addSubview:linen]; 462 | [self sendSubviewToBack:linen]; 463 | [linen release]; 464 | 465 | linen = [[objc_getClass("SBLinenView") alloc] initWithFrame:CGRectMake([self bounds].size.width - width, [self bounds].size.height - height, width, height)]; 466 | [linen setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin]; 467 | ZephyrRotateViewFromOrientationToOrientation(linen, UIInterfaceOrientationPortrait, UIInterfaceOrientationPortraitUpsideDown, YES); 468 | [self addSubview:linen]; 469 | [self sendSubviewToBack:linen]; 470 | [linen release]; 471 | } 472 | 473 | return self; 474 | } 475 | 476 | %end 477 | 478 | %hook SBUIController 479 | 480 | static ZephyrSwitcherGesture *gesture = nil; 481 | 482 | - (void)finishLaunching { 483 | %orig; 484 | 485 | NSInteger touches = [(NSNumber *) ZephyrPreferencesGet(@"BottomMinimumTouchCount", ZephyrPreferencesGet(@"MinimumTouchCount", [NSNumber numberWithInt:1])) intValue]; 486 | CGFloat edgeMargin = [(NSNumber *) ZephyrPreferencesGet(@"BottomSensitivityDistance", [NSNumber numberWithFloat:30.0f]) floatValue]; 487 | 488 | gesture = [[ZephyrSwitcherGesture alloc] init]; 489 | [gesture addOffscreenEdge:kSBOffscreenEdgeBottom minimumTouchCount:touches edgeMargin:edgeMargin]; 490 | 491 | ZephyrPreferencesApplyActionRegister(^{ 492 | NSInteger touches = [(NSNumber *) ZephyrPreferencesGet(@"BottomMinimumTouchCount", ZephyrPreferencesGet(@"MinimumTouchCount", [NSNumber numberWithInt:1])) intValue]; 493 | CGFloat edgeMargin = [(NSNumber *) ZephyrPreferencesGet(@"BottomSensitivityDistance", [NSNumber numberWithFloat:30.0f]) floatValue]; 494 | 495 | for (SBOffscreenSwipeGestureRecognizer *recognizer in [gesture gestureRecognizers]) { 496 | [recognizer setMinTouches:touches]; 497 | [recognizer setEdgeMargin:edgeMargin]; 498 | } 499 | }); 500 | } 501 | 502 | - (void)handleFluidVerticalSystemGesture:(SBFluidSlideGestureRecognizer *)recognizer { 503 | if ([gesture isActive]) return; 504 | 505 | BOOL enabled = [ZephyrPreferencesGet(@"SystemSwitcherEnabled", [NSNumber numberWithBool:YES]) boolValue]; 506 | if (!enabled) return; 507 | 508 | %orig; 509 | } 510 | 511 | - (void)handleFluidScaleSystemGesture:(SBFluidSlideGestureRecognizer *)recognizer { 512 | if ([gesture isActive]) return; 513 | 514 | BOOL enabled = [ZephyrPreferencesGet(@"SystemSuspendEnabled", [NSNumber numberWithBool:YES]) boolValue]; 515 | if (!enabled) return; 516 | 517 | MSHookIvar(self, "_switcherVisibleWhenSuspendGestureStarted") = NO; 518 | 519 | %orig; 520 | } 521 | 522 | - (void)_suspendGestureEndedWithCompletionType:(int)completionType { 523 | if (completionType == 1) { 524 | // The spotlight keyboard will otherwise come up in landscape if we exit the app there. 525 | if (!ZephyrHomeShouldRotateToInterfaceOrientation(ZephyrCurrentInterfaceOrientation())) { 526 | UIInterfaceOrientation currentOrientation = ZephyrHomeInterfaceOrientation(); 527 | [[UIDevice currentDevice] setOrientation:currentOrientation animated:NO]; 528 | [SBApp noteInterfaceOrientationChanged:currentOrientation]; 529 | } 530 | } 531 | 532 | %orig; 533 | } 534 | 535 | %end 536 | 537 | %hook SBTouchTemplate 538 | 539 | - (id)initWithPoints:(CGPoint *)points count:(NSUInteger)count { 540 | if (count == 5 && [ZephyrPreferencesGet(@"SystemSuspendThreeFingers", [NSNumber numberWithBool:NO]) boolValue]) { 541 | count = 3; 542 | 543 | // rearrange points 544 | points[0] = points[0]; 545 | points[1] = points[2]; 546 | points[2] = points[4]; 547 | } 548 | 549 | return %orig; 550 | } 551 | 552 | %end 553 | 554 | %end 555 | 556 | %ctor { 557 | %init(Switcher); 558 | } 559 | 560 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | # Bugs 3 | * various conflict/crashes when used at the same time as LockInfo 4 | - guided access is not respected at all 5 | - fix bug with auto-canceled swipe gestures 6 | 7 | # Improvements 8 | - reorganize, clean up preferences (add context to all settings) 9 | - Smoothly fade out icons when showing switcher. 10 | - swipe down switcher w/ smooth finger tracking 11 | 12 | # Features 13 | - gesture to lock device or just go to lock screen 14 | - Swipe down to close expanded Siri. 15 | - rotate gesture to switch to landscape 16 | - remove slide to unlock, swipe up to close lockscreen 17 | - gesture for a sepcific app (on/off home screen) 18 | 19 | -------------------------------------------------------------------------------- /Zephyr.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard" ); }; } 2 | -------------------------------------------------------------------------------- /Zephyr@2x.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grp/Zephyr/ce2b93606e3427128ef549c4519c4100789eee92/Zephyr@2x.pxm -------------------------------------------------------------------------------- /ZephyrKeyboardProxy.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.UIKit" ); }; } 2 | -------------------------------------------------------------------------------- /iPhonePrivate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | 32 | %config(generator=internal); 33 | 34 | extern NSString *kGSMultitaskingGesturesCapability; 35 | 36 | @interface UIApplication (Private) 37 | - (UIWindow *)statusBarWindow; 38 | - (UIInterfaceOrientation)interfaceOrientation; 39 | - (UIInterfaceOrientation)activeInterfaceOrientation; 40 | @end 41 | 42 | @interface UIWindow (Private) 43 | - (BOOL)_shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; 44 | @end 45 | 46 | @interface UIKeyboard : UIView 47 | + (CGSize)defaultSize; 48 | + (CGSize)defaultSizeForInterfaceOrientation:(UIInterfaceOrientation)orientation; 49 | + (BOOL)isOnScreen; 50 | @end 51 | 52 | @class SBApplication; 53 | @interface SpringBoard : UIApplication 54 | - (SBApplication *)_accessibilityFrontMostApplication; 55 | - (BOOL)_accessibilityIsSystemGestureActive; 56 | - (void)_accessibilitySetSystemGesturesDisabledByAccessibility:(BOOL)disabled; 57 | 58 | - (void)applicationOpenURL:(NSURL *)url publicURLsOnly:(BOOL)publicOnly; 59 | 60 | - (void)_handleMenuButtonEvent; 61 | - (void)clearMenuButtonTimer; 62 | - (void)menuButtonDown:(GSEventRef)event; 63 | - (void)menuButtonUp:(GSEventRef)event; 64 | - (void)lockButtonDown:(GSEventRef)event; 65 | - (void)lockButtonUp:(GSEventRef)event; 66 | 67 | - (void)showSpringBoardStatusBar; 68 | - (void)hideSpringBoardStatusBar; 69 | 70 | - (UIInterfaceOrientation)_frontMostAppOrientation; 71 | - (UIInterfaceOrientation)interfaceOrientationForCurrentDeviceOrientation; 72 | - (void)noteInterfaceOrientationChanged:(UIInterfaceOrientation)orientation; 73 | @end 74 | 75 | static SpringBoard *SBApp = nil; 76 | 77 | @interface UIDevice (Private) 78 | - (void)setOrientation:(int)orientation animated:(BOOL)animated; // not sure if UIInterfaceOrientation or UIDeviceOrientation 79 | @end 80 | 81 | @interface UIStatusBar : UIView 82 | - (void)requestStyle:(UIStatusBarStyle)style animated:(BOOL)animated; 83 | @end 84 | 85 | extern CGFloat UIStatusBarHeight; 86 | 87 | @interface SBDisplay : NSObject // iOS 5 88 | - (BOOL)displayFlag:(unsigned)flag; 89 | @end 90 | 91 | @interface SBDisplayStack : NSObject // iOS 5 92 | - (void)pushDisplay:(SBDisplay *)display; 93 | - (void)popDisplay:(SBDisplay *)display; 94 | - (BOOL)containsDisplay:(SBDisplay *)display; 95 | @end 96 | 97 | @interface SBAlert : SBDisplay 98 | @end 99 | 100 | @interface SBProcess : NSObject // iOS 5 101 | - (void)killWithSignal:(int)signal; 102 | @end 103 | 104 | @interface SBApplication : SBDisplay // iOS 6: NSObject 105 | + (BOOL)multitaskingIsSupported; // iOS 5 106 | - (SBProcess *)process; // iOS 5 107 | - (int)suspensionType; // iOS 5 108 | - (NSString *)displayIdentifier; 109 | - (void)enableContextHostingForRequester:(CFStringRef)requester orderFront:(BOOL)orderFront; 110 | - (void)disableContextHostingForRequester:(CFStringRef)requester; 111 | - (void)setActivationSetting:(unsigned)setting flag:(BOOL)flag; 112 | - (void)setActivationSetting:(unsigned)setting value:(id)value; 113 | - (void)setDeactivationSetting:(unsigned)setting flag:(BOOL)flag; 114 | - (void)setDeactivationSetting:(unsigned)setting value:(id)value; 115 | - (void)setDisplaySetting:(unsigned)setting flag:(BOOL)flag; 116 | - (void)setDisplaySetting:(unsigned)setting value:(id)value; 117 | - (UIInterfaceOrientation)statusBarOrientation; 118 | - (void)clearDeactivationSettings; 119 | - (void)willAnimateActivation; // iOS 6 120 | - (void)didAnimateActivation; // iOS 6 121 | @end 122 | 123 | @interface SBApplicationController : NSObject 124 | + (id)sharedInstance; 125 | - (SBApplication *)applicationWithDisplayIdentifier:(NSString *)displayIdentifier; 126 | @end 127 | 128 | typedef enum { 129 | SBShowcaseModeHidden, 130 | SBShowcaseModeDefault, 131 | SBShowcaseModeFull 132 | } SBShowcaseMode; // iOS 6 133 | 134 | @interface SBShowcaseViewController : NSObject 135 | - (CGFloat)revealAmountForMode:(SBShowcaseMode)mode; // iOS 6 136 | @end 137 | 138 | @interface SBAppSwitcherController : SBShowcaseViewController 139 | + (id)sharedInstance; 140 | - (BOOL)printViewIsShowing; 141 | - (CGFloat)bottomBarHeight; 142 | - (void)setupForApp:(SBApplication *)app orientation:(UIInterfaceOrientation)orientation; 143 | @end 144 | 145 | @interface SBAssistantController : SBShowcaseViewController 146 | + (id)sharedInstance; 147 | + (BOOL)isAssistantVisible; // iOS 6 148 | - (BOOL)isAssistantVisible; // iOS 5 149 | @end 150 | 151 | @interface SBShowcaseContext : NSObject 152 | @property(assign, nonatomic) UIInterfaceOrientation showcaseOrientation; 153 | @end 154 | 155 | @interface SBOrientationLockManager : NSObject 156 | + (id)sharedInstance; 157 | - (void)setLockOverrideEnabled:(BOOL)enabled forReason:(id)reason; 158 | @end 159 | 160 | @interface SBBulletinListTabView : UIView 161 | @end 162 | 163 | @interface SBBulletinListView : UIView 164 | @property(readonly, assign) CGFloat currentY; 165 | @property(readonly, retain) UIImageView *linenView; 166 | @property(readonly, retain) UIView *slidingView; 167 | @property(readonly, retain) UITableView *tableView; 168 | - (CGFloat)offscreenY; 169 | - (CGFloat)onscreenY; 170 | - (void)positionSlidingViewAtY:(CGFloat)y; 171 | - (void)setBottomCornersOffscreen:(BOOL)offscreen animated:(BOOL)animated; 172 | - (void)setBottomShadowAlpha:(CGFloat)alpha; 173 | - (void)setCornerAlpha:(CGFloat)alpha; 174 | - (void)setShowsNoNotificationsLabel:(BOOL)label animated:(BOOL)animated; 175 | - (CGRect)slidingViewFrame; 176 | @end 177 | 178 | @interface SBBulletinListController : NSObject 179 | @property(readonly, retain) SBBulletinListView *listView; 180 | @property(readonly, assign) BOOL listViewIsActive; 181 | + (id)sharedInstance; 182 | + (id)sharedInstanceIfExists; 183 | - (void)_updateForTouchBeganOrMovedWithLocation:(CGPoint)location velocity:(CGPoint)velocity; 184 | - (void)_updateForTouchCanceled; 185 | - (void)_updateForTouchEndedWithVelocity:(CGPoint)velocity completion:(id)completion; 186 | 187 | - (void)handleShowNotificationsGestureBeganWithTouchLocation:(CGPoint)touchLocation; 188 | - (void)handleShowNotificationsGestureCanceled; 189 | - (void)handleShowNotificationsGestureChangedWithTouchLocation:(CGPoint)touchLocation velocity:(CGPoint)velocity; 190 | - (void)handleShowNotificationsGestureEndedWithVelocity:(CGPoint)velocity completion:(id)completion; 191 | 192 | - (void)hideListViewAnimated:(BOOL)animated; 193 | - (void)hideListViewWithInitialVelocity:(CGFloat)initialVelocity completion:(id)completion; 194 | - (void)hideListViewWithInitialVelocity:(CGFloat)initialVelocity hiddenY:(CGFloat)y extraPull:(BOOL)pull additionalValueApplier:(id)applier completion:(id)completion; 195 | 196 | - (void)prepareToHideListViewAnimated:(BOOL)hideListViewAnimated; 197 | - (void)prepareToShowListViewAnimated:(BOOL)showListViewAnimated aboveBanner:(BOOL)banner; 198 | 199 | - (void)positionListViewAtY:(CGFloat)y; 200 | 201 | - (void)showListViewAnimated:(BOOL)animated; 202 | - (void)showListViewWithInitialVelocity:(CGFloat)initialVelocity additionalValueApplier:(id)applier completion:(id)completion; 203 | - (void)showListViewWithInitialVelocity:(CGFloat)initialVelocity completion:(id)completion; 204 | 205 | - (void)_cleanupAfterShowListView; 206 | - (void)_cleanupAfterHideListView; // iOS 5 207 | - (void)_cleanupAfterHideListViewKeepingWindow:(BOOL)window; // iOS 6 208 | @end 209 | 210 | @interface SBBulletinWindowController : NSObject 211 | @property(readonly, assign, nonatomic) UIWindow *window; 212 | @property(readonly, assign, nonatomic) UIInterfaceOrientation windowOrientation; 213 | + (id)sharedInstance; 214 | - (BOOL)allowsDismissBannerGesture; 215 | - (BOOL)allowsHideNotificationsGesture; 216 | - (BOOL)allowsShowNotificationsGesture; 217 | - (BOOL)isBusy; 218 | @end 219 | 220 | @interface SBLinenView : UIView 221 | @end 222 | 223 | @interface SBSwitchAppGestureView : UIView 224 | @property(assign, nonatomic) CGFloat percentage; 225 | @property(retain, nonatomic) SBApplication *endingApp; 226 | @property(retain, nonatomic) SBApplication *leftwardApp; 227 | @property(retain, nonatomic) SBApplication *rightwardApp; 228 | @property(retain, nonatomic) SBApplication *startingApp; 229 | - (id)initWithInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation startingApp:(SBApplication *)app leftwardApp:(SBApplication *)app3 rightwardApp:(SBApplication *)app4; 230 | - (void)beginPaging; 231 | - (void)finishBackwardToStartWithCompletion:(id)completion; 232 | - (void)finishForwardToEndWithPercentage:(CGFloat)percentage completion:(id)completion; 233 | - (void)moveToApp:(SBApplication *)app; 234 | - (void)moveToApp:(SBApplication *)app animated:(BOOL)animated; 235 | - (CGFloat)percentageForApp:(SBApplication *)app; 236 | - (void)updatePaging:(CGFloat)paging; 237 | @end 238 | 239 | typedef enum { 240 | kSBGestureTypeNone, 241 | kSBGestureTypeLaunchSuspend, 242 | kSBGestureTypeUnknown1, 243 | kSBGestureTypeUnknown2, 244 | kSBGestureTypeSwitchApp 245 | } SBGestureType; 246 | 247 | @interface SBGestureViewVendor : NSObject 248 | + (id)sharedInstance; 249 | - (void)clearCacheForApp:(SBApplication *)app reason:(id)reason; 250 | - (UIView *)viewForApp:(SBApplication *)app gestureType:(SBGestureType)type includeStatusBar:(BOOL)include; 251 | - (UIView *)viewForApp:(SBApplication *)app gestureType:(SBGestureType)type includeStatusBar:(BOOL)include decodeImage:(BOOL)decode; // iOS 5.1 252 | @end 253 | 254 | @class SBGestureRecognizer; 255 | 256 | @interface SBUIController : NSObject 257 | + (id)sharedInstance; 258 | - (UIView *)rootView; 259 | - (UIWindow *)window; 260 | 261 | - (BOOL)_ignoringEvents; 262 | 263 | - (void)_noteAppDidAcitvate:(SBApplication *)app; // iOS 6 264 | - (void)_noteAppDidFailToAcitvate:(SBApplication *)app; // iOS 6 265 | 266 | - (void)_setToggleSwitcherAfterLaunchApp:(SBApplication *)app; // iOS 6 267 | - (SBApplication *)_toggleSwitcherAfterLaunchApp; // iOS 6 268 | 269 | - (BOOL)_canActivateShowcaseIgnoringTouches:(BOOL)touches; 270 | - (void)_activateSwitcher:(NSTimeInterval)duration; 271 | - (void)_dismissShowcase:(NSTimeInterval)duration unhost:(BOOL)unhost; 272 | 273 | - (void)_clearSwitchAppList; 274 | - (id)_calculateSwitchAppList; 275 | 276 | - (void)_switchAppGestureBegan; // iOS 5.0 277 | - (void)_switchAppGestureBegan:(CGFloat)location; // iOS 5.1 278 | - (void)_switchAppGestureChanged:(CGFloat)location; 279 | - (void)_switchAppGestureEndedWithCompletionType:(int)type cumulativePercentage:(CGFloat)location; 280 | - (void)_switchAppGestureCancelled; 281 | - (void)_switchAppGestureViewAnimationComplete; 282 | 283 | - (BOOL)_suspendGestureShouldFinish; 284 | - (void)_suspendGestureEndedWithCompletionType:(int)type; 285 | 286 | - (void)handleDismissBannerSystemGesture:(SBGestureRecognizer *)recognizer; 287 | - (void)handleFluidHorizontalSystemGesture:(SBGestureRecognizer *)recognizer; 288 | - (void)handleFluidVerticalSystemGesture:(SBGestureRecognizer *)recognizer; 289 | - (void)handleFluidScaleSystemGesture:(SBGestureRecognizer *)recognizer; 290 | - (void)handleHideNotificationsSystemGesture:(SBGestureRecognizer *)recognizer; 291 | 292 | - (SBShowcaseContext *)_showcaseContextForOffset:(CGFloat)offset; 293 | - (void)_toggleSwitcher; 294 | - (BOOL)_activateSwitcherFrom:(SBShowcaseContext *)from to:(SBShowcaseContext *)to duration:(NSTimeInterval)duration; // iOS 5 295 | - (BOOL)_activateSwitcher:(NSTimeInterval)duration fromSystemGesture:(BOOL)systemGesture; // iOS 6 296 | 297 | - (void)_clearGestureViewVendorCacheForAppWithDisplayIdenitifier:(NSString *)displayIdenitifier; 298 | - (void)_prefetchAdjacentAppsAndEvictRemotes:(SBApplication *)currentApplication; // iOS 5.1 299 | - (void)prefetchAdjacentAppsAndEvictRemotes:(SBApplication *)currentApplication; // iOS 6 300 | 301 | - (id)_makeSwitchAppValidList:(id)list; 302 | - (id)_makeSwitchAppFilteredList:(id)list initialApp:(SBApplication *)app; 303 | - (void)_getSwitchAppPrefetchApps:(id)apps initialApp:(SBApplication *)app outLeftwardAppIdentifier:(NSString **)leftwardIdentifier outRightwardAppIdentifier:(NSString **)rightwardIdentifier; // iOS 6 304 | 305 | - (int)_dismissSheetsAndDetermineAlertStateForMenuClickOrSystemGesture; 306 | - (void)_resumeEventsIfNecessary; 307 | - (void)_lockOrientationForSystemGesture; 308 | - (void)_releaseSystemGestureOrientationLock; 309 | - (void)_releaseSystemGestureOrientationLock; 310 | 311 | - (void)_clearAllInstalledSystemGestureViews; 312 | - (void)_installSwitchAppGesturePlaceholderViewForEndingApp:(SBApplication *)endingApp; 313 | 314 | - (BOOL)window:(UIWindow *)window shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation; 315 | 316 | - (void)createFakeSpringBoardStatusBar; 317 | - (void)setFakeSpringBoardStatusBarVisible:(BOOL)visible; 318 | - (void)clearFakeSpringBoardStatusBarAndCorners; 319 | 320 | - (void)setRootViewHiddenForScatter:(BOOL)scatter duration:(NSTimeInterval)duration startTime:(NSTimeInterval)time; // iOS 5 321 | - (void)setRootViewHiddenForScatter:(BOOL)scatter duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay; // iOS 6 322 | 323 | - (void)restoreIconListAnimated:(BOOL)animated animateWallpaper:(BOOL)animateWallpaper keepSwitcher:(BOOL)keepSwitcher; 324 | - (void)stopRestoringIconList; 325 | - (void)tearDownIconListAndBar; 326 | 327 | - (BOOL)isSwitcherShowing; 328 | 329 | - (void)clearZoomLayer; // iOS 5 330 | 331 | - (void)prepareSwitchAppGestureStatusBar; 332 | - (void)updateSwitchAppGestureStatusBar; 333 | - (void)cleanupSwitchAppGestureStatusBar; 334 | 335 | - (void)cleanupSwitchAppGestureViews; 336 | - (void)cleanupRunningGestureIfNeeded; 337 | 338 | - (void)notifyAppResignActive:(SBApplication *)application; 339 | - (void)notifyAppResumeActive:(SBApplication *)application; 340 | 341 | - (void)showSystemGestureBackdrop; 342 | - (void)hideSystemGestureBackdrop; 343 | 344 | - (void)programmaticSwitchAppGestureApplyWithPercentage:(CGFloat)percentage; 345 | - (void)programmaticSwitchAppGestureMoveToLeft; 346 | - (void)programmaticSwitchAppGestureMoveToRight; 347 | 348 | - (void)scheduleApplicationForLaunchByGesture:(SBApplication *)application; 349 | - (void)clearPendingAppActivatedByGesture; 350 | @end 351 | 352 | @interface SBFolder : NSObject 353 | @end 354 | 355 | @interface SBIconController : NSObject 356 | + (id)sharedInstance; 357 | @property (assign) BOOL isEditing; 358 | @property (retain) SBFolder *openFolder; 359 | @end 360 | 361 | @interface SBAwayController : SBAlert 362 | + (id)sharedAwayController; 363 | + (id)sharedAwayControllerIfExists; 364 | - (BOOL)allowDismissCameraSystemGesture; // iOS 5.1+ 365 | - (void)handleDismissCameraSystemGesture:(SBGestureRecognizer *)gesture; // iOS 5.1+ 366 | - (BOOL)isLocked; 367 | @end 368 | 369 | @interface SBLockdownManager : NSObject 370 | + (id)sharedInstance; 371 | - (void)_developerDeviceStateChanged; 372 | @end 373 | 374 | typedef enum { /* no fucking clue */ } SBTouchType; 375 | 376 | typedef struct { 377 | SBTouchType type; 378 | NSUInteger pathIndex; 379 | CGPoint location; 380 | CGPoint previousLocation; 381 | CGFloat totalDistanceTraveled; 382 | UIInterfaceOrientation interfaceOrientation; 383 | UIInterfaceOrientation previousInterfaceOrientation; 384 | } SBTouchInfo; 385 | 386 | typedef struct { 387 | UIEdgeInsets pixelDeltas; // 0x00, 0x04, 0x08, 0x0C 388 | UIEdgeInsets averageVelocities; // 0x10, 0x14, 0x18, 0x1C 389 | CGFloat averageTranslation; // 0x20 390 | CGPoint movementVelocityInPointsPerSecond; // 0x24, 0x28 391 | CGFloat farthestFingerSeparation; // 0x2C 392 | NSInteger activeTouchCount; // 0x30 393 | 394 | int unk1; 395 | int unk2; 396 | int unk3; 397 | int unk4; 398 | 399 | SBTouchInfo activeTouches[30]; // 0x44; count is activeTouchCount 400 | } SBGestureContext; 401 | 402 | typedef enum { 403 | SBGestureRecognizerStatePossible, 404 | SBGestureRecognizerStateBegan, 405 | SBGestureRecognizerStateChanged, 406 | SBGestureRecognizerStateEnded, 407 | SBGestureRecognizerStateCancelled, 408 | SBGestureRecognizerStateFailed, 409 | SBGestureRecognizerStateRecognized = SBGestureRecognizerStateEnded 410 | } SBGestureRecognizerState; 411 | 412 | typedef enum { 413 | kSBOffscreenEdgeLeft = 1, 414 | kSBOffscreenEdgeTop = 2, 415 | kSBOffscreenEdgeRight = 4, 416 | kSBOffscreenEdgeBottom = 8 417 | } SBOffscreenEdge; 418 | 419 | @interface SBTouchTemplate : NSObject 420 | @property (assign, nonatomic) CGFloat acceptFactor; 421 | @property (readonly, assign, nonatomic) NSUInteger pointCount; 422 | - (id)initWithPoints:(CGPoint *)points count:(NSUInteger)count; 423 | - (BOOL)acceptPoints:(CGPoint *)points count:(NSUInteger)count; 424 | @end 425 | 426 | @interface SBGestureRecognizer : NSObject 427 | @property (nonatomic, copy) BOOL (^canBeginCondition)(); 428 | - (BOOL)shouldReceiveTouches; 429 | 430 | @property (assign, nonatomic) BOOL sendsTouchesCancelledToApplication; 431 | - (void)sendTouchesCancelledToApplicationIfNeeded; 432 | 433 | @property (nonatomic, assign) int types; 434 | 435 | @property (nonatomic, copy) void (^handler)(); 436 | @property (nonatomic, assign) SBGestureRecognizerState state; 437 | - (void)touchesBegan:(SBGestureContext *)began; 438 | - (void)touchesCancelled:(SBGestureContext *)cancelled; 439 | - (void)touchesEnded:(SBGestureContext *)ended; 440 | - (void)touchesMoved:(SBGestureContext *)moved; 441 | - (void)reset; 442 | @end 443 | 444 | @interface SBFluidSlideGestureRecognizer : SBGestureRecognizer 445 | @property (nonatomic, assign) NSInteger minTouches; 446 | @property(assign, nonatomic) int requiredDirectionality; 447 | @property(assign, nonatomic) CGFloat accelerationPower; 448 | @property(assign, nonatomic) CGFloat accelerationThreshold; 449 | @property(assign, nonatomic) CGFloat animationDistance; 450 | @property(readonly, assign, nonatomic) int degreeOfFreedom; 451 | @property(readonly, assign, nonatomic) CGFloat cumulativeMotion; 452 | @property(readonly, assign, nonatomic) CGFloat cumulativePercentage; 453 | @property(readonly, assign, nonatomic) CGFloat incrementalMotion; 454 | @property(readonly, assign, nonatomic) CGFloat skippedCumulativePercentage; 455 | @property(readonly, assign, nonatomic) CGPoint centroidPoint; 456 | @property(readonly, assign, nonatomic) CGPoint movementVelocityInPointsPerSecond; 457 | - (int)completionTypeProjectingMomentumForInterval:(NSTimeInterval)interval; 458 | - (CGPoint)centroidPoint; 459 | @end 460 | 461 | @interface SBPanGestureRecognizer : SBFluidSlideGestureRecognizer 462 | @end 463 | 464 | @interface SBOffscreenSwipeGestureRecognizer : SBPanGestureRecognizer 465 | @property(assign, nonatomic) CGFloat allowableDistanceFromEdgeCenter; 466 | @property(assign, nonatomic) CGFloat edgeCenter; 467 | @property(assign, nonatomic) CGFloat edgeMargin; 468 | @property(assign, nonatomic) CGFloat falseEdge; 469 | @property(assign, nonatomic) BOOL requiresSecondTouchInRange; 470 | - (id)initForOffscreenEdge:(SBOffscreenEdge)edge; 471 | - (void)_updateAnimationDistanceAndEdgeCenter; 472 | - (BOOL)firstTouchInRange:(CGPoint)range; 473 | - (BOOL)secondTouchInRange:(CGPoint)range; 474 | @end 475 | 476 | @interface SBHandMotionExtractor : NSObject 477 | @property (readonly, assign, nonatomic) UIEdgeInsets allPixelDeltas; 478 | @property (readonly, assign, nonatomic) CGFloat averageTranslation; 479 | @property (readonly, assign, nonatomic) UIEdgeInsets averageVelocities; 480 | @property (readonly, assign, nonatomic) CGFloat farthestFingerSeparation; 481 | @property (readonly, assign, nonatomic) CGPoint movementVelocityInPointsPerSecond; 482 | @property (readonly, assign, nonatomic) UIEdgeInsets pixelDeltas; 483 | - (void)clear; 484 | - (void)extractHandMotionForActiveTouches:(SBTouchInfo *)activeTouches count:(NSUInteger)count centroid:(CGPoint)centroid; 485 | @end 486 | 487 | @interface BKSWorkspace : NSObject // iOS 6 488 | @end 489 | 490 | @interface SBWorkspaceTransaction : NSObject // iOS 6 491 | @property (readonly, assign) BOOL completed; 492 | @property (readonly, assign, nonatomic) BKSWorkspace *workspace; 493 | - (id)initWithWorkspace:(BKSWorkspace *)workspace alertManager:(id)manager; 494 | - (void)commit; 495 | - (BOOL)canBeInterrupted; 496 | - (void)interrupt; 497 | @end 498 | 499 | @interface SBToAppWorkspaceTransaction : SBWorkspaceTransaction // iOS 6 500 | @property (retain, nonatomic) SBApplication *toApplication; 501 | - (id)initWithWorkspace:(BKSWorkspace *)workspace alertManager:(id)manager toApplication:(SBApplication *)application; 502 | @end 503 | 504 | @interface SBAppToAppWorkspaceTransaction : SBToAppWorkspaceTransaction // iOS 6 505 | @property (retain, nonatomic) SBApplication *fromApp; 506 | - (id)initWithWorkspace:(BKSWorkspace *)workspace alertManager:(id)manager exitedApp:(SBApplication *)app; 507 | - (id)initWithWorkspace:(BKSWorkspace *)workspace alertManager:(id)manager from:(SBApplication *)from to:(SBApplication *)to; 508 | @end 509 | 510 | @interface SBWorkspace : NSObject // iOS 6 511 | @property (nonatomic, assign, readonly) BKSWorkspace *bksWorkspace; 512 | @property (nonatomic, retain) SBWorkspaceTransaction *currentTransaction; 513 | - (void)updateInterruptedByCallSettingsFrom:(SBApplication *)from to:(SBApplication *)to; 514 | @end 515 | 516 | @interface SBWorkspaceEvent : NSObject // iOS 6 517 | + (id)eventWithLabel:(NSString *)label handler:(void (^)())handler; 518 | @end 519 | 520 | @interface SBWorkspaceEventQueue : NSObject // iOS 6 521 | + (id)sharedInstance; 522 | - (void)executeOrPrependEvent:(SBWorkspaceEvent *)event; 523 | - (void)executeOrAppendEvent:(SBWorkspaceEvent *)event; 524 | @end 525 | 526 | static NSMutableArray *displayStacks = nil; 527 | 528 | #define SBWPreActivateDisplayStack [displayStacks objectAtIndex:0] 529 | #define SBWActiveDisplayStack [displayStacks objectAtIndex:1] 530 | #define SBWSuspendingDisplayStack [displayStacks objectAtIndex:2] 531 | #define SBWSuspendedEventOnlyDisplayStack [displayStacks objectAtIndex:3] 532 | 533 | static SBWorkspace *sharedWorkspace = nil; 534 | 535 | #define SBWSharedWorkspace sharedWorkspace 536 | 537 | %group Shared 538 | 539 | %hook SBDisplayStack 540 | 541 | - (id)init { 542 | self = %orig; 543 | [displayStacks addObject:self]; 544 | return self; 545 | } 546 | 547 | - (void)dealloc { 548 | [displayStacks removeObject:self]; 549 | %orig; 550 | } 551 | 552 | %end 553 | 554 | %hook SBWorkspace 555 | 556 | - (id)init { 557 | self = %orig; 558 | sharedWorkspace = self; 559 | return self; 560 | } 561 | 562 | %end 563 | 564 | %hook SBUIController 565 | 566 | - (void)finishLaunching { 567 | SBApp = (SpringBoard *) [objc_getClass("SpringBoard") sharedApplication]; 568 | %orig; 569 | } 570 | 571 | %end 572 | 573 | %end 574 | 575 | %ctor { 576 | displayStacks = [[NSMutableArray alloc] init]; 577 | %init(Shared); 578 | } 579 | 580 | -------------------------------------------------------------------------------- /layersnapshotter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011-2013, Xuzz Productions, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #import 27 | #import 28 | 29 | @interface CALayer (Snapshot) 30 | - (UIImage *)renderSnapshotWithScale:(CGFloat)scale; 31 | @end 32 | 33 | @interface UIView (Snapshot) 34 | - (UIImage *)renderSnapshot; 35 | @end 36 | 37 | @interface UIImage (Saving) 38 | - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)atomically; 39 | @end 40 | -------------------------------------------------------------------------------- /layout/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: com.chpwn.zephyr 2 | Name: Zephyr 3 | Section: Tweaks 4 | Version: 1.6.4 5 | Pre-Depends: firmware (>= 5.0) 6 | Depends: mobilesubstrate, preferenceloader, applist, com.rpetrich.layersnapshotter 7 | Conflicts: com.dapetcu21.MultiCleaner (<= 2.9.4), com.xsellize.multicleaner, com.xsellize.zephyr 8 | Icon: file:///Library/PreferenceLoader/Preferences/Zephyr@2x.png 9 | Architecture: iphoneos-arm 10 | Description: Awesome swipe multitasking and gestures! 11 | Depiction: http://chpwn.com/cydia/zephyr.html 12 | Homepage: http://chpwn.com/cydia/zephyr.html 13 | Author: chpwn 14 | Maintainer: BigBoss 15 | Sponsor: thebigboss.org 16 | Tag: cydia::commercial, purpose::extension 17 | dev: chpwn 18 | -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/Zephyr.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | title 6 | Zephyr 7 | entry 8 | 9 | cell 10 | PSLinkCell 11 | icon 12 | Zephyr.png 13 | label 14 | Zephyr 15 | 16 | items 17 | 18 | 19 | cell 20 | PSGroupCell 21 | label 22 | Swipe from Left and Right 23 | 24 | 25 | cell 26 | PSSwitchCell 27 | default 28 | 29 | key 30 | SwipeSideEnabled 31 | label 32 | Enabled 33 | PostNotification 34 | com.chpwn.zephyr.preferences-changed 35 | defaults 36 | com.chpwn.zephyr 37 | 38 | 39 | cell 40 | PSSwitchCell 41 | default 42 | 43 | key 44 | SideGrabberEnabled 45 | label 46 | Use Grabber 47 | PostNotification 48 | com.chpwn.zephyr.preferences-changed 49 | defaults 50 | com.chpwn.zephyr 51 | 52 | 53 | cell 54 | PSLinkListCell 55 | default 56 | 1 57 | detail 58 | PSListItemsController 59 | key 60 | SideMinimumTouchCount 61 | label 62 | Number of Fingers 63 | validTitles 64 | 65 | One 66 | Two 67 | Three 68 | Four 69 | 70 | validValues 71 | 72 | 1 73 | 2 74 | 3 75 | 4 76 | 77 | defaults 78 | com.chpwn.zephyr 79 | PostNotification 80 | com.chpwn.zephyr.preferences-changed 81 | 82 | 83 | cell 84 | PSLinkListCell 85 | default 86 | 30.0 87 | detail 88 | PSListItemsController 89 | key 90 | SideSensitivityDistance 91 | label 92 | Sensitivity 93 | validTitles 94 | 95 | Tiny 96 | Wee 97 | Small 98 | Normal 99 | Large 100 | Big 101 | Huge 102 | Gigantic 103 | 104 | validValues 105 | 106 | 15.0 107 | 20.0 108 | 25.0 109 | 30.0 110 | 40.0 111 | 55.0 112 | 80.0 113 | 120.0 114 | 115 | defaults 116 | com.chpwn.zephyr 117 | PostNotification 118 | com.chpwn.zephyr.preferences-changed 119 | 120 | 121 | cell 122 | PSLinkCell 123 | bundle 124 | AppList 125 | isController 126 | 127 | label 128 | Disable in Applications 129 | ALSettingsPath 130 | /var/mobile/Library/Preferences/com.chpwn.zephyr.plist 131 | ALSettingsKeyPrefix 132 | SideDisable- 133 | ALChangeNotification 134 | com.chpwn.zephyr.preferences-changed 135 | 136 | 137 | cell 138 | PSLinkListCell 139 | default 140 | 1.0 141 | detail 142 | PSListItemsController 143 | key 144 | SideScreenArea 145 | label 146 | Screen Area 147 | validTitles 148 | 149 | Whole Screen 150 | Top Three Quarters 151 | Top Two Thirds 152 | Top Half 153 | Top Third 154 | Top Quarter 155 | Bottom Three Quarters 156 | Bottom Two Thirds 157 | Bottom Half 158 | Bottom Third 159 | Bottom Quarter 160 | 161 | validValues 162 | 163 | 1.00 164 | 0.75 165 | 0.66 166 | 0.50 167 | 0.33 168 | 0.25 169 | -0.75 170 | -0.66 171 | -0.50 172 | -0.33 173 | -0.25 174 | 175 | PostNotification 176 | com.chpwn.zephyr.preferences-changed 177 | defaults 178 | com.chpwn.zephyr 179 | 180 | 181 | 182 | cell 183 | PSGroupCell 184 | label 185 | Swipe up from Bottom 186 | footerText 187 | Switcher delay is only used when the action is close app. 188 | 189 | 190 | cell 191 | PSLinkListCell 192 | default 193 | 1 194 | detail 195 | PSListItemsController 196 | key 197 | SwipeUpAction 198 | label 199 | Action 200 | validTitles 201 | 202 | Disabled 203 | Close App (and Switcher) 204 | Switcher Only 205 | 206 | validValues 207 | 208 | 0 209 | 1 210 | 2 211 | 212 | defaults 213 | com.chpwn.zephyr 214 | PostNotification 215 | com.chpwn.zephyr.preferences-changed 216 | 217 | 218 | cell 219 | PSSwitchCell 220 | default 221 | 222 | key 223 | BottomGrabberEnabled 224 | label 225 | Use Grabber 226 | PostNotification 227 | com.chpwn.zephyr.preferences-changed 228 | defaults 229 | com.chpwn.zephyr 230 | 231 | 232 | cell 233 | PSLinkListCell 234 | default 235 | 1 236 | detail 237 | PSListItemsController 238 | key 239 | BottomMinimumTouchCount 240 | label 241 | Number of Fingers 242 | validTitles 243 | 244 | One 245 | Two 246 | Three 247 | Four 248 | 249 | validValues 250 | 251 | 1 252 | 2 253 | 3 254 | 4 255 | 256 | defaults 257 | com.chpwn.zephyr 258 | PostNotification 259 | com.chpwn.zephyr.preferences-changed 260 | 261 | 262 | cell 263 | PSLinkListCell 264 | default 265 | 30.0 266 | detail 267 | PSListItemsController 268 | key 269 | BottomSensitivityDistance 270 | label 271 | Sensitivity 272 | validTitles 273 | 274 | Tiny 275 | Wee 276 | Small 277 | Normal 278 | Large 279 | Big 280 | Huge 281 | Gigantic 282 | 283 | validValues 284 | 285 | 15.0 286 | 20.0 287 | 25.0 288 | 30.0 289 | 40.0 290 | 55.0 291 | 80.0 292 | 120.0 293 | 294 | defaults 295 | com.chpwn.zephyr 296 | PostNotification 297 | com.chpwn.zephyr.preferences-changed 298 | 299 | 300 | cell 301 | PSLinkCell 302 | bundle 303 | AppList 304 | isController 305 | 306 | label 307 | Disable in Applications 308 | ALSettingsPath 309 | /var/mobile/Library/Preferences/com.chpwn.zephyr.plist 310 | ALSettingsKeyPrefix 311 | BottomDisable- 312 | ALChangeNotification 313 | com.chpwn.zephyr.preferences-changed 314 | 315 | 316 | cell 317 | PSLinkListCell 318 | default 319 | 0.8 320 | detail 321 | PSListItemsController 322 | key 323 | BottomSwitcherActivationDelay 324 | label 325 | Switcher Delay 326 | validTitles 327 | 328 | 0.1 seconds 329 | 0.2 seconds 330 | 0.3 seconds 331 | 0.4 seconds 332 | 0.5 seconds 333 | 0.6 seconds 334 | 0.7 seconds 335 | 0.8 seconds 336 | 0.9 seconds 337 | 1.0 seconds 338 | 1.1 seconds 339 | 1.2 seconds 340 | 1.3 seconds 341 | 1.4 seconds 342 | 1.5 seconds 343 | Never 344 | 345 | validValues 346 | 347 | 0.1 348 | 0.2 349 | 0.3 350 | 0.4 351 | 0.5 352 | 0.6 353 | 0.7 354 | 0.8 355 | 0.9 356 | 1.0 357 | 1.1 358 | 1.2 359 | 1.3 360 | 1.4 361 | 1.5 362 | 0 363 | 364 | defaults 365 | com.chpwn.zephyr 366 | PostNotification 367 | com.chpwn.zephyr.preferences-changed 368 | 369 | 370 | cell 371 | PSLinkListCell 372 | default 373 | 1 374 | detail 375 | PSListItemsController 376 | key 377 | BottomDisableKeyboard 378 | label 379 | Keyboard 380 | validTitles 381 | 382 | Enabled 383 | Disabled 384 | Grabber 385 | 386 | validValues 387 | 388 | 0 389 | 1 390 | 2 391 | 392 | PostNotification 393 | com.chpwn.zephyr.preferences-changed 394 | defaults 395 | com.chpwn.zephyr 396 | 397 | 398 | 399 | cell 400 | PSGroupCell 401 | label 402 | System Gestures 403 | 404 | 405 | cell 406 | PSSwitchCell 407 | default 408 | 409 | key 410 | SystemSwitcherEnabled 411 | label 412 | Four-finger Switcher 413 | PostNotification 414 | com.chpwn.zephyr.preferences-changed 415 | defaults 416 | com.chpwn.zephyr 417 | 418 | 419 | cell 420 | PSSwitchCell 421 | default 422 | 423 | key 424 | SystemSwitchAppEnabled 425 | label 426 | Four-finger Switch App 427 | PostNotification 428 | com.chpwn.zephyr.preferences-changed 429 | defaults 430 | com.chpwn.zephyr 431 | 432 | 433 | cell 434 | PSSwitchCell 435 | default 436 | 437 | key 438 | SystemSuspendEnabled 439 | label 440 | Pinch to Close 441 | PostNotification 442 | com.chpwn.zephyr.preferences-changed 443 | defaults 444 | com.chpwn.zephyr 445 | 446 | 447 | cell 448 | PSSwitchCell 449 | default 450 | 451 | key 452 | SystemSuspendThreeFingers 453 | label 454 | Three-Finger Pinch 455 | PostNotification 456 | com.chpwn.zephyr.preferences-changed 457 | defaults 458 | com.chpwn.zephyr 459 | 460 | 461 | 462 | cell 463 | PSGroupCell 464 | requiredCapabilities 465 | 466 | ipad 467 | 468 | footerText 469 | These override the built-in system gesture setting. 470 | 471 | 472 | 473 | cell 474 | PSGroupCell 475 | label 476 | Notification Center (iPhone) 477 | footerText 478 | Zephyr © 2013 Xuzz Productions, LLC. 479 | Support is available in Cydia. 480 | 481 | 482 | cell 483 | PSSwitchCell 484 | default 485 | 486 | key 487 | NotificationEnabled 488 | label 489 | Enabled 490 | PostNotification 491 | com.chpwn.zephyr.preferences-changed 492 | defaults 493 | com.chpwn.zephyr 494 | 495 | 496 | 497 | 498 | cell 499 | PSGroupCell 500 | 501 | 502 | 503 | 504 | -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/Zephyr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grp/Zephyr/ce2b93606e3427128ef549c4519c4100789eee92/layout/Library/PreferenceLoader/Preferences/Zephyr.png -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/Zephyr@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grp/Zephyr/ce2b93606e3427128ef549c4519c4100789eee92/layout/Library/PreferenceLoader/Preferences/Zephyr@2x.png --------------------------------------------------------------------------------