[MPlayer-dev-eng] [PATCH] Apple Remote support double click
Ulion
ulion2002 at gmail.com
Fri Aug 24 18:05:26 CEST 2007
Hello,
Here's a patch to support Apple Remote double click event, judged by a
double click max difference time.
Original mp_input_ar_read function now become mp_input_ar_read_raw,
and add a wrapper function as mp_input_ar_read to judge double click
event.
Most useful usage come by the double click feature is double click to
toggle fullscreen (in default cmd mapping).
--
Ulion
-------------- next part --------------
Index: input/input.c
===================================================================
--- input/input.c (revision 24142)
+++ input/input.c (working copy)
@@ -288,14 +288,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" },
@@ -426,14 +432,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 24142)
+++ 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)
{
@@ -305,6 +316,9 @@
int is_mplayer_front()
{
+#ifdef TEST
+ return 1;
+#else
ProcessSerialNumber myProc, frProc;
Boolean sameProc;
pid_t parentPID;
@@ -319,9 +333,10 @@
return parentPID==getppid();
}
return 0;
+#endif
}
-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,14 +349,28 @@
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 has to close and re-open the hid device once to make
+ // system not response 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);
+ AbsoluteTime upTime = UpTime();
+ mp_msg(MSGT_INPUT,MSGL_DBG3, " - 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);
#endif
// Shorten cookie sequence by removing cookies value 5 and 18,
// since 5 always follows 6 (on tiger), 18 follows 19 (on leopard).
@@ -353,6 +382,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 +397,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 +421,217 @@
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;
+ // Click down record is only for AR_VUP and AR_VDOWN.
+ 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) {
+ // Double click timeouted or key changed,
+ // send the last click event we delayed.
+ if (last_click_event == ret) {
+ mp_msg(MSGT_INPUT,MSGL_DBG2, "double click down timeouted %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 double click event.
+ if (!first_click_down) {
+ mp_msg(MSGT_INPUT,MSGL_DBG2, "got double click 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 double click.
+ 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 double click
+ // 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, "double click release timeouted %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 has 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] double click timeouted %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] double click timeouted %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 +647,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;
}
@@ -439,6 +678,9 @@
{
int ret;
+ mp_msg_init();
+ verbose = 3;
+
if (mp_input_ar_init() < 0) {
printf("Unable to initialize Apple Remote.\n");
return 1;
@@ -448,19 +690,26 @@
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_PLAY_DCLICK:printf(" - AR_PLAY_DCLICK."); break;
case AR_NEXT: printf(" - AR_NEXT."); break;
case AR_NEXT_HOLD: printf(" - AR_NEXT_HOLD."); break;
+ case AR_NEXT_DCLICK:printf(" - AR_NEXT_DCLICK."); break;
case AR_PREV: printf(" - AR_PREV."); break;
case AR_PREV_HOLD: printf(" - AR_PREV_HOLD."); break;
+ case AR_PREV_DCLICK:printf(" - AR_PREV_DCLICK."); break;
case AR_MENU: printf(" - AR_MENU."); break;
case AR_MENU_HOLD: printf(" - AR_MENU_HOLD."); break;
+ case AR_MENU_DCLICK:printf(" - AR_MENU_DCLICK."); break;
case AR_VUP: printf(" - AR_VUP."); break;
+ case AR_VUP_DCLICK: printf(" - AR_VUP_DCLICK."); break;
case AR_VDOWN: printf(" - AR_VDOWN."); break;
+ case AR_VDOWN_DCLICK:printf(" - AR_VDOWN_DCLICK."); break;
}
if ((ret > 0 )&&(ret & MP_KEY_DOWN))
- printf(" [hold]");
+ printf(" [down]");
if (ret > 0)
printf("\n");
+ usleep(100000);
}
mp_input_ar_close(0);
Index: input/ar.h
===================================================================
--- input/ar.h (revision 24142)
+++ 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 24142)
+++ 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
More information about the MPlayer-dev-eng
mailing list