comparison input/ar.c @ 24037:c7f95196dfe5

Apple Remote support patch by Zoltan Ponekker, pontscho kac.poliod hu cleaned up by Ulion, ulion2002 gmail com with some help by Reimar and me
author diego
date Tue, 14 Aug 2007 14:29:22 +0000
parents
children 0354fab1e257
comparison
equal deleted inserted replaced
24036:698aa9b7a44c 24037:c7f95196dfe5
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 *
20 * You should have received a copy of the GNU General Public License
21 * along with MPlayer; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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;
109 static int inited = 0;
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
224 if (inited)
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
273 // Add elements to the queue to make the queue work
274 // on tiger. It's a sequence from 1 to 21,
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
289 inited = 1;
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
338 if (inited == 0)
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
395 // read out all pending events
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 {
418 if (inited == 0)
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
434 inited = 0;
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