Mercurial > mplayer.hg
annotate input/ar.c @ 36607:6b17e4932555
Improve the dialog for font encodings.
In case iconv is available, add only available encodings from the
full list to avoid possible confusion.
In any case (and especially if iconv isn't available) add UNICODE,
since this is internally provided by MPlayer.
author | ib |
---|---|
date | Thu, 23 Jan 2014 12:09:37 +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 |