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