Mercurial > mplayer.hg
annotate input/ar.c @ 29130:33956c5f5005
Reemit the ID_AID_x_LANG for the track. This allows the identification of the
audio track by language code (en or es) rather than by ID (128 or 129).
patch by Kevin DeKorte, kdekorte gmail com
author | diego |
---|---|
date | Sat, 11 Apr 2009 13:51:02 +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 |