Mercurial > mplayer.hg
annotate input/ar.c @ 34335:c5f6bc8ae2b4
Use an enum to define keycodes and fix CTRL and backspace key having
the same keycode.
This is because KEY_CTRL was supposed to be the name of the "key class",
not the key, but that was later misunderstood.
Obviously that was some major bad naming....
author | reimar |
---|---|
date | Sat, 10 Dec 2011 14:27:04 +0000 |
parents | 8fa2f43cb760 |
children |
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 | |
32537
8fa2f43cb760
Remove most of the NULL pointer check before free all over the code
cboesch
parents:
32069
diff
changeset
|
279 free(cookies); |
24037 | 280 |
281 // Start data delivery to the queue. | |
282 (*queue)->start(queue); | |
283 | |
284 // not useful anymore | |
285 IOObjectRelease(hidObjectIterator); | |
286 | |
25962 | 287 initialized = 1; |
24037 | 288 return 0; |
289 | |
290 mp_input_ar_init_error: | |
32537
8fa2f43cb760
Remove most of the NULL pointer check before free all over the code
cboesch
parents:
32069
diff
changeset
|
291 free(cookies); |
24037 | 292 if (hidDeviceInterface != NULL) { |
293 if (*hidDeviceInterface != NULL) { | |
294 (*hidDeviceInterface)->close(hidDeviceInterface); | |
295 (*hidDeviceInterface)->Release(hidDeviceInterface); | |
296 } | |
297 hidDeviceInterface = NULL; | |
298 } | |
299 IOObjectRelease(hidObjectIterator); | |
300 return -1; | |
301 } | |
302 | |
32069
ca56fb28bfa2
Mark is_mplayer_front() as static, it is only used within the file; fixes:
diego
parents:
32054
diff
changeset
|
303 static int is_mplayer_front(void) |
24037 | 304 { |
305 ProcessSerialNumber myProc, frProc; | |
306 Boolean sameProc; | |
307 pid_t parentPID; | |
308 | |
309 if (GetFrontProcess(&frProc) == noErr | |
310 && GetCurrentProcess(&myProc) == noErr | |
311 && SameProcess(&frProc, &myProc, &sameProc) == noErr) { | |
312 if (sameProc) | |
313 return 1; | |
314 // If MPlayer is running in slave mode, also check parent process. | |
315 if (slave_mode && GetProcessPID(&frProc, &parentPID) == noErr) | |
316 return parentPID==getppid(); | |
317 } | |
318 return 0; | |
319 } | |
320 | |
321 int mp_input_ar_read(int fd) | |
322 { | |
323 int i, down = 0; | |
324 int ret = MP_INPUT_NOTHING; | |
325 AbsoluteTime zeroTime = {0,0}; | |
326 IOHIDEventStruct event; | |
327 static int prev_event = 0; | |
328 IOReturn result = kIOReturnSuccess; | |
329 | |
330 const cookie_keycode_map_t *ar_code; | |
331 int cookie_nr = 0; | |
332 char cookie_queue[MAX_QUEUE_SIZE]; | |
333 int value_queue[MAX_QUEUE_SIZE]; | |
334 | |
25962 | 335 if (initialized == 0) |
24037 | 336 return MP_INPUT_NOTHING; |
337 | |
338 while ((result = (*queue)->getNextEvent(queue, &event, zeroTime, 0)) == kIOReturnSuccess) { | |
339 #ifdef TEST | |
340 printf(" - event cookie: %d, value: %d, long value: %d\n", | |
341 (int)event.elementCookie, (int)event.value, (int)event.longValue); | |
342 #endif | |
343 // Shorten cookie sequence by removing cookies value 5 and 18, | |
344 // since 5 always follows 6 (on tiger), 18 follows 19 (on leopard). | |
345 if ((int)event.elementCookie == 5 | |
346 || ((int)event.elementCookie == 18 && is_leopard)) | |
347 continue; | |
348 // Check valid cookie range. | |
349 if ((int)event.elementCookie > 35 || (int)event.elementCookie < 2) { | |
350 cookie_nr = 0; | |
351 continue; | |
352 } | |
353 cookie_queue[cookie_nr] = (char)(int)event.elementCookie; | |
354 value_queue[cookie_nr++] = event.value; | |
355 // 4 cookies are necessary to make up a valid sequence. | |
356 if (cookie_nr>=4) { | |
357 // Find matching sequence. | |
358 ar_code = ar_codes; | |
359 while (ar_code->cookies != NULL && | |
360 (cookie_nr < ar_code->seq_len || | |
361 0 != memcmp(ar_code->cookies, | |
362 &cookie_queue[cookie_nr-ar_code->seq_len], | |
363 ar_code->seq_len))) | |
364 ++ar_code; | |
365 if (ar_code->cookies != NULL) { | |
366 ret = ar_code->keycode; | |
367 switch (ret) { | |
368 // For these 4 keys, the remote can keep a hold state. | |
369 case AR_VUP: | |
370 case AR_VDOWN: | |
371 case AR_NEXT_HOLD: | |
372 case AR_PREV_HOLD: | |
373 for (i = cookie_nr-ar_code->seq_len; i < cookie_nr; ++i) { | |
374 if (value_queue[i]) { | |
375 down = MP_KEY_DOWN; | |
376 break; | |
377 } | |
378 } | |
379 break; | |
380 default: | |
381 down = 0; | |
382 } | |
383 } | |
384 } | |
385 } | |
386 | |
387 if (!is_mplayer_front()) { | |
388 if (hidDeviceIsOpen) { | |
389 (*hidDeviceInterface)->close(hidDeviceInterface); | |
390 hidDeviceIsOpen = 0; | |
391 | |
24643 | 392 // Read out all pending events. |
24037 | 393 while (result == kIOReturnSuccess) |
394 result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); | |
395 } | |
396 return MP_INPUT_NOTHING; | |
397 } | |
398 // If we are switched from running as a foreground process to a | |
399 // background process and back again, re-open the device to make | |
400 // sure we are not affected by the system or other applications | |
401 // using the Apple Remote. | |
402 else if (!hidDeviceIsOpen) { | |
403 if ((*hidDeviceInterface)->open(hidDeviceInterface, | |
404 kIOHIDOptionsTypeSeizeDevice) == kIOReturnSuccess) | |
405 hidDeviceIsOpen = 1; | |
406 } | |
407 | |
408 if (ret > 0) | |
409 prev_event = ret; | |
410 return ret | down; | |
411 } | |
412 | |
413 void mp_input_ar_close(int fd) | |
414 { | |
25962 | 415 if (initialized == 0) |
24037 | 416 return; |
417 | |
418 // Close the device. | |
419 (*hidDeviceInterface)->close(hidDeviceInterface); | |
420 | |
421 // Stop data delivery to queue. | |
422 (*queue)->stop(queue); | |
423 // Dispose of queue. | |
424 (*queue)->dispose(queue); | |
425 // Release the queue we allocated. | |
426 (*queue)->Release(queue); | |
427 | |
428 // Release the interface. | |
429 (*hidDeviceInterface)->Release(hidDeviceInterface); | |
430 | |
25962 | 431 initialized = 0; |
24037 | 432 } |
433 | |
434 #ifdef TEST | |
435 int main(void) | |
436 { | |
437 int ret; | |
438 | |
439 if (mp_input_ar_init() < 0) { | |
440 printf("Unable to initialize Apple Remote.\n"); | |
441 return 1; | |
442 } | |
443 | |
444 while (1) { | |
445 switch ((ret = mp_input_ar_read(0)) & ~MP_KEY_DOWN) { | |
446 case AR_PLAY: printf(" - AR_PLAY."); break; | |
447 case AR_PLAY_HOLD: printf(" - AR_PLAY_HOLD."); break; | |
448 case AR_NEXT: printf(" - AR_NEXT."); break; | |
449 case AR_NEXT_HOLD: printf(" - AR_NEXT_HOLD."); break; | |
450 case AR_PREV: printf(" - AR_PREV."); break; | |
451 case AR_PREV_HOLD: printf(" - AR_PREV_HOLD."); break; | |
452 case AR_MENU: printf(" - AR_MENU."); break; | |
453 case AR_MENU_HOLD: printf(" - AR_MENU_HOLD."); break; | |
454 case AR_VUP: printf(" - AR_VUP."); break; | |
455 case AR_VDOWN: printf(" - AR_VDOWN."); break; | |
456 } | |
457 if ((ret > 0 )&&(ret & MP_KEY_DOWN)) | |
458 printf(" [hold]"); | |
459 if (ret > 0) | |
460 printf("\n"); | |
461 } | |
462 | |
463 mp_input_ar_close(0); | |
464 return 0; | |
465 } | |
466 #endif |