diff options
Diffstat (limited to 'core/input')
-rw-r--r-- | core/input/appleir.c | 155 | ||||
-rw-r--r-- | core/input/ar.c | 469 | ||||
-rw-r--r-- | core/input/ar.h | 35 | ||||
-rw-r--r-- | core/input/input.c | 1959 | ||||
-rw-r--r-- | core/input/input.h | 219 | ||||
-rw-r--r-- | core/input/joystick.c | 162 | ||||
-rw-r--r-- | core/input/joystick.h | 26 | ||||
-rw-r--r-- | core/input/keycodes.h | 214 | ||||
-rw-r--r-- | core/input/lirc.c | 123 | ||||
-rw-r--r-- | core/input/lirc.h | 30 |
10 files changed, 3392 insertions, 0 deletions
diff --git a/core/input/appleir.c b/core/input/appleir.c new file mode 100644 index 0000000000..c64bc9648d --- /dev/null +++ b/core/input/appleir.c @@ -0,0 +1,155 @@ +/* + * Linux Apple IR Remote input interface + * + * Copyright (C) 2008 Benjamin Zores <ben at geexbox dot org> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include "ar.h" +#include "input.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <linux/types.h> +#include <linux/input.h> + +#include "mp_msg.h" + +// keycodes.h defines would conflict with linux/input.h ones +#define AR_DEFINES_ONLY +#include "keycodes.h" + +#define EVDEV_MAX_EVENTS 32 + +/* ripped from AppleIR driver */ +#define USB_VENDOR_APPLE 0x05ac +#define USB_DEV_APPLE_IR 0x8240 +#define USB_DEV_APPLE_IR_2 0x8242 + +/* Apple IR Remote evdev mapping */ +#define APPLE_IR_MINUS KEY_VOLUMEDOWN +#define APPLE_IR_PLUS KEY_VOLUMEUP +#define APPLE_IR_MENU KEY_MENU +#define APPLE_IR_FORWARD KEY_NEXTSONG +#define APPLE_IR_PLAY KEY_PLAYPAUSE +#define APPLE_IR_BACKWARD KEY_PREVIOUSSONG + +static const struct { + int linux_keycode; + int value; + int mp_keycode; +} apple_ir_mapping[] = { + { APPLE_IR_PLAY, 1, AR_PLAY }, + { APPLE_IR_PLAY, 2, AR_PLAY_HOLD }, + { APPLE_IR_FORWARD, 1, AR_NEXT }, + { APPLE_IR_FORWARD, 2, AR_NEXT_HOLD }, + { APPLE_IR_BACKWARD, 1, AR_PREV }, + { APPLE_IR_BACKWARD, 2, AR_PREV_HOLD }, + { APPLE_IR_MENU, 1, AR_MENU }, + { APPLE_IR_MENU, 2, AR_MENU_HOLD }, + { APPLE_IR_PLUS, 1, AR_VUP }, + { APPLE_IR_MINUS, 1, AR_VDOWN }, + { -1, -1, -1 } +}; + +int mp_input_appleir_init (char *dev) +{ + int i, fd; + + if (dev) + { + mp_tmsg (MSGT_INPUT, MSGL_V, "Initializing Apple IR on %s\n", dev); + fd = open (dev, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + mp_tmsg (MSGT_INPUT, MSGL_ERR, + "Can't open Apple IR device: %s\n", strerror (errno)); + return -1; + } + + return fd; + } + else + { + /* look for a valid AppleIR device on system */ + for (i = 0; i < EVDEV_MAX_EVENTS; i++) + { + struct input_id id; + char file[64]; + + sprintf (file, "/dev/input/event%d", i); + fd = open (file, O_RDONLY | O_NONBLOCK); + if (fd < 0) + continue; + + ioctl (fd, EVIOCGID, &id); + if (id.bustype == BUS_USB && + id.vendor == USB_VENDOR_APPLE && + (id.product == USB_DEV_APPLE_IR ||id.product == USB_DEV_APPLE_IR_2)) + { + mp_tmsg (MSGT_INPUT, MSGL_V, "Detected Apple IR on %s\n", file); + return fd; + } + close (fd); + } + + mp_tmsg (MSGT_INPUT, MSGL_ERR, + "Can't open Apple IR device: %s\n", strerror (errno)); + } + + return -1; +} + +int mp_input_appleir_read(void *ctx, int fd) +{ + struct input_event ev; + int i, r; + + r = read (fd, &ev, sizeof (struct input_event)); + if (r <= 0 || r < sizeof (struct input_event)) + return MP_INPUT_NOTHING; + + /* check for key press only */ + if (ev.type != EV_KEY) + return MP_INPUT_NOTHING; + + /* EvDev Key values: + * 0: key release + * 1: key press + * 2: key auto-repeat + */ + if (ev.value == 0) + return MP_INPUT_NOTHING; + + /* find Linux evdev -> MPlayer keycode mapping */ + for (i = 0; apple_ir_mapping[i].linux_keycode != -1; i++) + if (apple_ir_mapping[i].linux_keycode == ev.code && + apple_ir_mapping[i].value == ev.value) + return apple_ir_mapping[i].mp_keycode; + + return MP_INPUT_NOTHING; +} diff --git a/core/input/ar.c b/core/input/ar.c new file mode 100644 index 0000000000..7b7cc8e3db --- /dev/null +++ b/core/input/ar.c @@ -0,0 +1,469 @@ +/* + * Apple Remote input interface + * + * Copyright (C) 2007 Zoltan Ponekker <pontscho at kac.poliod.hu> + * + * (modified a bit by Ulion <ulion2002 at gmail.com>) + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/hid/IOHIDLib.h> +#include <Carbon/Carbon.h> + +#include <sys/types.h> +#include <unistd.h> + +#include "input.h" +#include "ar.h" +#include "keycodes.h" + +extern int slave_mode; + +extern const double NSAppKitVersionNumber; + +typedef struct cookie_keycode_map { + char *cookies; + int seq_len; + int keycode; +} cookie_keycode_map_t; + +/* On tiger, 5 always follows 6; on leopard, 18 always follows 19. + * On leopard, there seems to be no cookie value of 5 or 6. + * Following is the shortened cookie sequence list + * keycode cookies_on_tiger cookies_on_leopard *down_state + * AR_PREV_HOLD 14+6+3+2 31+19+3+2 yes + * AR_NEXT_HOLD 14+6+4+2 31+19+4+2 yes + * AR_MENU_HOLD 14+6+14+6 31+19+31+19 + * AR_VUP 14+12+11+6 31+29+28+19 yes + * AR_VDOWN 14+13+11+6 31+30+28+19 yes + * AR_MENU 14+7+6+14+7+6 31+20+19+31+20+19 + * AR_PLAY 14+8+6+14+8+6 31+21+19+31+21+19 + * AR_NEXT 14+9+6+14+9+6 31+22+19+31+22+19 + * AR_PREV 14+10+6+14+10+6 31+23+19+31+23+19 + * AR_PLAY_HOLD 18+14+6+18+14+6 35+31+19+35+31+19 + * + * *down_state: A button with this feature has a pressed event and + * a released event, with which we can trace the state of the button. + * A button without this feature will only return one release event. + * + * hidden keys currently not implemented: + * hold for 5 secs + * MENU_NEXT_HOLD 15+14+6+15+14+6 + * MENU_PREV_HOLD 16+14+6+16+14+6 + * MENU_VUP_HOLD 20+14+6+20+14+6 + * MENU_VDOWN_HOLD 19+14+6+19+14+6 + * + * It seems that pressing 'menu' and 'play' on the Apple Remote for + * 5 seconds will trigger the make-pair function of the remote. + * MENU_PLAY_HOLD 21+15+14+6+15+14+6 + */ + +static const cookie_keycode_map_t ar_codes_tiger[] = { + { "\x0E\x06\x03\x02", 4, AR_PREV_HOLD }, + { "\x0E\x06\x04\x02", 4, AR_NEXT_HOLD }, + { "\x0E\x06\x0E\x06", 4, AR_MENU_HOLD }, + { "\x0E\x0C\x0B\x06", 4, AR_VUP }, + { "\x0E\x0D\x0B\x06", 4, AR_VDOWN }, + { "\x0E\x07\x06\x0E\x07\x06", 6, AR_MENU }, + { "\x0E\x08\x06\x0E\x08\x06", 6, AR_PLAY }, + { "\x0E\x09\x06\x0E\x09\x06", 6, AR_NEXT }, + { "\x0E\x0A\x06\x0E\x0A\x06", 6, AR_PREV }, + { "\x12\x0E\x06\x12\x0E\x06", 6, AR_PLAY_HOLD }, + { NULL, 0, MP_INPUT_NOTHING }, +}; + +static const cookie_keycode_map_t ar_codes_leopard[] = { + { "\x1F\x13\x03\x02", 4, AR_PREV_HOLD }, + { "\x1F\x13\x04\x02", 4, AR_NEXT_HOLD }, + { "\x1F\x13\x1F\x13", 4, AR_MENU_HOLD }, + { "\x1F\x1D\x1C\x13", 4, AR_VUP }, + { "\x1F\x1E\x1C\x13", 4, AR_VDOWN }, + { "\x1F\x14\x13\x1F\x14\x13", 6, AR_MENU }, + { "\x1F\x15\x13\x1F\x15\x13", 6, AR_PLAY }, + { "\x1F\x16\x13\x1F\x16\x13", 6, AR_NEXT }, + { "\x1F\x17\x13\x1F\x17\x13", 6, AR_PREV }, + { "\x23\x1F\x13\x23\x1F\x13", 6, AR_PLAY_HOLD }, + { NULL, 0, MP_INPUT_NOTHING }, +}; + +static int is_leopard; +static const cookie_keycode_map_t *ar_codes; + +static IOHIDQueueInterface **queue; +static IOHIDDeviceInterface **hidDeviceInterface = NULL; +static int initialized = 0; +static int hidDeviceIsOpen; + +/* Maximum number of elements in queue before oldest elements + in queue begin to be lost. Set to 16 to hold at least 2 events. */ +static const int MAX_QUEUE_SIZE = 16; + + +static int FindHIDDevices(mach_port_t masterPort, + io_iterator_t *hidObjectIterator) +{ + CFMutableDictionaryRef hidMatchDictionary; + IOReturn ioReturnValue; + + // Set up a matching dictionary to search the I/O Registry + // by class name for all HID class devices. + hidMatchDictionary = IOServiceMatching("AppleIRController"); + + // Now search I/O Registry for matching devices. + ioReturnValue = IOServiceGetMatchingServices(masterPort, + hidMatchDictionary, + hidObjectIterator); + + // If search is unsuccessful, print message and hang. + if (ioReturnValue != kIOReturnSuccess || + !IOIteratorIsValid(*hidObjectIterator)) { + return -1; + } + return 0; +} + +static int getHIDCookies(IOHIDDeviceInterface122 **handle, + IOHIDElementCookie **cookies, + int *nr_cookies) +{ + CFTypeRef object; + long number; + CFArrayRef elements; + CFDictionaryRef element; + CFIndex i; + + *nr_cookies = 0; + + if (!handle || !(*handle)) + return -1; + + // Copy all elements, since we're grabbing most of the elements + // for this device anyway, and thus, it's faster to iterate them + // ourselves. When grabbing only one or two elements, a matching + // dictionary should be passed in here instead of NULL. + if (((*handle)->copyMatchingElements(handle, NULL, &elements)) != kIOReturnSuccess) + return -1; + + // No elements, still a valid result. + if (CFArrayGetCount(elements)==0) + return 0; + + *cookies = calloc(CFArrayGetCount(elements), sizeof(IOHIDElementCookie)); + if (*cookies == NULL) + return -1; + + for (i=0; i<CFArrayGetCount(elements); i++) { + element = CFArrayGetValueAtIndex(elements, i); + + // Get cookie. + object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey)); + if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) + continue; + if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &number)) + continue; + (*cookies)[(*nr_cookies)++] = (IOHIDElementCookie)number; + } + + return 0; +} + +static int CreateHIDDeviceInterface(io_object_t hidDevice, + IOHIDDeviceInterface ***hidDeviceInterface) +{ + io_name_t className; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score = 0; + + if (IOObjectGetClass(hidDevice, className) != kIOReturnSuccess) + return -1; + + if (IOCreatePlugInInterfaceForService(hidDevice, + kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, + &plugInInterface, + &score) != kIOReturnSuccess) + return -1; + + // Call a method of the intermediate plugin to create the device interface + if ((*plugInInterface)->QueryInterface(plugInInterface, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), + (LPVOID)hidDeviceInterface) != S_OK + || *hidDeviceInterface == NULL || **hidDeviceInterface == NULL) { + (*plugInInterface)->Release(plugInInterface); + return -1; + } + + (*plugInInterface)->Release(plugInInterface); + + return 0; +} + +int mp_input_ar_init(void) +{ + io_iterator_t hidObjectIterator; + io_object_t hidDevice; + int i; + IOHIDElementCookie *cookies = NULL; + int nr_cookies = 0; + + if (initialized) + mp_input_ar_close(-1); + + if (floor(NSAppKitVersionNumber) <= 824 /* NSAppKitVersionNumber10_4 */) { + ar_codes = &ar_codes_tiger[0]; + is_leopard = 0; + } + else { + ar_codes = &ar_codes_leopard[0]; + is_leopard = 1; + } + + if (FindHIDDevices(kIOMasterPortDefault, &hidObjectIterator)) + return -1; + + // Multiple controls could be found, we only use the first usable one. + while ((hidDevice = IOIteratorNext(hidObjectIterator))) { + if (CreateHIDDeviceInterface(hidDevice, &hidDeviceInterface) < 0) { + hidDeviceInterface = NULL; + IOObjectRelease(hidDevice); + continue; + } + if (getHIDCookies((IOHIDDeviceInterface122 **)hidDeviceInterface, + &cookies, + &nr_cookies) < 0) { + (*hidDeviceInterface)->Release(hidDeviceInterface); + hidDeviceInterface = NULL; + IOObjectRelease(hidDevice); + continue; + } + IOObjectRelease(hidDevice); + break; + } + if (hidDeviceInterface == NULL) + goto mp_input_ar_init_error; + + // Open the device. + if ((*hidDeviceInterface)->open(hidDeviceInterface, + kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) + goto mp_input_ar_init_error; + hidDeviceIsOpen = 1; + + if ((queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface)) == NULL + || *queue == NULL) + goto mp_input_ar_init_error; + + // Create the queue. + (*queue)->create(queue, 0, MAX_QUEUE_SIZE); + + // Add elements to the queue to make the queue work. + // On tiger, it's a sequence from 1 to 21, + // maybe it's the range of cookie values. + for (i = 0;i < nr_cookies;i++) + (*queue)->addElement(queue, cookies[i], 0); + + // not used anymore + free(cookies); + + // Start data delivery to the queue. + (*queue)->start(queue); + + // not useful anymore + IOObjectRelease(hidObjectIterator); + + initialized = 1; + return 0; + +mp_input_ar_init_error: + free(cookies); + if (hidDeviceInterface != NULL) { + if (*hidDeviceInterface != NULL) { + (*hidDeviceInterface)->close(hidDeviceInterface); + (*hidDeviceInterface)->Release(hidDeviceInterface); + } + hidDeviceInterface = NULL; + } + IOObjectRelease(hidObjectIterator); + return -1; +} + +static int is_mplayer_front(void) +{ + ProcessSerialNumber myProc, frProc; + Boolean sameProc; + pid_t parentPID; + + if (GetFrontProcess(&frProc) == noErr + && GetCurrentProcess(&myProc) == noErr + && SameProcess(&frProc, &myProc, &sameProc) == noErr) { + if (sameProc) + return 1; + // If MPlayer is running in slave mode, also check parent process. + if (slave_mode && GetProcessPID(&frProc, &parentPID) == noErr) + return parentPID==getppid(); + } + return 0; +} + +int mp_input_ar_read(void *ctx, int fd) +{ + int i, down = 0; + int ret = MP_INPUT_NOTHING; + AbsoluteTime zeroTime = {0,0}; + IOHIDEventStruct event; + static int prev_event = 0; + IOReturn result = kIOReturnSuccess; + + const cookie_keycode_map_t *ar_code; + int cookie_nr = 0; + char cookie_queue[MAX_QUEUE_SIZE]; + int value_queue[MAX_QUEUE_SIZE]; + + if (initialized == 0) + return MP_INPUT_NOTHING; + + while ((result = (*queue)->getNextEvent(queue, &event, zeroTime, 0)) == kIOReturnSuccess) { +#ifdef TEST + printf(" - event cookie: %d, value: %d, long value: %d\n", + (int)event.elementCookie, (int)event.value, (int)event.longValue); +#endif + // Shorten cookie sequence by removing cookies value 5 and 18, + // since 5 always follows 6 (on tiger), 18 follows 19 (on leopard). + if ((int)event.elementCookie == 5 + || ((int)event.elementCookie == 18 && is_leopard)) + continue; + // Check valid cookie range. + if ((int)event.elementCookie > 35 || (int)event.elementCookie < 2) { + cookie_nr = 0; + continue; + } + cookie_queue[cookie_nr] = (char)(int)event.elementCookie; + value_queue[cookie_nr++] = event.value; + // 4 cookies are necessary to make up a valid sequence. + if (cookie_nr>=4) { + // Find matching sequence. + ar_code = ar_codes; + while (ar_code->cookies != NULL && + (cookie_nr < ar_code->seq_len || + 0 != memcmp(ar_code->cookies, + &cookie_queue[cookie_nr-ar_code->seq_len], + ar_code->seq_len))) + ++ar_code; + if (ar_code->cookies != NULL) { + ret = ar_code->keycode; + switch (ret) { + // For these 4 keys, the remote can keep a hold state. + case AR_VUP: + case AR_VDOWN: + case AR_NEXT_HOLD: + case AR_PREV_HOLD: + for (i = cookie_nr-ar_code->seq_len; i < cookie_nr; ++i) { + if (value_queue[i]) { + down = MP_KEY_DOWN; + break; + } + } + break; + default: + down = 0; + } + } + } + } + + if (!is_mplayer_front()) { + if (hidDeviceIsOpen) { + (*hidDeviceInterface)->close(hidDeviceInterface); + hidDeviceIsOpen = 0; + + // Read out all pending events. + while (result == kIOReturnSuccess) + result = (*queue)->getNextEvent(queue, &event, zeroTime, 0); + } + return MP_INPUT_NOTHING; + } + // If we are switched from running as a foreground process to a + // background process and back again, re-open the device to make + // sure we are not affected by the system or other applications + // using the Apple Remote. + else if (!hidDeviceIsOpen) { + if ((*hidDeviceInterface)->open(hidDeviceInterface, + kIOHIDOptionsTypeSeizeDevice) == kIOReturnSuccess) + hidDeviceIsOpen = 1; + } + + if (ret > 0) + prev_event = ret; + return ret | down; +} + +int mp_input_ar_close(int fd) +{ + if (initialized == 0) + return 0; + + // Close the device. + (*hidDeviceInterface)->close(hidDeviceInterface); + + // Stop data delivery to queue. + (*queue)->stop(queue); + // Dispose of queue. + (*queue)->dispose(queue); + // Release the queue we allocated. + (*queue)->Release(queue); + + // Release the interface. + (*hidDeviceInterface)->Release(hidDeviceInterface); + + initialized = 0; + return 0; +} + +#ifdef TEST +int main(void) +{ + int ret; + + if (mp_input_ar_init() < 0) { + printf("Unable to initialize Apple Remote.\n"); + return 1; + } + + while (1) { + switch ((ret = mp_input_ar_read(NULL, 0)) & ~MP_KEY_DOWN) { + case AR_PLAY: printf(" - AR_PLAY."); break; + case AR_PLAY_HOLD: printf(" - AR_PLAY_HOLD."); break; + case AR_NEXT: printf(" - AR_NEXT."); break; + case AR_NEXT_HOLD: printf(" - AR_NEXT_HOLD."); break; + case AR_PREV: printf(" - AR_PREV."); break; + case AR_PREV_HOLD: printf(" - AR_PREV_HOLD."); break; + case AR_MENU: printf(" - AR_MENU."); break; + case AR_MENU_HOLD: printf(" - AR_MENU_HOLD."); break; + case AR_VUP: printf(" - AR_VUP."); break; + case AR_VDOWN: printf(" - AR_VDOWN."); break; + } + if ((ret > 0 )&&(ret & MP_KEY_DOWN)) + printf(" [hold]"); + if (ret > 0) + printf("\n"); + } + + mp_input_ar_close(0); + return 0; +} +#endif diff --git a/core/input/ar.h b/core/input/ar.h new file mode 100644 index 0000000000..dff6d12f8a --- /dev/null +++ b/core/input/ar.h @@ -0,0 +1,35 @@ +/* + * Apple Remote input interface + * + * Copyright (C) 2007 Zoltan Ponekker <pontscho at kac.poliod.hu> + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_AR_H +#define MPLAYER_AR_H + +/* MacOSX Driver */ +int mp_input_ar_init(void); +int mp_input_ar_read(void *ctx, int fd); +int mp_input_ar_close(int fd); + +/* Linux Driver */ +int mp_input_appleir_init(char* dev); +int mp_input_appleir_read(void *ctx, int fd); + +#endif /* MPLAYER_AR_H */ diff --git a/core/input/input.c b/core/input/input.c new file mode 100644 index 0000000000..6643747e4d --- /dev/null +++ b/core/input/input.c @@ -0,0 +1,1959 @@ +/* + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stdbool.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <fcntl.h> +#include <ctype.h> +#include <assert.h> + +#include "osdep/io.h" + +#include "input.h" +#include "mp_fifo.h" +#include "keycodes.h" +#include "osdep/timer.h" +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include "mp_msg.h" +#include "m_config.h" +#include "m_option.h" +#include "path.h" +#include "talloc.h" +#include "options.h" +#include "bstr.h" +#include "stream/stream.h" + +#include "joystick.h" + +#ifdef CONFIG_LIRC +#include "lirc.h" +#endif + +#ifdef CONFIG_LIRCC +#include <lirc/lircc.h> +#endif + +#include "ar.h" + +#ifdef CONFIG_COCOA +#include "osdep/cocoa_events.h" +#endif + +#define MP_MAX_KEY_DOWN 32 + +struct cmd_bind { + int input[MP_MAX_KEY_DOWN + 1]; + char *cmd; + char *location; // filename/line number of definition + struct cmd_bind_section *owner; +}; + +struct key_name { + int key; + char *name; +}; + +/* This array defines all known commands. + * The first field is an id used to recognize the command. + * The second is the command name used in slave mode and input.conf. + * Then comes the definition of each argument, first mandatory arguments + * (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments + * (OARG_INT(default), etc) if any. The command will be given the default + * argument value if the user didn't give enough arguments to specify it. + * A command can take a maximum of MP_CMD_MAX_ARGS arguments (10). + */ + +#define ARG_INT { .type = {"", NULL, &m_option_type_int} } +#define ARG_FLOAT { .type = {"", NULL, &m_option_type_float} } +#define ARG_STRING { .type = {"", NULL, &m_option_type_string} } +#define ARG_CHOICE(c) { .type = {"", NULL, &m_option_type_choice, \ + M_CHOICES(c)} } + +#define OARG_FLOAT(def) { .type = {"", NULL, &m_option_type_float}, \ + .optional = true, .v.f = def } +#define OARG_INT(def) { .type = {"", NULL, &m_option_type_int}, \ + .optional = true, .v.i = def } +#define OARG_CHOICE(def, c) { .type = {"", NULL, &m_option_type_choice, \ + M_CHOICES(c)}, \ + .optional = true, .v.i = def } + +static int parse_cycle_dir(const struct m_option *opt, struct bstr name, + struct bstr param, void *dst); +static const struct m_option_type m_option_type_cycle_dir = { + .name = "up|down", + .parse = parse_cycle_dir, +}; + +static const mp_cmd_t mp_cmds[] = { + { MP_CMD_IGNORE, "ignore", }, + + { MP_CMD_RADIO_STEP_CHANNEL, "radio_step_channel", { ARG_INT } }, + { MP_CMD_RADIO_SET_CHANNEL, "radio_set_channel", { ARG_STRING } }, + { MP_CMD_RADIO_SET_FREQ, "radio_set_freq", { ARG_FLOAT } }, + { MP_CMD_RADIO_STEP_FREQ, "radio_step_freq", {ARG_FLOAT } }, + + { MP_CMD_SEEK, "seek", { + ARG_FLOAT, + OARG_CHOICE(0, ({"relative", 0}, {"0", 0}, + {"absolute-percent", 1}, {"1", 1}, + {"absolute", 2}, {"2", 2})), + OARG_CHOICE(0, ({"default-precise", 0}, {"0", 0}, + {"exact", 1}, {"1", 1}, + {"keyframes", -1}, {"-1", -1})), + }}, + { MP_CMD_EDL_MARK, "edl_mark", }, + { MP_CMD_SPEED_MULT, "speed_mult", { ARG_FLOAT } }, + { MP_CMD_QUIT, "quit", { OARG_INT(0) } }, + { MP_CMD_STOP, "stop", }, + { MP_CMD_FRAME_STEP, "frame_step", }, + { MP_CMD_PLAYLIST_NEXT, "playlist_next", { + OARG_CHOICE(0, ({"weak", 0}, {"0", 0}, + {"force", 1}, {"1", 1})), + }}, + { MP_CMD_PLAYLIST_PREV, "playlist_prev", { + OARG_CHOICE(0, ({"weak", 0}, {"0", 0}, + {"force", 1}, {"1", 1})), + }}, + { MP_CMD_SUB_STEP, "sub_step", { ARG_INT } }, + { MP_CMD_OSD, "osd", { OARG_INT(-1) } }, + { MP_CMD_PRINT_TEXT, "print_text", { ARG_STRING } }, + { MP_CMD_SHOW_TEXT, "show_text", { ARG_STRING, OARG_INT(-1), OARG_INT(0) } }, + { MP_CMD_SHOW_PROGRESS, "show_progress", }, + { MP_CMD_SUB_LOAD, "sub_load", { ARG_STRING } }, + + { MP_CMD_TV_START_SCAN, "tv_start_scan", }, + { MP_CMD_TV_STEP_CHANNEL, "tv_step_channel", { ARG_INT } }, + { MP_CMD_TV_STEP_NORM, "tv_step_norm", }, + { MP_CMD_TV_STEP_CHANNEL_LIST, "tv_step_chanlist", }, + { MP_CMD_TV_SET_CHANNEL, "tv_set_channel", { ARG_STRING } }, + { MP_CMD_TV_LAST_CHANNEL, "tv_last_channel", }, + { MP_CMD_TV_SET_FREQ, "tv_set_freq", { ARG_FLOAT } }, + { MP_CMD_TV_STEP_FREQ, "tv_step_freq", { ARG_FLOAT } }, + { MP_CMD_TV_SET_NORM, "tv_set_norm", { ARG_STRING } }, + + { MP_CMD_DVB_SET_CHANNEL, "dvb_set_channel", { ARG_INT, ARG_INT } }, + + { MP_CMD_SCREENSHOT, "screenshot", { + OARG_CHOICE(2, ({"video", 0}, + {"window", 1}, + {"subtitles", 2})), + OARG_CHOICE(0, ({"single", 0}, + {"each-frame", 1})), + }}, + { MP_CMD_LOADFILE, "loadfile", { + ARG_STRING, + OARG_CHOICE(0, ({"replace", 0}, {"0", 0}, + {"append", 1}, {"1", 1})), + }}, + { MP_CMD_LOADLIST, "loadlist", { + ARG_STRING, + OARG_CHOICE(0, ({"replace", 0}, {"0", 0}, + {"append", 1}, {"1", 1})), + }}, + { MP_CMD_PLAYLIST_CLEAR, "playlist_clear", }, + { MP_CMD_RUN, "run", { ARG_STRING } }, + + { MP_CMD_KEYDOWN_EVENTS, "key_down_event", { ARG_INT } }, + { MP_CMD_SET, "set", { ARG_STRING, ARG_STRING } }, + { MP_CMD_GET_PROPERTY, "get_property", { ARG_STRING } }, + { MP_CMD_ADD, "add", { ARG_STRING, OARG_FLOAT(0) } }, + { MP_CMD_CYCLE, "cycle", { + ARG_STRING, + { .type = {"", NULL, &m_option_type_cycle_dir}, + .optional = true, + .v.f = 1 }, + }}, + + { MP_CMD_SET_MOUSE_POS, "set_mouse_pos", { ARG_INT, ARG_INT } }, + + { MP_CMD_AF_SWITCH, "af_switch", { ARG_STRING } }, + { MP_CMD_AF_ADD, "af_add", { ARG_STRING } }, + { MP_CMD_AF_DEL, "af_del", { ARG_STRING } }, + { MP_CMD_AF_CLR, "af_clr", }, + { MP_CMD_AF_CMDLINE, "af_cmdline", { ARG_STRING, ARG_STRING } }, + + { MP_CMD_SHOW_CHAPTERS, "show_chapters", }, + { MP_CMD_SHOW_TRACKS, "show_tracks", }, + + { MP_CMD_VO_CMDLINE, "vo_cmdline", { ARG_STRING } }, + + {0} +}; + +// Map legacy commands to proper commands +struct legacy_cmd { + const char *old, *new; +}; +static const struct legacy_cmd legacy_cmds[] = { + {"loop", "cycle loop"}, + {"seek_chapter", "add chapter"}, + {"switch_angle", "cycle angle"}, + {"pause", "cycle pause"}, + {"volume", "add volume"}, + {"mute", "cycle mute"}, + {"audio_delay", "add audio-delay"}, + {"switch_audio", "cycle audio"}, + {"balance", "add balance"}, + {"vo_fullscreen", "no-osd cycle fullscreen"}, + {"panscan", "add panscan"}, + {"vo_ontop", "cycle ontop"}, + {"vo_rootwin", "cycle rootwin"}, + {"vo_border", "cycle border"}, + {"frame_drop", "cycle framedrop"}, + {"gamma", "add gamma"}, + {"brightness", "add brightness"}, + {"contrast", "add contrast"}, + {"saturation", "add saturation"}, + {"hue", "add hue"}, + {"switch_vsync", "cycle vsync"}, + {"sub_select", "cycle sub"}, + {"sub_pos", "add sub-pos"}, + {"sub_delay", "add sub-delay"}, + {"sub_visibility", "cycle sub-visibility"}, + {"forced_subs_only", "cycle sub-forced-only"}, + {"sub_scale", "add sub-scale"}, + {"ass_use_margins", "cycle ass-use-margins"}, + {"tv_set_brightness", "add tv-brightness"}, + {"tv_set_hue", "add tv-hue"}, + {"tv_set_saturation", "add tv-saturation"}, + {"tv_set_contrast", "add tv-contrast"}, + {"step_property_osd", "cycle"}, + {"step_property", "no-osd cycle"}, + {"set_property", "no-osd set"}, + {"set_property_osd", "set"}, + {"speed_set", "set speed"}, + {"osd_show_text", "show_text"}, + {"osd_show_property_text", "show_text"}, + {"osd_show_progression", "show_progress"}, + {"show_chapters_osd", "show_chapters"}, + {"show_tracks_osd", "show_tracks"}, + // Approximate (can fail if user added additional whitespace) + {"pt_step 1", "playlist_next"}, + {"pt_step -1", "playlist_prev"}, + // Switch_ratio without argument resets aspect ratio + {"switch_ratio ", "set aspect "}, + {"switch_ratio", "set aspect 0"}, + {0} +}; + + +/// The names of the keys as used in input.conf +/// If you add some new keys, you also need to add them here + +static const struct key_name key_names[] = { + { ' ', "SPACE" }, + { '#', "SHARP" }, + { KEY_ENTER, "ENTER" }, + { KEY_TAB, "TAB" }, + { KEY_BACKSPACE, "BS" }, + { KEY_DELETE, "DEL" }, + { KEY_INSERT, "INS" }, + { KEY_HOME, "HOME" }, + { KEY_END, "END" }, + { KEY_PAGE_UP, "PGUP" }, + { KEY_PAGE_DOWN, "PGDWN" }, + { KEY_ESC, "ESC" }, + { KEY_PRINT, "PRINT" }, |