-
Notifications
You must be signed in to change notification settings - Fork 3
/
GestureEnabler.xmi
210 lines (165 loc) · 7.96 KB
/
GestureEnabler.xmi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
* Copyright (c) 2011-2013, Xuzz Productions, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "Common.h"
// The purpose of this file is to re-enable the iPad system gestures on the iPhone,
// as well as the methods that implement them, so other parts of Zephyr can use that
// code. To do so, it needs to hook a C function that determines if gesture recognition
// is allowed. Unfortunately, there is no way to get a reference to that function.
// Instead, we hook the places that function is called, and then call it again in
// a context we control in order to force the gestures enabled, if just disabled.
static NSInteger disableNextCheck = 0;
static NSInteger fakeNextScrollingCheck = 0;
static void EnsureGesturesEnabled() {
disableNextCheck += 1;
// This function calls through to the gesture setup function. Since we can't get the address of that function,
// we have to call through to it though something we do have access to. This is a rarely-used method that works.
[[objc_getClass("SBLockdownManager") sharedInstance] _developerDeviceStateChanged];
disableNextCheck -= 1;
}
// -[SBLockdownManager _developerDeviceStateChanged] calls CFPreferencesSynchronize, but in my testing, I've seen
// that crash badly. Since we don't have other options for what to call on iOS 5, just neuter the synchronize call.
static Boolean (*orig_CFPreferencesSynchronize)(CFStringRef, CFStringRef, CFStringRef);
static Boolean replaced_CFPreferencesSynchronize(CFStringRef applicationID, CFStringRef userName, CFStringRef hostName) {
if (disableNextCheck > 0) {
return NO;
}
return orig_CFPreferencesSynchronize(applicationID, userName, hostName);
}
%group GestureEnabler
%hook UIDevice
// The gesture recognition for system gestures is only setup when the device
// is an iPad. When we are faking it (see above), pretend to be on an iPad.
- (UIUserInterfaceIdiom)userInterfaceIdiom {
if (disableNextCheck > 0) {
return UIUserInterfaceIdiomPad;
}
return %orig;
}
%end
%hook SBIconController
// On iOS 6 and later, the home screen being scrolling blocks system gestures.
// This apparently bugged people (quite a bit of support email), so force it back.
- (BOOL)isScrolling {
if (fakeNextScrollingCheck > 0) {
fakeNextScrollingCheck -= 1;
return NO;
}
return %orig;
}
%end
%hook SBPlatformController
// The gesture setup will only be called if we have the capability.
- (BOOL)hasCapability:(NSString *)capability {
if ([capability isEqual:kGSMultitaskingGesturesCapability]) {
return YES;
}
return %orig;
}
%end
%end
// One of the common call sites for "the function" is SBSystemGesturesChangeGestureAndRecognitionState,
// another C function, called from too many C functions and methods for us to hook them all. However,
// there is one way to detect SBSystemGesturesChangeGestureAndRecognitionState: if logging is enabled,
// it will print out a log message that we can detect. Then, we can force the gestures enabled again.
static void (*orig_NSLogv)(NSString *, va_list);
static void replaced_NSLogv(NSString *what, va_list args) {
if ([what hasPrefix:@"SBSystemGesturesChangeGestureAndRecognitionState"]) {
// Only iOS 6 checks for home screen scrolling (see above). But rather
// than hardcoding a set of versions to do this on, check dynamically:
if ([what rangeOfString:@"home-screen scrolling"].location != NSNotFound) {
fakeNextScrollingCheck++;
}
// This is called in the middle of the method, but we need to force it
// re-enabled after a later portion. To do that, just async the call.
dispatch_async(dispatch_get_main_queue(), ^{
EnsureGesturesEnabled();
});
return;
}
orig_NSLogv(what, args);
}
static Boolean (*orig_CFPreferencesGetAppBooleanValue)(CFStringRef, CFStringRef, Boolean *);
static Boolean replaced_CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef applicationID, Boolean *keyExistsAndHasValidFormat) {
if ([(NSString *)key isEqual:@"SBSystemGesturesLogging"]) {
// The above logging message that we depend on to get notified when
// SBSystemGesturesChangeGestureAndRecognitionState is called is only
// logged when this preferences key is enabled. Let's hope it doesn't
// go away, since there doesn't seem to be a clear need for it in SB.
return YES;
} else {
return orig_CFPreferencesGetAppBooleanValue(key, applicationID, keyExistsAndHasValidFormat);
}
}
static CFPropertyListRef (*orig_CFPreferencesCopyAppValue)(CFStringRef, CFStringRef);
static CFPropertyListRef replaced_CFPreferencesCopyAppValue(CFStringRef key, CFStringRef applicationID) {
if ([(NSString *)key isEqual:@"SBUseSystemGestures"]) {
// The iPad has this set by default, but the iPhone doesn't (and has no
// way to set it in the UI), so we need to enable gestures manually here.
// (But even on the iPad, we need it set for our gestures to work at all.)
return [[NSNumber numberWithInt:1] copy];
} else {
return orig_CFPreferencesCopyAppValue(key, applicationID);
}
}
// Besides SBSystemGesturesChangeGestureAndRecognitionState, there are a few other
// functions and methods that call the gesture setup function. We need to make sure
// to reset the enabled state after they are called. (We can't just pretend to be
// an iPad for the entirety of these methods, as that would cause lots of issues.)
// NOTE: When new iOS versions are released, be sure to check this list in IDA!
%group GestureEnablerFixes
%hook SBUIController
- (void)_reloadDemoAndDebuggingDefaultsAndCapabilities { // iOS 6
%orig;
EnsureGesturesEnabled();
}
- (void)noteInterfaceOrientationChanged:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration updateMirroredDisplays:(BOOL)mirror force:(BOOL)force {
%orig;
EnsureGesturesEnabled();
}
- (id)init {
self = %orig;
EnsureGesturesEnabled();
return self;
}
%end
%hook SpringBoard
- (void)userDefaultsDidChange:(id)something { // iOS 5
%orig;
EnsureGesturesEnabled();
}
- (void)applicationDidFinishLaunching:(SpringBoard *)app {
%orig;
EnsureGesturesEnabled();
}
%end
%end
%ctor {
%init(GestureEnabler);
%init(GestureEnablerFixes);
MSHookFunction(CFPreferencesGetAppBooleanValue, replaced_CFPreferencesGetAppBooleanValue, &orig_CFPreferencesGetAppBooleanValue);
MSHookFunction(CFPreferencesCopyAppValue, replaced_CFPreferencesCopyAppValue, &orig_CFPreferencesCopyAppValue);
MSHookFunction(CFPreferencesSynchronize, replaced_CFPreferencesSynchronize, &orig_CFPreferencesSynchronize);
MSHookFunction((void *)NSLogv, (void *)replaced_NSLogv, (void **)&orig_NSLogv);
}