root/Spiral/trunk/SPBluetoothRemote.m

Revision 5523, 6.6 kB (checked in by morris, 4 months ago)

Only disconnect from Bluetooth remotes on delay when there are no player windows open

Refactor delegate methods a bit

  • Property svn:keywords set to Id Rev
Line 
1 /* $Id$ */
2
3 /*
4  *  Copyright (c) 2008 Axel Andersson
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "SPBluetoothRemote.h"
30
31 @interface SPBluetoothRemote(Private)
32
33 + (IOBluetoothDevice *)_recentRemoteDevice;
34
35 - (void)_connect;
36 - (BOOL)_connectAsync;
37 - (BOOL)_connectControlAsync;
38 - (BOOL)_connectInterruptAsync;
39 - (void)_disconnect;
40
41 @end
42
43
44 @implementation SPBluetoothRemote(Private)
45
46 + (IOBluetoothDevice *)_recentRemoteDevice {
47         NSArray                         *devices;
48         IOBluetoothDevice       *device;
49        
50         devices = [IOBluetoothDevice recentDevices:0];
51        
52         for(device in devices) {
53                 if([[device getName] isEqualToString:[self remoteName]])
54                         return device;
55         }
56        
57         return NULL;
58 }
59
60
61
62 #pragma mark -
63
64 - (void)_connect {
65         if(![self _connectAsync])
66                 [self connectAfterDelay];
67 }
68
69
70
71 - (BOOL)_connectAsync {
72         if(!_device)
73                 return NO;
74        
75         [_disconnectNotification unregister];
76         _disconnectNotification = [_device registerForDisconnectNotification:self selector:@selector(bluetoothDeviceDidDisconnect:device:)];
77        
78         if(!_controlConnected && [[self class] needsControlChannel]) {
79                 if(![self _connectControlAsync])
80                         return NO;
81         }
82
83         if(!_interruptConnected && [[self class] needsInterruptChannel]) {
84                 if(![self _connectInterruptAsync])
85                         return NO;
86         }
87        
88         return YES;
89 }
90
91
92
93 - (BOOL)_connectControlAsync {
94         IOReturn                result;
95        
96         _controlCompleted = NO;
97        
98         if(_logging)
99                 NSLog(@"*** -[%@ _connectControlAsync]", [self class]);
100
101         result = [_device openL2CAPChannelAsync:&_controlChannel withPSM:kBluetoothL2CAPPSMHIDControl delegate:self];
102        
103         return (result == kIOReturnSuccess);
104 }
105
106
107
108 - (BOOL)_connectInterruptAsync {
109         IOReturn                result;
110
111         _interruptCompleted = NO;
112        
113         if(_logging)
114                 NSLog(@"*** -[%@ _connectInterruptAsync]", [self class]);
115        
116         result = [_device openL2CAPChannelAsync:&_interruptChannel withPSM:kBluetoothL2CAPPSMHIDInterrupt delegate:self];
117        
118         return (result == kIOReturnSuccess);
119 }
120
121
122
123 - (void)_disconnect {
124         NSLog(@"*** -[%@ _disconnect]", [self class]);
125
126         if([self remoteShouldDisconnect]) {
127                 if(_controlConnected)
128                         [_controlChannel closeChannel];
129
130                 if(_interruptConnected)
131                         [_interruptChannel closeChannel];
132                
133                 [_device closeConnection];
134         } else {
135                 [self disconnectAfterDelay];
136         }
137 }
138
139 @end
140
141
142
143 @implementation SPBluetoothRemote
144
145 + (NSString *)remoteName {
146         [self doesNotRecognizeSelector:_cmd];
147        
148         return NULL;
149 }
150
151
152
153 + (BOOL)needsControlChannel {
154         return NO;
155 }
156
157
158
159 + (BOOL)needsInterruptChannel {
160         return NO;
161 }
162
163
164
165 #pragma mark -
166
167 - (id)init {
168         self = [super init];
169        
170         _device = [[[self class] _recentRemoteDevice] retain];
171        
172         if(_device)
173                 [self connectAfterDelay];
174        
175         [IOBluetoothDevice registerForConnectNotifications:self selector:@selector(bluetoothDeviceDidConnect:device:)];
176
177         return self;
178 }
179
180
181
182 - (void)dealloc {
183         [_device release];
184        
185         [super dealloc];
186 }
187
188
189
190 #pragma mark -
191
192 - (void)connectAfterDelay {
193         _connecting = YES;
194        
195         [[NSNotificationCenter defaultCenter] postNotificationName:SPBluetoothRemoteWillConnect object:self];
196        
197         [self performSelectorOnce:@selector(_connect) afterDelay:1.0];
198 }
199
200
201
202 - (void)disconnectAfterDelay {
203         [self performSelectorOnce:@selector(_disconnect) afterDelay:600.0];
204 }
205
206
207
208 - (BOOL)remoteShouldDisconnect {
209         return NO;
210 }
211
212
213
214 - (void)remoteDidConnect {
215         _connected = YES;
216        
217         [[NSNotificationCenter defaultCenter] postNotificationName:SPBluetoothRemoteDidConnect object:self];
218 }
219
220
221
222 #pragma mark -
223
224 - (BOOL)isConnecting {
225         return _connecting;
226 }
227
228
229
230 - (BOOL)isConnected {
231         return _connected;
232 }
233
234
235
236 - (BOOL)hasDevice {
237         return (_device != NULL);
238 }
239
240
241
242 #pragma mark -
243
244 - (void)l2capChannelOpenComplete:(IOBluetoothL2CAPChannel *)channel status:(IOReturn)status {
245         BOOL            needsControl, needsInterrupt;
246        
247         if(channel == _controlChannel) {
248                 _controlCompleted = YES;
249                 _controlConnected = (status == kIOReturnSuccess);
250
251                 if(_logging)
252                         NSLog(@"*** -[%@ l2capChannelOpenComplete:status::] control %s", [self class], mach_error_string(status));
253         }
254         else if(channel == _interruptChannel) {
255                 _interruptCompleted = YES;
256                 _interruptConnected = (status == kIOReturnSuccess);
257
258                 if(_logging)
259                         NSLog(@"*** -[%@ l2capChannelOpenComplete:status::]: interrupt %s", [self class], mach_error_string(status));
260         }
261        
262         needsControl    = [[self class] needsControlChannel];
263         needsInterrupt  = [[self class] needsInterruptChannel];
264        
265         if((_controlCompleted || !needsControl) && (_interruptCompleted || !needsInterrupt)) {
266                 _connecting = NO;
267                
268                 if((_controlConnected || !needsControl) && (_interruptConnected || !needsInterrupt))
269                         [self remoteDidConnect];
270                 else
271                         [self connectAfterDelay];
272         }
273 }
274
275
276
277 - (void)l2capChannelData:(IOBluetoothL2CAPChannel *)channel data:(unsigned char *)buffer length:(size_t)length {
278         [self doesNotRecognizeSelector:_cmd];
279 }
280
281
282
283 - (void)bluetoothDeviceDidConnect:(IOBluetoothUserNotification *)notification device:(IOBluetoothDevice *)device {
284         if([[device getName] isEqualToString:[[self class] remoteName]]) {
285                 _logging = YES;
286                
287                 NSLog(@"*** -[%@ bluetoothDeviceDidConnect:device::]: %@", [self class], [device getName]);
288
289                 [device retain];
290                 [_device release];
291                
292                 _device = device;
293                
294                 [self connectAfterDelay];
295         }
296 }
297
298
299
300 - (void)bluetoothDeviceDidDisconnect:(IOBluetoothUserNotification *)notification device:(IOBluetoothDevice *)device {
301         if([[device getName] isEqualToString:[[self class] remoteName]]) {
302                 _logging = YES;
303                
304                 NSLog(@"*** -[%@ bluetoothDeviceDidDisconnect:device::]: %@", [self class], [device getName]);
305        
306                 _controlConnected = NO;
307                 _interruptConnected = NO;
308                 _connected = NO;
309        
310                 [[NSNotificationCenter defaultCenter] postNotificationName:SPBluetoothRemoteDidDisconnect object:self];
311         }
312 }
313
314 @end
Note: See TracBrowser for help on using the browser.