Index: input/input.c =================================================================== --- input/input.c (revision 24645) +++ input/input.c (working copy) @@ -291,14 +291,20 @@ { AR_PLAY, "AR_PLAY" }, { AR_PLAY_HOLD, "AR_PLAY_HOLD" }, + { AR_PLAY_DCLICK, "AR_PLAY_DCLICK" }, { AR_NEXT, "AR_NEXT" }, { AR_NEXT_HOLD, "AR_NEXT_HOLD" }, + { AR_NEXT_DCLICK, "AR_NEXT_DCLICK" }, { AR_PREV, "AR_PREV" }, { AR_PREV_HOLD, "AR_PREV_HOLD" }, + { AR_PREV_DCLICK, "AR_PREV_DCLICK" }, { AR_MENU, "AR_MENU" }, { AR_MENU_HOLD, "AR_MENU_HOLD" }, + { AR_MENU_DCLICK, "AR_MENU_DCLICK" }, { AR_VUP, "AR_VUP" }, + { AR_VUP_DCLICK, "AR_VUP_DCLICK" }, { AR_VDOWN, "AR_VDOWN" }, + { AR_VDOWN_DCLICK, "AR_VDOWN_DCLICK" }, { KEY_POWER, "POWER" }, { KEY_MENU, "MENU" }, @@ -429,14 +435,20 @@ #ifdef HAVE_APPLE_REMOTE { { AR_PLAY, 0}, "pause" }, { { AR_PLAY_HOLD, 0}, "quit" }, + { { AR_PLAY_DCLICK, 0}, "vo_fullscreen" }, { { AR_NEXT, 0 }, "seek 30" }, { { AR_NEXT_HOLD, 0 }, "seek 120" }, + { { AR_NEXT_DCLICK, 0 }, "seek 120" }, { { AR_PREV, 0 }, "seek -10" }, { { AR_PREV_HOLD, 0 }, "seek -120" }, + { { AR_PREV_DCLICK, 0 }, "seek -120" }, { { AR_MENU, 0 }, "osd" }, { { AR_MENU_HOLD, 0 }, "mute" }, + { { AR_MENU_DCLICK, 0 }, "sub_select" }, { { AR_VUP, 0 }, "volume 1"}, + { { AR_VUP_DCLICK, 0 }, "vo_ontop"}, { { AR_VDOWN, 0 }, "volume -1"}, +// { { AR_VDOWN_DCLICK, 0 }, "mute"}, #endif { { 'T', 0 }, "vo_ontop" }, { { 'f', 0 }, "vo_fullscreen" }, Index: input/ar.c =================================================================== --- input/ar.c (revision 24645) +++ input/ar.c (working copy) @@ -32,6 +32,8 @@ #include "input.h" #include "ar.h" +#include "mp_msg.h" + extern int slave_mode; extern const double NSAppKitVersionNumber; @@ -113,7 +115,16 @@ in queue begin to be lost. Set to 16 to hold at least 2 events. */ static const int MAX_QUEUE_SIZE = 16; +static const float MAX_DCLICK_TIME_INTERVAL=0.33; +static const int CHECK_DOUBLE_CLICK_EVENT = 1; + +#define DURATION_LESS_THAN(interval, t) \ + (((interval)>0 && (interval)<(t)*1000) || ((interval)<0 && -(interval)<(t)*1000000)) +#define DURATION_GREAT_OR_EQUAL(interval, t) \ + (((interval)>0 && (interval)>=(t)*1000) || -(interval)>=(t)*1000000) + + static int FindHIDDevices(mach_port_t masterPort, io_iterator_t *hidObjectIterator) { @@ -321,7 +332,7 @@ return 0; } -int mp_input_ar_read(int fd) +static int mp_input_ar_read_raw(AbsoluteTime *eventTime) { int i, down = 0; int ret = MP_INPUT_NOTHING; @@ -334,15 +345,29 @@ int cookie_nr = 0; char cookie_queue[MAX_QUEUE_SIZE]; int value_queue[MAX_QUEUE_SIZE]; + AbsoluteTime eventTimeQueue[MAX_QUEUE_SIZE]; + static int first_time = 1; if (inited == 0) return MP_INPUT_NOTHING; + // We have to close and reopen the hid device once to make + // system not respond to the remote input. + if (first_time) { + first_time = 0; + (*hidDeviceInterface)->close(hidDeviceInterface); + if ((*hidDeviceInterface)->open(hidDeviceInterface, + kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) + hidDeviceIsOpen = 0; + } + + 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 + AbsoluteTime upTime = UpTime(); + mp_msg(MSGT_INPUT,MSGL_DBG4, + "- cookie: %d, value: %d, timestamp: %lu,%lu, uptime: %lu,%lu\n", + (int)event.elementCookie, (int)event.value, event.timestamp.hi, + event.timestamp.lo, upTime.hi, upTime.lo); // 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 @@ -353,6 +378,7 @@ cookie_nr = 0; continue; } + eventTimeQueue[cookie_nr] = event.timestamp; cookie_queue[cookie_nr] = (char)(int)event.elementCookie; value_queue[cookie_nr++] = event.value; // 4 cookies are necessary to make up a valid sequence. @@ -367,6 +393,14 @@ ++ar_code; if (ar_code->cookies != NULL) { ret = ar_code->keycode; + *eventTime = zeroTime; + for (i = cookie_nr-ar_code->seq_len; i < cookie_nr; ++i) { + if (eventTimeQueue[i].hi>eventTime->hi + || (eventTimeQueue[i].hi==eventTime->hi + && eventTimeQueue[i].lo>eventTime->lo)) { + *eventTime = eventTimeQueue[i]; + } + } switch (ret) { // For these 4 keys, the remote can keep a hold state. case AR_VUP: @@ -383,18 +417,261 @@ default: down = 0; } + break; } } } + if (ret > 0) + prev_event = ret; + return ret | down; +} + +int mp_input_ar_read(int fd) +{ + int i, down; + int ret; + AbsoluteTime zeroTime = {0,0}; + AbsoluteTime eventTime, upTime; + Duration interval = 0; + static AbsoluteTime pendingEventTime = {0,0}; + static AbsoluteTime lastClickEventTime = {0,0}, secondClickDownTime = {0,0}; + static int prev_event = 0, pending_event = 0, last_click_event = 0; + // Record whether we ever got the click down event. + static int first_click_down = 0, second_click_down = 0; + + if (inited == 0) + return MP_INPUT_NOTHING; +mp_input_ar_read_again: + ret = MP_INPUT_NOTHING; + eventTime = zeroTime; + + if (pending_event) { + ret = pending_event & ~MP_KEY_DOWN; + down = pending_event & MP_KEY_DOWN; + eventTime = pendingEventTime; + pending_event = 0; + upTime = UpTime(); + mp_msg(MSGT_INPUT,MSGL_DBG2, + "[%lu, %lu] got pending event %d, %d, %lu, %lu\n", + upTime.hi, upTime.lo, ret, down, eventTime.hi, eventTime.lo); + } + else { + down = 0; + ret = mp_input_ar_read_raw(&eventTime); + if (ret != MP_INPUT_NOTHING) { + down = ret & MP_KEY_DOWN; + ret &= ~MP_KEY_DOWN; + upTime = UpTime(); + mp_msg(MSGT_INPUT,MSGL_DBG2, + "[%lu, %lu] got raw event %d, %d, %lu, %lu\n", + upTime.hi, upTime.lo, ret, down, eventTime.hi, eventTime.lo); + } + } + + if (CHECK_DOUBLE_CLICK_EVENT) { + switch (ret) { + // These 2 keys have both press and release events. + // Here we only handle press event, release event keeps going down. + case AR_VUP: + case AR_VDOWN: + if (down) { + if (last_click_event == ret) { + interval = AbsoluteDeltaToDuration(eventTime, + lastClickEventTime); + if (DURATION_LESS_THAN(interval, MAX_DCLICK_TIME_INTERVAL)) { + // It's the second down event, we record it. + second_click_down = 1; + secondClickDownTime = eventTime; + mp_msg(MSGT_INPUT,MSGL_DBG2, + "got second click down, interval %d, " + "continue read\n", (int)interval); + goto mp_input_ar_read_again; + } + } + if (last_click_event) { + // Doubleclick timed out or key changed, + // send the last click event we delayed. + if (last_click_event == ret) { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "doubleclick down timed out %d, " + "send last click %lu, %lu\n", + (int)interval, lastClickEventTime.hi, + lastClickEventTime.lo); + } + else { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "click event changed %d=>%d, " + "send last click %lu, %lu\n", + last_click_event, ret, lastClickEventTime.hi, + lastClickEventTime.lo); + } + pending_event = ret | down; + pendingEventTime = eventTime; + ret = last_click_event; + down = 0; + eventTime = lastClickEventTime; + last_click_event = 0; + } + else { + // First click down, record it. + printf("first click down, record and continue read\n"); + last_click_event = ret; + first_click_down = 1; + lastClickEventTime = eventTime; + goto mp_input_ar_read_again; + } + break; + } + // For these 4 keys, we always get release events. + case AR_PLAY: + case AR_NEXT: + case AR_PREV: + case AR_MENU: + if (last_click_event == ret) { + interval = AbsoluteDeltaToDuration(eventTime, + lastClickEventTime); + if (DURATION_LESS_THAN(interval, MAX_DCLICK_TIME_INTERVAL)) { + // Send the doubleclick event. + if (!first_click_down) { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "got doubleclick event for %d, " + "interval %d, (%lu, %lu=>%lu, %lu)\n", + ret, (int)interval, lastClickEventTime.hi, + lastClickEventTime.lo, eventTime.hi, + eventTime.lo); + ret += 2; + down = 0; + last_click_event = 0; + second_click_down = 0; + break; + } + // First click down released in time (volume up/down). + mp_msg(MSGT_INPUT,MSGL_DBG2, + "got first click down released in time for %d, " + "interval %d, (%lu, %lu=>%lu, %lu) clear " + "down flag and continue read\n", + ret, (int)interval, lastClickEventTime.hi, + lastClickEventTime.lo, eventTime.hi, + eventTime.lo); + // Because click down and release has a delay, + // we use the first release time to judge doubleclick. + lastClickEventTime = eventTime; + first_click_down = 0; + goto mp_input_ar_read_again; + } + else if (first_click_down) { + // First click down too long time, so the doubleclick + // should fail. Just send out current release event. + mp_msg(MSGT_INPUT,MSGL_DBG2, + "first click down %d released timeout, " + "interval %d, (%lu, %lu=>%lu, %lu), " + "just send the release event\n", + ret, (int)interval, lastClickEventTime.hi, + lastClickEventTime.lo, eventTime.hi, + eventTime.lo); + last_click_event = 0; + first_click_down = 0; + break; + } + } + if (last_click_event) { + // Send out last click event we delayed. + if (last_click_event == ret) { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "doubleclick release timed out %d, " + "send last click %lu, %lu\n", + (int)interval, lastClickEventTime.hi, + lastClickEventTime.lo); + } + else { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "click event changed %d=>%d, send last click " + "%lu, %lu, and record current click\n", + last_click_event, ret, lastClickEventTime.hi, + lastClickEventTime.lo); + } + i = ret; + ret = last_click_event; + down = first_click_down ? MP_KEY_DOWN : 0; + eventTime = lastClickEventTime; + last_click_event = i; + lastClickEventTime = eventTime; + first_click_down = 0; + second_click_down = 0; + } + else { + mp_msg(MSGT_INPUT,MSGL_DBG2, + "first click released, record and continue read\n"); + last_click_event = ret; + lastClickEventTime = eventTime; + goto mp_input_ar_read_again; + } + break; + case MP_INPUT_NOTHING: + if (last_click_event == 0) + break; + // We have click event and no input, check the interval. + interval = AbsoluteDeltaToDuration(UpTime(), + lastClickEventTime); + if (DURATION_GREAT_OR_EQUAL(interval, MAX_DCLICK_TIME_INTERVAL)) { + // Last click event reach the time + // Send out the click event we delayed. + ret = last_click_event; + down = first_click_down ? MP_KEY_DOWN : 0; + if (second_click_down) { + upTime = UpTime(); + mp_msg(MSGT_INPUT,MSGL_DBG2, + "[%lu,%lu] doubleclick timed out %d, send " + "last click %d, %d, %lu, %lu, and pending " + "the second down %lu,%lu\n", + upTime.hi, upTime.lo, (int)interval, ret, down, + lastClickEventTime.hi, lastClickEventTime.lo, + secondClickDownTime.hi, secondClickDownTime.lo); + pending_event = last_click_event | MP_KEY_DOWN; + pendingEventTime = secondClickDownTime; + second_click_down = 0; + } + else { + upTime = UpTime(); + mp_msg(MSGT_INPUT,MSGL_DBG2, + "[%lu,%lu] doubleclick timed out %d, " + "send last click %d, %d, %lu, %lu\n", + upTime.hi, upTime.lo, (int)interval, ret, down, + lastClickEventTime.hi, lastClickEventTime.lo); + } + last_click_event = 0; + first_click_down = 0; + } + break; + default: + // other keys + if (last_click_event) { + // pending current event and send out last click. + i = ret | down; + ret = last_click_event & ~MP_KEY_DOWN; + down = last_click_event & MP_KEY_DOWN; + mp_msg(MSGT_INPUT,MSGL_DBG2, + "click event, send last click %d, %d, %lu, %lu " + "and pending current event\n", ret, down, + lastClickEventTime.hi, lastClickEventTime.lo); + last_click_event = 0; + pending_event = i; + pendingEventTime = eventTime; + } + } + } + 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); + while (ret != MP_INPUT_NOTHING) + ret = mp_input_ar_read_raw(&eventTime); + pending_event = 0; + last_click_event = first_click_down = second_click_down = 0; } return MP_INPUT_NOTHING; } @@ -410,6 +687,8 @@ if (ret > 0) prev_event = ret; + if (ret != MP_INPUT_NOTHING) + mp_msg(MSGT_INPUT,MSGL_V, "got Apple Remote event %d, %d\n", ret, down); return ret | down; } @@ -434,36 +713,3 @@ inited = 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(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 Index: input/ar.h =================================================================== --- input/ar.h (revision 24645) +++ input/ar.h (working copy) @@ -23,17 +23,26 @@ #ifndef INPUT_AR_H #define INPUT_AR_H +// AR_VUP_HOLD and AR_VDOWN_HOLD currently not implemented. #define AR_BASE 0x500 #define AR_PLAY (AR_BASE + 0) #define AR_PLAY_HOLD (AR_BASE + 1) -#define AR_NEXT (AR_BASE + 2) -#define AR_NEXT_HOLD (AR_BASE + 3) -#define AR_PREV (AR_BASE + 4) -#define AR_PREV_HOLD (AR_BASE + 5) -#define AR_MENU (AR_BASE + 6) -#define AR_MENU_HOLD (AR_BASE + 7) -#define AR_VUP (AR_BASE + 8) -#define AR_VDOWN (AR_BASE + 9) +#define AR_PLAY_DCLICK (AR_BASE + 2) +#define AR_NEXT (AR_BASE + 3) +#define AR_NEXT_HOLD (AR_BASE + 4) +#define AR_NEXT_DCLICK (AR_BASE + 5) +#define AR_PREV (AR_BASE + 6) +#define AR_PREV_HOLD (AR_BASE + 7) +#define AR_PREV_DCLICK (AR_BASE + 8) +#define AR_MENU (AR_BASE + 9) +#define AR_MENU_HOLD (AR_BASE + 10) +#define AR_MENU_DCLICK (AR_BASE + 11) +#define AR_VUP (AR_BASE + 12) +#define AR_VUP_HOLD (AR_BASE + 13) +#define AR_VUP_DCLICK (AR_BASE + 14) +#define AR_VDOWN (AR_BASE + 15) +#define AR_VDOWN_HOLD (AR_BASE + 16) +#define AR_VDOWN_DCLICK (AR_BASE + 17) int mp_input_ar_init(void); int mp_input_ar_read(int fd); Index: etc/input.conf =================================================================== --- etc/input.conf (revision 24645) +++ etc/input.conf (working copy) @@ -105,14 +105,20 @@ AR_PLAY pause AR_PLAY_HOLD quit +AR_PLAY_DCLICK vo_fullscreen AR_NEXT seek 30 AR_NEXT_HOLD seek 120 +AR_NEXT_DCLICK seek 120 AR_PREV seek -10 AR_PREV_HOLD seek -120 +AR_PREV_DCLICK seek -120 AR_MENU osd AR_MENU_HOLD mute +AR_MENU_DCLICK sub_select AR_VUP volume 1 +AR_VUP_DCLICK vo_ontop AR_VDOWN volume -1 +#AR_VDOWN_DCLICK mute ## ## OSD Menu movement keys