Mercurial > mplayer.hg
annotate input/ar.c @ 28080:0ab460f4e434
Use svn.ffmpeg.org for the externals which is both more correct and more reliable.
author | reimar |
---|---|
date | Mon, 08 Dec 2008 14:43:28 +0000 |
parents | 0f42fb42843c |
children | eda346733b8c |
rev | line source |
---|---|
24037 | 1 /* |
2 * Apple Remote input interface | |
3 * | |
4 * Copyright (C) 2007 Zoltan Ponekker <pontscho at kac.poliod.hu> | |
5 * | |
6 * (modified a bit by Ulion <ulion2002 at gmail.com>) | |
7 * | |
8 * This file is part of MPlayer. | |
9 * | |
10 * MPlayer is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * MPlayer is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
26743
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
25962
diff
changeset
|
20 * You should have received a copy of the GNU General Public License along |
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
25962
diff
changeset
|
21 * with MPlayer; if not, write to the Free Software Foundation, Inc., |
0f42fb42843c
Use standard license headers with standard formatting.
diego
parents:
25962
diff
changeset
|
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
24037 | 23 */ |
24 | |
25 #include <IOKit/IOCFPlugIn.h> | |
26 #include <IOKit/hid/IOHIDLib.h> | |
27 #include <Carbon/Carbon.h> | |
28 | |
29 #include <sys/types.h> | |
30 #include <unistd.h> | |
31 | |
32 #include "input.h" | |
33 #include "ar.h" | |
34 | |
35 extern int slave_mode; | |
36 | |
37 extern const double NSAppKitVersionNumber; | |
38 | |
39 typedef struct cookie_keycode_map { | |
40 char *cookies; | |
41 int seq_len; | |
42 int keycode; | |
43 } cookie_keycode_map_t; | |
44 | |
45 /* On tiger, 5 always follows 6; on leopard, 18 always follows 19. | |
46 * On leopard, there seems to be no cookie value of 5 or 6. | |
47 * Following is the shortened cookie sequence list | |
48 * keycode cookies_on_tiger cookies_on_leopard *down_state | |
49 * AR_PREV_HOLD 14+6+3+2 31+19+3+2 yes | |
50 * AR_NEXT_HOLD 14+6+4+2 31+19+4+2 yes | |
51 * AR_MENU_HOLD 14+6+14+6 31+19+31+19 | |
52 * AR_VUP 14+12+11+6 31+29+28+19 yes | |
53 * AR_VDOWN 14+13+11+6 31+30+28+19 yes | |
54 * AR_MENU 14+7+6+14+7+6 31+20+19+31+20+19 | |
55 * AR_PLAY 14+8+6+14+8+6 31+21+19+31+21+19 | |
56 * AR_NEXT 14+9+6+14+9+6 31+22+19+31+22+19 | |
57 * AR_PREV 14+10+6+14+10+6 31+23+19+31+23+19 | |
58 * AR_PLAY_HOLD 18+14+6+18+14+6 35+31+19+35+31+19 | |
59 * | |
60 * *down_state: A button with this feature has a pressed event and | |
61 * a released event, with which we can trace the state of the button. | |
62 * A button without this feature will only return one release event. | |
63 * | |
64 * hidden keys currently not implemented: | |
65 * hold for 5 secs | |
66 * MENU_NEXT_HOLD 15+14+6+15+14+6 | |
67 * MENU_PREV_HOLD 16+14+6+16+14+6 | |
68 * MENU_VUP_HOLD 20+14+6+20+14+6 | |
69 * MENU_VDOWN_HOLD 19+14+6+19+14+6 | |
70 * | |
71 * It seems that pressing 'menu' and 'play' on the Apple Remote for | |
72 * 5 seconds will trigger the make-pair function of the remote. | |
73 * MENU_PLAY_HOLD 21+15+14+6+15+14+6 | |
74 */ | |
75 | |
76 static const cookie_keycode_map_t ar_codes_tiger[] = { | |
77 { "\x0E\x06\x03\x02", 4, AR_PREV_HOLD }, | |
78 { "\x0E\x06\x04\x02", 4, AR_NEXT_HOLD }, | |
79 { "\x0E\x06\x0E\x06", 4, AR_MENU_HOLD }, | |
80 { "\x0E\x0C\x0B\x06", 4, AR_VUP }, | |
81 { "\x0E\x0D\x0B\x06", 4, AR_VDOWN }, | |
82 { "\x0E\x07\x06\x0E\x07\x06", 6, AR_MENU }, | |
83 { "\x0E\x08\x06\x0E\x08\x06", 6, AR_PLAY }, | |
84 { "\x0E\x09\x06\x0E\x09\x06", 6, AR_NEXT }, | |
85 { "\x0E\x0A\x06\x0E\x0A\x06", 6, AR_PREV }, | |
86 { "\x12\x0E\x06\x12\x0E\x06", 6, AR_PLAY_HOLD }, | |
87 { NULL, 0, MP_INPUT_NOTHING }, | |
88 }; | |
89 | |
90 static const cookie_keycode_map_t ar_codes_leopard[] = { | |
91 { "\x1F\x13\x03\x02", 4, AR_PREV_HOLD }, | |
92 { "\x1F\x13\x04\x02", 4, AR_NEXT_HOLD }, | |
93 { "\x1F\x13\x1F\x13", 4, AR_MENU_HOLD }, | |
94 { "\x1F\x1D\x1C\x13", 4, AR_VUP }, | |
95 { "\x1F\x1E\x1C\x13", 4, AR_VDOWN }, | |
96 { "\x1F\x14\x13\x1F\x14\x13", 6, AR_MENU }, | |
97 { "\x1F\x15\x13\x1F\x15\x13", 6, AR_PLAY }, | |
98 { "\x1F\x16\x13\x1F\x16\x13", 6, AR_NEXT }, | |
99 { "\x1F\x17\x13\x1F\x17\x13", 6, AR_PREV }, | |
100 { "\x23\x1F\x13\x23\x1F\x13", 6, AR_PLAY_HOLD }, | |
101 { NULL, 0, MP_INPUT_NOTHING }, | |
102 }; | |
103 | |
104 static int is_leopard; | |
105 static const cookie_keycode_map_t *ar_codes; | |
106 | |
107 static IOHIDQueueInterface **queue; | |
108 static IOHIDDeviceInterface **hidDeviceInterface = NULL; | |
25962 | 109 static int initialized = 0; |
24037 | 110 static int hidDeviceIsOpen; |
111 | |
112 /* Maximum number of elements in queue before oldest elements | |
113 in queue begin to be lost. Set to 16 to hold at least 2 events. */ | |
114 static const int MAX_QUEUE_SIZE = 16; | |
115 | |
116 | |
117 static int FindHIDDevices(mach_port_t masterPort, | |
118 io_iterator_t *hidObjectIterator) | |
119 { | |
120 CFMutableDictionaryRef hidMatchDictionary; | |
121 IOReturn ioReturnValue; | |
122 | |
123 // Set up a matching dictionary to search the I/O Registry | |
124 // by class name for all HID class devices. | |
125 hidMatchDictionary = IOServiceMatching("AppleIRController"); | |
126 | |
127 // Now search I/O Registry for matching devices. | |
128 ioReturnValue = IOServiceGetMatchingServices(masterPort, | |
129 hidMatchDictionary, | |
130 hidObjectIterator); | |
131 | |
132 // If search is unsuccessful, print message and hang. | |
133 if (ioReturnValue != kIOReturnSuccess || | |
134 !IOIteratorIsValid(*hidObjectIterator)) { | |
135 return -1; | |
136 } | |
137 return 0; | |
138 } | |
139 | |
140 static int getHIDCookies(IOHIDDeviceInterface122 **handle, | |
141 IOHIDElementCookie **cookies, | |
142 int *nr_cookies) | |
143 { | |
144 CFTypeRef object; | |
145 long number; | |
146 CFArrayRef elements; | |
147 CFDictionaryRef element; | |
148 CFIndex i; | |
149 | |
150 *nr_cookies = 0; | |
151 | |
152 if (!handle || !(*handle)) | |
153 return -1; | |
154 | |
155 // Copy all elements, since we're grabbing most of the elements | |
156 // for this device anyway, and thus, it's faster to iterate them | |
157 // ourselves. When grabbing only one or two elements, a matching | |
158 // dictionary should be passed in here instead of NULL. | |
159 if (((*handle)->copyMatchingElements(handle, NULL, &elements)) != kIOReturnSuccess) | |
160 return -1; | |
161 | |
162 // No elements, still a valid result. | |
163 if (CFArrayGetCount(elements)==0) | |
164 return 0; | |
165 | |
166 *cookies = calloc(CFArrayGetCount(elements), sizeof(IOHIDElementCookie)); | |
167 if (*cookies == NULL) | |
168 return -1; | |
169 | |
170 for (i=0; i<CFArrayGetCount(elements); i++) { | |
171 element = CFArrayGetValueAtIndex(elements, i); | |
172 | |
173 // Get cookie. | |
174 object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey)); | |
175 if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) | |
176 continue; | |
177 if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &number)) | |
178 continue; | |
179 (*cookies)[(*nr_cookies)++] = (IOHIDElementCookie)number; | |
180 } | |
181 | |
182 return 0; | |
183 } | |
184 | |
185 static int CreateHIDDeviceInterface(io_object_t hidDevice, | |
186 IOHIDDeviceInterface ***hidDeviceInterface) | |
187 { | |
188 io_name_t className; | |
189 IOCFPlugInInterface **plugInInterface = NULL; | |
190 SInt32 score = 0; | |
191 | |
192 if (IOObjectGetClass(hidDevice, className) != kIOReturnSuccess) | |
193 return -1; | |
194 | |
195 if (IOCreatePlugInInterfaceForService(hidDevice, | |
196 kIOHIDDeviceUserClientTypeID, | |
197 kIOCFPlugInInterfaceID, | |
198 &plugInInterface, | |
199 &score) != kIOReturnSuccess) | |
200 return -1; | |
201 | |
202 // Call a method of the intermediate plugin to create the device interface | |
203 if ((*plugInInterface)->QueryInterface(plugInInterface, | |
204 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), | |
205 (LPVOID)hidDeviceInterface) != S_OK | |
206 || *hidDeviceInterface == NULL || **hidDeviceInterface == NULL) { | |
207 (*plugInInterface)->Release(plugInInterface); | |
208 return -1; | |
209 } | |
210 | |
211 (*plugInInterface)->Release(plugInInterface); | |
212 | |
213 return 0; | |
214 } | |
215 | |
216 int mp_input_ar_init(void) | |
217 { | |
218 io_iterator_t hidObjectIterator; | |
219 io_object_t hidDevice; | |
220 int i; | |
221 IOHIDElementCookie *cookies = NULL; | |
222 int nr_cookies = 0; | |
223 | |
25962 | 224 if (initialized) |
24037 | 225 mp_input_ar_close(-1); |
226 | |
227 if (floor(NSAppKitVersionNumber) <= 824 /* NSAppKitVersionNumber10_4 */) { | |
228 ar_codes = &ar_codes_tiger[0]; | |
229 is_leopard = 0; | |
230 } | |
231 else { | |
232 ar_codes = &ar_codes_leopard[0]; | |
233 is_leopard = 1; | |
234 } | |
235 | |
236 if (FindHIDDevices(kIOMasterPortDefault, &hidObjectIterator)) | |
237 return -1; | |
238 | |
239 // Multiple controls could be found, we only use the first usable one. | |
240 while ((hidDevice = IOIteratorNext(hidObjectIterator))) { | |
241 if (CreateHIDDeviceInterface(hidDevice, &hidDeviceInterface) < 0) { | |
242 hidDeviceInterface = NULL; | |
243 IOObjectRelease(hidDevice); | |
244 continue; | |
245 } | |
246 if (getHIDCookies((IOHIDDeviceInterface122 **)hidDeviceInterface, | |
247 &cookies, | |
248 &nr_cookies) < 0) { | |
249 (*hidDeviceInterface)->Release(hidDeviceInterface); | |
250 hidDeviceInterface = NULL; | |
251 IOObjectRelease(hidDevice); | |
252 continue; | |
253 } | |
254 IOObjectRelease(hidDevice); | |
255 break; | |
256 } | |
257 if (hidDeviceInterface == NULL) | |
258 goto mp_input_ar_init_error; | |
259 | |
260 // Open the device. | |
261 if ((*hidDeviceInterface)->open(hidDeviceInterface, | |
262 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) | |
263 goto mp_input_ar_init_error; | |
264 hidDeviceIsOpen = 1; | |
265 | |
266 if ((queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface)) == NULL | |
267 || *queue == NULL) | |
268 goto mp_input_ar_init_error; | |
269 | |
270 // Create the queue. | |
271 (*queue)->create(queue, 0, MAX_QUEUE_SIZE); | |
272 | |
24042 | 273 // Add elements to the queue to make the queue work. |
274 // On tiger, it's a sequence from 1 to 21, | |
24037 | 275 // maybe it's the range of cookie values. |
276 for (i = 0;i < nr_cookies;i++) | |
277 (*queue)->addElement(queue, cookies[i], 0); | |
278 | |
279 // not used anymore | |
280 if (cookies != NULL) | |
281 free(cookies); | |
282 | |
283 // Start data delivery to the queue. | |
284 (*queue)->start(queue); | |
285 | |
286 // not useful anymore | |
287 IOObjectRelease(hidObjectIterator); | |
288 | |
25962 | 289 initialized = 1; |
24037 | 290 return 0; |
291 | |
292 mp_input_ar_init_error: | |
293 if (cookies != NULL) | |
294 free(cookies); | |
295 if (hidDeviceInterface != NULL) { | |
296 if (*hidDeviceInterface != NULL) { | |
297 (*hidDeviceInterface)->close(hidDeviceInterface); | |
298 (*hidDeviceInterface)->Release(hidDeviceInterface); | |
299 } | |
300 hidDeviceInterface = NULL; | |
301 } | |
302 IOObjectRelease(hidObjectIterator); | |
303 return -1; | |
304 } | |
305 | |
306 int is_mplayer_front() | |
307 { | |
308 ProcessSerialNumber myProc, frProc; | |
309 Boolean sameProc; | |
310 pid_t parentPID; | |
311 | |
312 if (GetFrontProcess(&frProc) == noErr | |
313 && GetCurrentProcess(&myProc) == noErr | |
314 && SameProcess(&frProc, &myProc, &sameProc) == noErr) { | |
315 if (sameProc) | |
316 return 1; | |
317 // If MPlayer is running in slave mode, also check parent process. | |
318 if (slave_mode && GetProcessPID(&frProc, &parentPID) == noErr) | |
319 return parentPID==getppid(); | |
320 } | |
321 return 0; | |
322 } | |
323 | |
324 int mp_input_ar_read(int fd) | |
325 { | |
326 int i, down = 0; | |
327 int ret = MP_INPUT_NOTHING; | |
328 AbsoluteTime zeroTime = {0,0}; | |
329 IOHIDEventStruct event; | |
330 static int prev_event = 0; | |
331 IOReturn result = kIOReturnSuccess; | |
332 | |
333 const cookie_keycode_map_t *ar_code; | |
334 int cookie_nr = 0; | |
335 char cookie_queue[MAX_QUEUE_SIZE]; | |
336 int value_queue[MAX_QUEUE_SIZE]; | |
337 | |
25962 | 338 if (initialized == 0) |
24037 | 339 return MP_INPUT_NOTHING; |
340 | |
341 while ((result = (*queue)->getNextEvent(queue, &event, zeroTime, 0)) == kIOReturnSuccess) { | |
342 #ifdef TEST | |
343 printf(" - event cookie: %d, value: %d, long value: %d\n", | |
344 (int)event.elementCookie, (int)event.value, (int)event.longValue); | |
345 #endif | |
346 // Shorten cookie sequence by removing cookies value 5 and 18, | |
347 // since 5 always follows 6 (on tiger), 18 follows 19 (on leopard). | |
348 if ((int)event.elementCookie == 5 | |
349 || ((int)event.elementCookie == 18 && is_leopard)) | |
350 continue; | |
351 // Check valid cookie range. | |
352 if ((int)event.elementCookie > 35 || (int)event.elementCookie < 2) { | |
353 cookie_nr = 0; | |
354 continue; | |
355 } | |
356 cookie_queue[cookie_nr] = (char)(int)event.elementCookie; | |
357 value_queue[cookie_nr++] = event.value; | |
358 // 4 cookies are necessary to make up a valid sequence. | |
359 if (cookie_nr>=4) { | |
360 // Find matching sequence. | |
361 ar_code = ar_codes; | |
362 while (ar_code->cookies != NULL && | |
363 (cookie_nr < ar_code->seq_len || | |
364 0 != memcmp(ar_code->cookies, | |
365 &cookie_queue[cookie_nr-ar_code->seq_len], | |
366 ar_code->seq_len))) | |
367 ++ar_code; | |
368 if (ar_code->cookies != NULL) { | |
369 ret = ar_code->keycode; | |
370 switch (ret) { | |
371 // For these 4 keys, the remote can keep a hold state. | |
372 case AR_VUP: | |
373 case AR_VDOWN: | |
374 case AR_NEXT_HOLD: | |
375 case AR_PREV_HOLD: | |
376 for (i = cookie_nr-ar_code->seq_len; i < cookie_nr; ++i) { | |
377 if (value_queue[i]) { | |
378 down = MP_KEY_DOWN; | |
379 break; | |
380 } | |
381 } | |
382 break; | |
383 default: | |
384 down = 0; | |
385 } | |
386 } | |
387 } | |
388 } | |
389 | |
390 if (!is_mplayer_front()) { | |
391 if (hidDeviceIsOpen) { | |
392 (*hidDeviceInterface)->close(hidDeviceInterface); | |
393 hidDeviceIsOpen = 0; | |
394 | |
24643 | 395 // Read out all pending events. |
24037 | 396 while (result == kIOReturnSuccess) |
397 result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); | |
398 } | |
399 return MP_INPUT_NOTHING; | |
400 } | |
401 // If we are switched from running as a foreground process to a | |
402 // background process and back again, re-open the device to make | |
403 // sure we are not affected by the system or other applications | |
404 // using the Apple Remote. | |
405 else if (!hidDeviceIsOpen) { | |
406 if ((*hidDeviceInterface)->open(hidDeviceInterface, | |
407 kIOHIDOptionsTypeSeizeDevice) == kIOReturnSuccess) | |
408 hidDeviceIsOpen = 1; | |
409 } | |
410 | |
411 if (ret > 0) | |
412 prev_event = ret; | |
413 return ret | down; | |
414 } | |
415 | |
416 void mp_input_ar_close(int fd) | |
417 { | |
25962 | 418 if (initialized == 0) |
24037 | 419 return; |
420 | |
421 // Close the device. | |
422 (*hidDeviceInterface)->close(hidDeviceInterface); | |
423 | |
424 // Stop data delivery to queue. | |
425 (*queue)->stop(queue); | |
426 // Dispose of queue. | |
427 (*queue)->dispose(queue); | |
428 // Release the queue we allocated. | |
429 (*queue)->Release(queue); | |
430 | |
431 // Release the interface. | |
432 (*hidDeviceInterface)->Release(hidDeviceInterface); | |
433 | |
25962 | 434 initialized = 0; |
24037 | 435 } |
436 | |
437 #ifdef TEST | |
438 int main(void) | |
439 { | |
440 int ret; | |
441 | |
442 if (mp_input_ar_init() < 0) { | |
443 printf("Unable to initialize Apple Remote.\n"); | |
444 return 1; | |
445 } | |
446 | |
447 while (1) { | |
448 switch ((ret = mp_input_ar_read(0)) & ~MP_KEY_DOWN) { | |
449 case AR_PLAY: printf(" - AR_PLAY."); break; | |
450 case AR_PLAY_HOLD: printf(" - AR_PLAY_HOLD."); break; | |
451 case AR_NEXT: printf(" - AR_NEXT."); break; | |
452 case AR_NEXT_HOLD: printf(" - AR_NEXT_HOLD."); break; | |
453 case AR_PREV: printf(" - AR_PREV."); break; | |
454 case AR_PREV_HOLD: printf(" - AR_PREV_HOLD."); break; | |
455 case AR_MENU: printf(" - AR_MENU."); break; | |
456 case AR_MENU_HOLD: printf(" - AR_MENU_HOLD."); break; | |
457 case AR_VUP: printf(" - AR_VUP."); break; | |
458 case AR_VDOWN: printf(" - AR_VDOWN."); break; | |
459 } | |
460 if ((ret > 0 )&&(ret & MP_KEY_DOWN)) | |
461 printf(" [hold]"); | |
462 if (ret > 0) | |
463 printf("\n"); | |
464 } | |
465 | |
466 mp_input_ar_close(0); | |
467 return 0; | |
468 } | |
469 #endif |