From 86790494d38c4f33f74875e1ab7a9dc1bdae1772 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 3 Apr 2012 08:13:12 +0200 Subject: OSX, input: implement wakeup in response to Cocoa events Add code to wake up the select() call in input.c when an OSX event is available and a Cocoa OpenGL backend is initialized. Fixes the slow response to input or other events in Cocoa-based VOs during long select() sleeps (e.g., when mplayer2 is paused) introduced by commit 7040968. --- osdep/cocoa_events.h | 30 +++++++++++ osdep/cocoa_events.m | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 osdep/cocoa_events.h create mode 100644 osdep/cocoa_events.m (limited to 'osdep') diff --git a/osdep/cocoa_events.h b/osdep/cocoa_events.h new file mode 100644 index 0000000000..3f0e775e03 --- /dev/null +++ b/osdep/cocoa_events.h @@ -0,0 +1,30 @@ +/* + * Cocoa Event Handling + * + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2. If not, see . + */ + +#ifndef MPLAYER_COCOA_EVENTS_H +#define MPLAYER_COCOA_EVENTS_H + +#include "input/input.h" + +void cocoa_events_init(struct input_ctx *ictx, + void (*read_all_fd_events)(struct input_ctx *ictx, int time)); +void cocoa_events_uninit(void); +void cocoa_events_read_all_events(struct input_ctx *ictx, int time); + +#endif /* MPLAYER_COCOA_EVENTS_H */ diff --git a/osdep/cocoa_events.m b/osdep/cocoa_events.m new file mode 100644 index 0000000000..ba09c13cad --- /dev/null +++ b/osdep/cocoa_events.m @@ -0,0 +1,137 @@ +/* + * Cocoa Event Handling + * + * This file is part of mplayer2. + * + * mplayer2 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. + * + * mplayer2 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 mplayer2. If not, see . + */ + +/* + * Implementation details: + * This file deals with custom event polling on MacOSX. When mplayer2 is paused + * it will asynchronously poll for events using select. This works correctly on + * Linux with X11 since the events are notified through the file descriptors + * where mplayer2 is listening on. On the other hand, the OSX window server + * notifies the processes for events using mach ports. + * + * The code below uses functionality from Cocoa that abstracts the async polling + * of events from the window server. When a Cocoa event comes in, the polling is + * interrupted and the event is dealt with in the next vo_check_events. + * + * To keep the select fd polling code working, that functionality is executed + * from another thread. Whoever finishes polling before the given time, be it + * Cocoa or the original select code, notifies the other for an immediate wake. + */ + +#include "cocoa_events.h" +#include "libvo/cocoa_common.h" +#include "talloc.h" + +#import +#include + +// Bogus event subtype to wake the Cocoa code from polling state +#define MP_EVENT_SUBTYPE_WAKE_EVENTLOOP 100 + +// This is the threshold in milliseconds below which the Cocoa polling is not +// executed. There is some overhead caused by the synchronization between +// threads. Even if in practice it isn't noticeable, we try to avoid the useless +// waste of resources. +#define MP_ASYNC_THRESHOLD 50 + +struct priv { + dispatch_queue_t select_queue; + bool is_runloop_polling; + void (*read_all_fd_events)(struct input_ctx *ictx, int time); +}; + +static struct priv *p; + +static void cocoa_wait_events(int mssleeptime) +{ + NSTimeInterval sleeptime = mssleeptime / 1000.0; + NSEvent *event; + p->is_runloop_polling = YES; + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate dateWithTimeIntervalSinceNow:sleeptime] + inMode:NSEventTrackingRunLoopMode dequeue:NO]; + + // dequeue the next event if it is a fake to wake the cocoa polling + if (event && [event type] == NSApplicationDefined && + [event subtype] == MP_EVENT_SUBTYPE_WAKE_EVENTLOOP) { + [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil + inMode:NSEventTrackingRunLoopMode dequeue:YES]; + } + p->is_runloop_polling = NO; +} + +static void cocoa_wake_runloop() +{ + if (p->is_runloop_polling) { + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + NSEvent *event; + + /* Post an event so we'll wake the run loop that is async polling */ + event = [NSEvent otherEventWithType: NSApplicationDefined + location: NSZeroPoint + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: MP_EVENT_SUBTYPE_WAKE_EVENTLOOP + data1: 0 + data2: 0]; + + [NSApp postEvent:event atStart:NO]; + [pool release]; + } +} + +void cocoa_events_init(struct input_ctx *ictx, + void (*read_all_fd_events)(struct input_ctx *ictx, int time)) +{ + NSApplicationLoad(); + p = talloc_ptrtype(NULL, p); + *p = (struct priv){ + .is_runloop_polling = NO, + .read_all_fd_events = read_all_fd_events, + .select_queue = dispatch_queue_create("org.mplayer2.select_queue", + NULL), + }; +} + +void cocoa_events_uninit(void) +{ + talloc_free(p); +} + +void cocoa_events_read_all_events(struct input_ctx *ictx, int time) +{ + // don't bother delegating the select to the async queue if the blocking + // time is really low or if we are not running a GUI + if (time > MP_ASYNC_THRESHOLD && vo_cocoa_gui_running()) { + dispatch_async(p->select_queue, ^{ + p->read_all_fd_events(ictx, time); + cocoa_wake_runloop(); + }); + + cocoa_wait_events(time); + mp_input_wakeup(ictx); + + // wait for the async queue to get empty. + dispatch_sync(p->select_queue, ^{}); + } else { + p->read_all_fd_events(ictx, time); + } +} -- cgit v1.2.3