summaryrefslogtreecommitdiffstats
path: root/osdep
diff options
context:
space:
mode:
authorStefano Pigozzi <stefano.pigozzi@gmail.com>2013-03-04 14:23:06 +0100
committerStefano Pigozzi <stefano.pigozzi@gmail.com>2013-05-12 15:27:54 +0200
commit134f3e97bf482b75c0eccbe5ac943a2a1d5a4ad6 (patch)
tree98bac431df2697072238864e48a0380d3b1745ff /osdep
parentafdc9c4ae2e69a9ced6c3c6580df19edfedea36a (diff)
downloadmpv-134f3e97bf482b75c0eccbe5ac943a2a1d5a4ad6.tar.bz2
mpv-134f3e97bf482b75c0eccbe5ac943a2a1d5a4ad6.tar.xz
OSX: run native event loop in a separate thread
This commit is a followup on the previous one and uses a solution I like more since it totally decouples the Cocoa code from mpv's core and tries to emulate a generic Cocoa application's lifecycle as much as possible without fighting the framework. mpv's main is executed in a pthread while the main thread runs the native cocoa event loop. All of the thread safety is mainly accomplished with additional logic in cocoa_common as to not increase complexity on the crossplatform parts of the code.
Diffstat (limited to 'osdep')
-rw-r--r--osdep/macosx_application.h17
-rw-r--r--osdep/macosx_application.m107
-rw-r--r--osdep/macosx_application_objc.h6
3 files changed, 71 insertions, 59 deletions
diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h
index c2227ae05d..b0f7944512 100644
--- a/osdep/macosx_application.h
+++ b/osdep/macosx_application.h
@@ -22,9 +22,7 @@
struct input_ctx;
struct mp_fifo;
-// Playloop callback function pointer
-typedef void(*play_loop_callback)(void *);
-typedef int(*should_stop_callback)(void *);
+typedef int (*mpv_main_fn)(int, char**);
// Menu Keys identifing menu items
typedef enum {
@@ -35,22 +33,23 @@ typedef enum {
MPM_ZOOM,
} MPMenuKey;
+// multithreaded wrapper for mpv_main
+int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[]);
+
void cocoa_register_menu_item_action(MPMenuKey key, void* action);
// initializes Cocoa application
void init_cocoa_application(void);
void terminate_cocoa_application(void);
+void cocoa_autorelease_pool_alloc(void);
+void cocoa_autorelease_pool_drain(void);
// Runs the Cocoa Main Event Loop
void cocoa_run_runloop(void);
+void cocoa_stop_runloop(void);
void cocoa_post_fake_event(void);
-// Adds play_loop as a timer of the Main Cocoa Event Loop
-void cocoa_run_loop_schedule(play_loop_callback callback,
- should_stop_callback playback_stopped,
- void *context,
- struct input_ctx *input_context,
- struct mp_fifo *key_fifo);
+void cocoa_set_state(struct input_ctx *input_context, struct mp_fifo *key_fifo);
void macosx_finder_args_preinit(int *argc, char ***argv);
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
index 39439e9fde..206313d294 100644
--- a/osdep/macosx_application.m
+++ b/osdep/macosx_application.m
@@ -16,8 +16,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <pthread.h>
#include "talloc.h"
+#include "core/mp_msg.h"
#include "core/mp_fifo.h"
#include "core/input/input.h"
#include "core/input/keycodes.h"
@@ -25,10 +27,9 @@
#include "osdep/macosx_application_objc.h"
#include "video/out/osx_common.h"
-// 0.0001 seems too much and 0.01 too low, no idea why this works so well
-#define COCOA_MAGIC_TIMER_DELAY 0.001
-
static Application *app;
+static NSAutoreleasePool *pool;
+static pthread_t playback_thread_id;
@interface Application (PrivateMethods)
- (NSMenuItem *)menuItemWithParent:(NSMenu *)parent
@@ -54,12 +55,8 @@ static Application *app;
@synthesize argumentsList = _arguments_list;
@synthesize willStopOnOpenEvent = _will_stop_on_open_event;
-@synthesize callback = _callback;
-@synthesize shouldStopPlayback = _should_stop_playback;
-@synthesize context = _context;
@synthesize inputContext = _input_context;
@synthesize keyFIFO = _key_fifo;
-@synthesize callbackTimer = _callback_timer;
@synthesize menuItems = _menu_items;
- (id)init
@@ -119,32 +116,6 @@ static Application *app;
#undef _R
-- (void)call_callback
-{
- if (self.shouldStopPlayback(self.context)) {
- [NSApp stop:nil];
- cocoa_post_fake_event();
- } else {
- self.callback(self.context);
- }
-}
-
-- (void)schedule_timer
-{
- self.callbackTimer =
- [NSTimer timerWithTimeInterval:COCOA_MAGIC_TIMER_DELAY
- target:self
- selector:@selector(call_callback)
- userInfo:nil
- repeats:YES];
-
- [[NSRunLoop currentRunLoop] addTimer:self.callbackTimer
- forMode:NSDefaultRunLoopMode];
-
- [[NSRunLoop currentRunLoop] addTimer:self.callbackTimer
- forMode:NSEventTrackingRunLoopMode];
-}
-
- (void)stopPlayback
{
mplayer_put_key(app.keyFIFO, MP_KEY_CLOSE_WIN);
@@ -216,7 +187,8 @@ static Application *app;
self.files = [filesToOpen sortedArrayUsingSelector:@selector(compare:)];
if (self.willStopOnOpenEvent) {
- [NSApp stop:nil];
+ self.willStopOnOpenEvent = NO;
+ cocoa_stop_runloop();
} else {
[self handleFiles];
}
@@ -236,6 +208,41 @@ static Application *app;
}
@end
+struct playback_thread_ctx {
+ mpv_main_fn mpv_main;
+ int *argc;
+ char ***argv;
+};
+
+static void *playback_thread(void *ctx_obj)
+{
+ struct playback_thread_ctx *ctx = (struct playback_thread_ctx*) ctx_obj;
+ ctx->mpv_main(*ctx->argc, *ctx->argv);
+ cocoa_stop_runloop();
+ pthread_exit(NULL);
+}
+
+int cocoa_main(mpv_main_fn mpv_main, int argc, char *argv[])
+{
+ struct playback_thread_ctx ctx = {0};
+ ctx.mpv_main = mpv_main;
+ ctx.argc = &argc;
+ ctx.argv = &argv;
+
+ init_cocoa_application();
+ macosx_finder_args_preinit(&argc, &argv);
+ pthread_create(&playback_thread_id, NULL, playback_thread, &ctx);
+ cocoa_run_runloop();
+
+ // This should never be reached: cocoa_run_runloop blocks until the process
+ // is quit
+ mp_msg(MSGT_CPLAYER, MSGL_ERR, "There was either a problem initializing "
+ "Cocoa or the Runloop was stopped unexpectedly. Please report this "
+ "issues to a developer.\n");
+ pthread_join(playback_thread_id, NULL);
+ return 1;
+}
+
void cocoa_register_menu_item_action(MPMenuKey key, void* action)
{
[app registerSelector:(SEL)action forKey:key];
@@ -256,26 +263,38 @@ void terminate_cocoa_application(void)
[NSApp terminate:app];
}
-void cocoa_run_runloop(void)
+void cocoa_autorelease_pool_alloc(void)
+{
+ pool = [[NSAutoreleasePool alloc] init];
+}
+
+void cocoa_autorelease_pool_drain(void)
+{
+ [pool drain];
+}
+
+
+void cocoa_run_runloop()
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSApp run];
[pool drain];
}
-void cocoa_run_loop_schedule(play_loop_callback callback,
- should_stop_callback stop_query,
- void *context,
- struct input_ctx *input_context,
- struct mp_fifo *key_fifo)
+void cocoa_stop_runloop(void)
+{
+ [NSApp performSelectorOnMainThread:@selector(stop:)
+ withObject:nil
+ waitUntilDone:true];
+ cocoa_post_fake_event();
+}
+
+void cocoa_set_state(struct input_ctx *input_context,
+ struct mp_fifo *key_fifo)
{
[NSApp setDelegate:app];
- app.callback = callback;
- app.context = context;
- app.shouldStopPlayback = stop_query;
app.inputContext = input_context;
app.keyFIFO = key_fifo;
- [app schedule_timer];
}
void cocoa_post_fake_event(void)
diff --git a/osdep/macosx_application_objc.h b/osdep/macosx_application_objc.h
index 340b2a9b1c..b569d60f25 100644
--- a/osdep/macosx_application_objc.h
+++ b/osdep/macosx_application_objc.h
@@ -22,16 +22,10 @@
@interface Application : NSObject<NSApplicationDelegate>
- (void)initialize_menu;
- (void)registerSelector:(SEL)selector forKey:(MPMenuKey)key;
-- (void)call_callback;
-- (void)schedule_timer;
- (void)stopPlayback;
-@property(nonatomic, assign) play_loop_callback callback;
-@property(nonatomic, assign) should_stop_callback shouldStopPlayback;
-@property(nonatomic, assign) void *context;
@property(nonatomic, assign) struct input_ctx *inputContext;
@property(nonatomic, assign) struct mp_fifo *keyFIFO;
-@property(nonatomic, retain) NSTimer *callbackTimer;
@property(nonatomic, retain) NSMutableDictionary *menuItems;
@property(nonatomic, retain) NSArray *files;
@property(nonatomic, retain) NSMutableArray *argumentsList;