0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00

osx: improve Media Keys support

This commit addresses some issues with the users had with the previous
implementation in commit c39efb9. Here's the changes:

  * Use Quartz Event Taps to remove Media Key events mpv handles from
    the global OS X queue. This prevents conflicts with iTunes. I did this on
    the main thread since it is mostly idling. It's the playloop thread that
    actually does all the work so there is no danger of blocking the event tap
    callback.
  * Introduce `--no-media-keys` switch so that users can disable all of mpv's
    media key handling at runtime (some prefer iTunes for example).
  * Use mpv's bindings so that users can customize what the media keys do via
    input.conf. Current bindings are:

      MK_PLAY cycle pause
      MK_PREV playlist_prev
      MK_NEXT playlist_next

An additional benefit of this implementation is that it is completly handled
by the `macosx_events` file instead of `macosx_application` making the
project organization more straightforward.
This commit is contained in:
Stefano Pigozzi 2013-06-04 22:12:23 +02:00
parent 1b6888ae8e
commit 213ad5d6c4
10 changed files with 140 additions and 33 deletions

View File

@ -1125,6 +1125,9 @@
--mc=<seconds/frame>
Maximum A-V sync correction per frame (in seconds)
--media-keys, --no-media-keys
OSX only: Enabled by default. Enables/disable media keys support.
--mf=<option1:option2:...>
Used when decoding from multiple PNG or JPEG files with ``mf://``.

View File

@ -110,6 +110,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
.use_lircc = 1,
#ifdef CONFIG_COCOA
.use_ar = 1,
.use_media_keys = 1,
#endif
.default_bindings = 1,
}

View File

@ -409,6 +409,10 @@ static const struct key_name key_names[] = {
{ MP_AR_VDOWN, "AR_VDOWN" },
{ MP_AR_VDOWN_HOLD, "AR_VDOWN_HOLD" },
{ MP_MK_PLAY, "MK_PLAY" },
{ MP_MK_PREV, "MK_PREV" },
{ MP_MK_NEXT, "MK_NEXT" },
{ MP_KEY_POWER, "POWER" },
{ MP_KEY_MENU, "MENU" },
{ MP_KEY_PLAY, "PLAY" },
@ -553,6 +557,7 @@ static const m_option_t mp_input_opts[] = {
OPT_FLAG("lircc", input.use_lircc, CONF_GLOBAL),
#ifdef CONFIG_COCOA
OPT_FLAG("ar", input.use_ar, CONF_GLOBAL),
OPT_FLAG("media-keys", input.use_media_keys, CONF_GLOBAL),
#endif
{ NULL, NULL, 0, 0, 0, 0, NULL}
};
@ -1830,7 +1835,11 @@ struct input_ctx *mp_input_init(struct input_conf *input_conf,
#ifdef CONFIG_COCOA
if (input_conf->use_ar) {
cocoa_start_apple_remote();
cocoa_init_apple_remote();
}
if (input_conf->use_media_keys) {
cocoa_init_media_keys();
}
#endif
@ -1872,7 +1881,11 @@ void mp_input_uninit(struct input_ctx *ictx, struct input_conf *input_conf)
#ifdef CONFIG_COCOA
if (input_conf->use_ar) {
cocoa_stop_apple_remote();
cocoa_uninit_apple_remote();
}
if (input_conf->use_media_keys) {
cocoa_uninit_media_keys();
}
#endif

View File

@ -189,6 +189,12 @@
#define MP_AR_VDOWN (MP_AR_BASE + 12)
#define MP_AR_VDOWN_HOLD (MP_AR_BASE + 13)
// Apple Media Keys input module
#define MP_MK_BASE (MP_KEY_BASE+0xF0)
#define MP_MK_PLAY (MP_AR_BASE + 0)
#define MP_MK_PREV (MP_AR_BASE + 1)
#define MP_MK_NEXT (MP_AR_BASE + 2)
/* Special keys */
#define MP_KEY_INTERN (MP_KEY_BASE+0x1000)
#define MP_KEY_CLOSE_WIN (MP_KEY_INTERN+0)

View File

@ -241,6 +241,7 @@ typedef struct MPOpts {
int use_lircc;
#ifdef CONFIG_COCOA
int use_ar;
int use_media_keys;
#endif
int default_bindings;
int test;

View File

@ -152,6 +152,11 @@ AR_VUP_HOLD add chapter 1
AR_VDOWN add volume -1
AR_VDOWN_HOLD add chapter -1
# Media Keys section
MK_PLAY cycle pause
MK_PREV playlist_prev
MK_NEXT playlist_next
# Joystick section
# WARNING: joystick support has to be explicitly enabled at
# compiletime with --enable-joystick

View File

@ -120,19 +120,7 @@ static NSString *escape_loadfile_name(NSString *input)
- (void)sendEvent:(NSEvent *)event
{
if ([event type] == NSSystemDefined && [event subtype] == 8) {
// It's a media key! Handle it specially. The magic numbers are reverse
// engineered and found on several blog posts. Unfortunately there is
// no public API for this. F-bomb.
int code = (([event data1] & 0xFFFF0000) >> 16);
int flags = ([event data1] & 0x0000FFFF);
int down = (((flags & 0xFF00) >> 8)) == 0xA;
if (down)
[self.eventsResponder handleMediaKey:code];
} else {
[super sendEvent:event];
}
[super sendEvent:event];
if (self.inputContext)
mp_input_wakeup(self.inputContext);

View File

@ -28,10 +28,13 @@ struct cocoa_input_queue;
@end
@interface EventsResponder : NSObject <HIDRemoteDelegate>
- (void)handleMediaKey:(int)key;
- (BOOL)handleMediaKey:(int)key;
- (NSEvent *)handleKeyDown:(NSEvent *)event;
- (void)startAppleRemote;
- (void)stopAppleRemote;
- (void)startMediaKeys;
- (void)restartMediaKeys;
- (void)stopMediaKeys;
@property(nonatomic, retain) HIDRemote *remote;
@end

View File

@ -23,7 +23,11 @@
void cocoa_put_key(int keycode);
void cocoa_check_events(void);
void cocoa_start_apple_remote(void);
void cocoa_stop_apple_remote(void);
void cocoa_init_apple_remote(void);
void cocoa_uninit_apple_remote(void);
void cocoa_init_media_keys(void);
void cocoa_uninit_media_keys(void);
#endif

View File

@ -92,18 +92,67 @@ static int convert_key(unsigned key, unsigned charcode)
return charcode;
}
void cocoa_start_apple_remote(void)
void cocoa_init_apple_remote(void)
{
Application *app = mpv_shared_app();
[app.eventsResponder startAppleRemote];
}
void cocoa_stop_apple_remote(void)
void cocoa_uninit_apple_remote(void)
{
Application *app = mpv_shared_app();
[app.eventsResponder stopAppleRemote];
}
static CGEventRef tap_event_callback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *ctx)
{
EventsResponder *responder = ctx;
if (type == kCGEventTapDisabledByTimeout) {
// The Mach Port receiving the taps became unresponsive for some
// reason, restart listening on it.
[responder restartMediaKeys];
return event;
}
if (type == kCGEventTapDisabledByUserInput)
return event;
NSEvent *nse = [NSEvent eventWithCGEvent:event];
if ([nse type] != NSSystemDefined || [nse subtype] != 8)
// This is not a media key
return event;
// It's a media key! Handle it specially. The magic numbers are reverse
// engineered and found on several blog posts. Unfortunately there is
// no public API for this. F-bomb.
int code = (([nse data1] & 0xFFFF0000) >> 16);
int flags = ([nse data1] & 0x0000FFFF);
int down = (((flags & 0xFF00) >> 8)) == 0xA;
if (down && [responder handleMediaKey:code]) {
// Handled this event, return nil so that it is removed from the
// global queue.
return nil;
} else {
// Was a media key but we were not interested in it. Leave it in the
// global queue by returning the original event.
return event;
}
}
void cocoa_init_media_keys(void) {
Application *app = mpv_shared_app();
[app.eventsResponder startMediaKeys];
}
void cocoa_uninit_media_keys(void) {
Application *app = mpv_shared_app();
[app.eventsResponder stopMediaKeys];
}
void cocoa_check_events(void)
{
Application *app = mpv_shared_app();
@ -117,7 +166,9 @@ void cocoa_put_key(int keycode)
[mpv_shared_app().iqueue push:keycode];
}
@implementation EventsResponder
@implementation EventsResponder {
CFMachPortRef _mk_tap_port;
}
- (void)startAppleRemote
{
dispatch_async(dispatch_get_main_queue(), ^{
@ -135,24 +186,56 @@ void cocoa_put_key(int keycode)
[self.remote stopRemoteControl];
});
}
- (void)restartMediaKeys
{
CGEventTapEnable(self->_mk_tap_port, true);
}
- (void)startMediaKeys
{
dispatch_async(dispatch_get_main_queue(), ^{
// Install a Quartz Event Tap. This will notify mpv through the
// returned Mach Port and cause mpv to execute the `tap_event_callback`
// function.
self->_mk_tap_port = CGEventTapCreate(kCGSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(NX_SYSDEFINED),
tap_event_callback,
self);
assert(self->_mk_tap_port != nil);
NSMachPort *port = (NSMachPort *)self->_mk_tap_port;
[[NSRunLoop mainRunLoop] addPort:port forMode:NSRunLoopCommonModes];
});
}
- (void)stopMediaKeys
{
dispatch_async(dispatch_get_main_queue(), ^{
NSMachPort *port = (NSMachPort *)self->_mk_tap_port;
[[NSRunLoop mainRunLoop] removePort:port forMode:NSRunLoopCommonModes];
CFRelease(self->_mk_tap_port);
self->_mk_tap_port = nil;
});
}
- (NSArray *) keyEquivalents
{
return @[@"h", @"q", @"Q", @"0", @"1", @"2"];
}
- (void)handleMediaKey:(int)key
- (BOOL)handleMediaKey:(int)key
{
switch (key) {
case NX_KEYTYPE_PLAY:
cocoa_put_key(MP_KEY_PLAY);
break;
NSDictionary *keymap = @{
@(NX_KEYTYPE_PLAY): @(MP_MK_PLAY),
@(NX_KEYTYPE_REWIND): @(MP_MK_PREV),
@(NX_KEYTYPE_FAST): @(MP_MK_NEXT),
};
case NX_KEYTYPE_FAST:
cocoa_put_key(MP_KEY_NEXT);
break;
case NX_KEYTYPE_REWIND:
cocoa_put_key(MP_KEY_PREV);
break;
int mpkey = [keymap[@(key)] intValue];
if (mpkey > 0) {
cocoa_put_key(mpkey);
return YES;
} else {
return NO;
}
}
- (NSEvent*)handleKeyDown:(NSEvent *)event