From 134f3e97bf482b75c0eccbe5ac943a2a1d5a4ad6 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Mon, 4 Mar 2013 14:23:06 +0100 Subject: 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. --- core/mplayer.c | 51 +++---- osdep/macosx_application.h | 17 +-- osdep/macosx_application.m | 107 +++++++------ osdep/macosx_application_objc.h | 6 - video/out/cocoa_common.h | 5 +- video/out/cocoa_common.m | 324 +++++++++++++++++++++++++++------------- video/out/gl_cocoa.c | 8 + video/out/gl_common.h | 7 + video/out/vo_opengl.c | 13 ++ 9 files changed, 341 insertions(+), 197 deletions(-) diff --git a/core/mplayer.c b/core/mplayer.c index 2e79e80c14..89d463daa1 100644 --- a/core/mplayer.c +++ b/core/mplayer.c @@ -3803,33 +3803,6 @@ static void run_playloop(struct MPContext *mpctx) execute_queued_seek(mpctx); } -static void run_playloop_opaque_callback(void *context) -{ - run_playloop((struct MPContext *)context); -} - -static int check_stop_play(void *context) -{ - struct MPContext *mpctx = context; - return mpctx->stop_play; -} - -static void schedule_run_playloop(struct MPContext *mpctx) -{ - - #ifdef CONFIG_COCOA - cocoa_run_loop_schedule(run_playloop_opaque_callback, - check_stop_play, - mpctx, // passed in as opaque type - mpctx->input, - mpctx->key_fifo); - cocoa_run_runloop(); - #else - while (!check_stop_play(mpctx)) - run_playloop(mpctx); - #endif -} - static int read_keys(void *ctx, int fd) { if (getch2(ctx)) @@ -3953,6 +3926,10 @@ static void init_input(struct MPContext *mpctx) mp_input_add_key_fd(mpctx->input, 0, 1, read_keys, NULL, mpctx->key_fifo); // Set the libstream interrupt callback stream_set_interrupt_callback(mp_input_check_interrupt, mpctx->input); + +#ifdef CONFIG_COCOA + cocoa_set_state(mpctx->input, mpctx->key_fifo); +#endif } static void open_subtitles_from_options(struct MPContext *mpctx) @@ -4432,7 +4409,8 @@ goto_enable_cache: ; if (mpctx->opts.pause) pause_player(mpctx); - schedule_run_playloop(mpctx); + while (!mpctx->stop_play) + run_playloop(mpctx); mp_msg(MSGT_GLOBAL, MSGL_V, "EOF code: %d \n", mpctx->stop_play); @@ -4621,11 +4599,6 @@ static void osdep_preinit(int *p_argc, char ***p_argv) GetCpuCaps(&gCpuCaps); -#ifdef CONFIG_COCOA - init_cocoa_application(); - macosx_finder_args_preinit(p_argc, p_argv); -#endif - #ifdef __MINGW32__ mp_get_converted_argv(p_argc, p_argv); #endif @@ -4654,7 +4627,7 @@ static void osdep_preinit(int *p_argc, char ***p_argv) /* This preprocessor directive is a hack to generate a mplayer-nomain.o object * file for some tools to link against. */ #ifndef DISABLE_MAIN -int main(int argc, char *argv[]) +static int mpv_main(int argc, char *argv[]) { osdep_preinit(&argc, &argv); @@ -4747,4 +4720,14 @@ int main(int argc, char *argv[]) return 1; } + +int main(int argc, char *argv[]) +{ +#ifdef CONFIG_COCOA + cocoa_main(mpv_main, argc, argv); +#else + mpv_main(argc, argv); +#endif +} + #endif /* DISABLE_MAIN */ 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 #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 - (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; diff --git a/video/out/cocoa_common.h b/video/out/cocoa_common.h index 3450ebd547..823325ad15 100644 --- a/video/out/cocoa_common.h +++ b/video/out/cocoa_common.h @@ -24,7 +24,6 @@ struct vo_cocoa_state; -bool vo_cocoa_gui_running(void); void *vo_cocoa_glgetaddr(const char *s); int vo_cocoa_init(struct vo *vo); @@ -37,6 +36,7 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, uint32_t d_height, uint32_t flags, int gl3profile); +void vo_cocoa_set_current_context(struct vo *vo, bool current); void vo_cocoa_swap_buffers(struct vo *vo); int vo_cocoa_check_events(struct vo *vo); void vo_cocoa_fullscreen(struct vo *vo); @@ -44,6 +44,9 @@ void vo_cocoa_ontop(struct vo *vo); void vo_cocoa_pause(struct vo *vo); void vo_cocoa_resume(struct vo *vo); +void vo_cocoa_register_resize_callback(struct vo *vo, + void (*cb)(struct vo *vo, int w, int h)); + // returns an int to conform to the gl extensions from other platforms int vo_cocoa_swap_interval(int enabled); diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m index fba906c9b2..cc8f69bc1d 100644 --- a/video/out/cocoa_common.m +++ b/video/out/cocoa_common.m @@ -85,10 +85,7 @@ static bool RightAltPressed(NSEvent *event) CFSTR("PreventUserIdleDisplaySleep") #endif -@interface GLMPlayerWindow : NSWindow { - struct vo *_vo; -} -- (void)setVideoOutput:(struct vo *)vo; +@interface GLMPlayerWindow : NSWindow - (BOOL)canBecomeKeyWindow; - (BOOL)canBecomeMainWindow; - (void)fullscreen; @@ -97,11 +94,54 @@ static bool RightAltPressed(NSEvent *event) - (int)titleHeight; - (NSRect)clipFrame:(NSRect)frame withContentAspect:(NSSize) aspect; - (void)setContentSize:(NSSize)newSize keepCentered:(BOOL)keepCentered; +@property(nonatomic, assign) struct vo *videoOutput; @end @interface GLMPlayerOpenGLView : NSView @end +struct vo_cocoa_input_queue { + NSMutableArray *fifo; +}; + +static int vo_cocoa_input_queue_free(void *ptr) +{ + struct vo_cocoa_input_queue *iq = ptr; + [iq->fifo release]; + return 0; +} + +static struct vo_cocoa_input_queue *vo_cocoa_input_queue_init(void *talloc_ctx) +{ + struct vo_cocoa_input_queue *iq = talloc_ptrtype(talloc_ctx, iq); + *iq = (struct vo_cocoa_input_queue) { + .fifo = [[NSMutableArray alloc] init], + }; + talloc_set_destructor(iq, vo_cocoa_input_queue_free); + return iq; +} + +static void cocoa_async_put_key(struct vo_cocoa_input_queue *iq, int key) +{ + @synchronized (iq->fifo) { + [iq->fifo addObject:[NSNumber numberWithInt:key]]; + } +} + +static int cocoa_sync_get_key(struct vo_cocoa_input_queue *iq) +{ + int r = -1; + + @synchronized (iq->fifo) { + if ([iq->fifo count] > 0) { + r = [[iq->fifo objectAtIndex:0] intValue]; + [iq->fifo removeObjectAtIndex:0]; + } + } + + return r; +} + struct vo_cocoa_state { GLMPlayerWindow *window; NSOpenGLContext *glContext; @@ -123,25 +163,32 @@ struct vo_cocoa_state { NSInteger window_level; + struct aspect_data aspdat; + int display_cursor; int cursor_timer; int vo_cursor_autohide_delay; bool did_resize; + bool did_async_resize; bool out_fs_resize; IOPMAssertionID power_mgmt_assertion; CGFloat accumulated_scroll; -}; -static int _instances = 0; + NSRecursiveLock *lock; + void (*resize_redraw)(struct vo *vo, int w, int h); + + struct vo_cocoa_input_queue *input_queue; +}; static struct vo_cocoa_state *vo_cocoa_init_state(struct vo *vo) { struct vo_cocoa_state *s = talloc_ptrtype(vo, s); *s = (struct vo_cocoa_state){ .did_resize = NO, + .did_async_resize = NO, .current_video_size = {0,0}, .previous_video_size = {0,0}, .windowed_mask = NSTitledWindowMask|NSClosableWindowMask| @@ -153,8 +200,11 @@ static struct vo_cocoa_state *vo_cocoa_init_state(struct vo *vo) .vo_cursor_autohide_delay = vo->opts->cursor_autohide_delay, .power_mgmt_assertion = kIOPMNullAssertionID, .accumulated_scroll = 0, + .lock = [[NSRecursiveLock alloc] init], + .input_queue = vo_cocoa_input_queue_init(s), }; if (!vo->opts->border) s->windowed_mask = NSBorderlessWindowMask; + return s; } @@ -165,9 +215,16 @@ static bool supports_hidpi(NSView *view) [view respondsToSelector:hdpi_selector]; } -bool vo_cocoa_gui_running(void) +static NSRect to_pixels(struct vo *vo, NSRect frame) { - return _instances > 0; + struct vo_cocoa_state *s = vo->cocoa; + NSView *view = [s->window contentView]; + + if (supports_hidpi(view)) { + return [view convertRectToBacking: frame]; + } else { + return frame; + } } void *vo_cocoa_glgetaddr(const char *s) @@ -208,7 +265,6 @@ int vo_cocoa_init(struct vo *vo) { vo->cocoa = vo_cocoa_init_state(vo); vo->wakeup_period = 0.02; - _instances++; disable_power_management(vo); return 1; @@ -216,17 +272,17 @@ int vo_cocoa_init(struct vo *vo) void vo_cocoa_uninit(struct vo *vo) { - struct vo_cocoa_state *s = vo->cocoa; - CGDisplayShowCursor(kCGDirectMainDisplay); - enable_power_management(vo); - [NSApp setPresentationOptions:NSApplicationPresentationDefault]; - - [s->window release]; - s->window = nil; - [s->glContext release]; - s->glContext = nil; + dispatch_sync(dispatch_get_main_queue(), ^{ + struct vo_cocoa_state *s = vo->cocoa; + CGDisplayShowCursor(kCGDirectMainDisplay); + enable_power_management(vo); + [NSApp setPresentationOptions:NSApplicationPresentationDefault]; - _instances--; + [s->window release]; + s->window = nil; + [s->glContext release]; + s->glContext = nil; + }); } void vo_cocoa_pause(struct vo *vo) @@ -239,6 +295,13 @@ void vo_cocoa_resume(struct vo *vo) disable_power_management(vo); } +void vo_cocoa_register_resize_callback(struct vo *vo, + void (*cb)(struct vo *vo, int w, int h)) +{ + struct vo_cocoa_state *s = vo->cocoa; + s->resize_redraw = cb; +} + static int current_screen_has_dock_or_menubar(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; @@ -296,23 +359,11 @@ void vo_cocoa_update_xinerama_info(struct vo *vo) vo->xinerama_y = s->screen_frame.origin.y; } -int vo_cocoa_change_attributes(struct vo *vo) -{ - return 0; -} - static void resize_window(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; NSView *view = [s->window contentView]; - NSRect frame; - - if (supports_hidpi(view)) { - frame = [view convertRectToBacking: [view frame]]; - } else { - frame = [view frame]; - } - + NSRect frame = to_pixels(vo, [view frame]); vo->dwidth = frame.size.width; vo->dheight = frame.size.height; [s->glContext update]; @@ -402,8 +453,7 @@ static int create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, [glView release]; [s->window setAcceptsMouseMovedEvents:YES]; [s->glContext setView:glView]; - [s->glContext makeCurrentContext]; - [s->window setVideoOutput:vo]; + s->window.videoOutput = vo; [s->window setDelegate:s->window]; [s->window makeMainWindow]; @@ -433,58 +483,103 @@ static void update_window(struct vo *vo) } } +static void resize_redraw(struct vo *vo, int width, int height) +{ + struct vo_cocoa_state *s = vo->cocoa; + if (s->resize_redraw) { + vo_cocoa_set_current_context(vo, true); + [s->glContext update]; + s->resize_redraw(vo, width, height); + [s->glContext flushBuffer]; + s->did_async_resize = YES; + vo_cocoa_set_current_context(vo, false); + } +} + int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, uint32_t d_height, uint32_t flags, int gl3profile) { - struct vo_cocoa_state *s = vo->cocoa; - struct mp_vo_opts *opts = vo->opts; + __block int rv = 0; + dispatch_sync(dispatch_get_main_queue(), ^{ + struct vo_cocoa_state *s = vo->cocoa; + struct mp_vo_opts *opts = vo->opts; - if (vo->config_count > 0) { - NSPoint origin = [s->window frame].origin; - vo->dx = origin.x; - vo->dy = origin.y; - } + if (vo->config_count > 0) { + NSPoint origin = [s->window frame].origin; + vo->dx = origin.x; + vo->dy = origin.y; + } - update_state_sizes(s, d_width, d_height); + s->aspdat = vo->aspdat; + update_state_sizes(s, d_width, d_height); - if (!(s->window || s->glContext)) { - if (create_window(vo, d_width, d_height, flags, gl3profile) < 0) - return -1; - } else { - update_window(vo); - } + if (!(s->window || s->glContext)) { + if (create_window(vo, d_width, d_height, flags, gl3profile) < 0) + rv = -1; + } else { + update_window(vo); + } - [s->window setFrameOrigin:NSMakePoint(vo->dx, vo->dy)]; + [s->window setFrameOrigin:NSMakePoint(vo->dx, vo->dy)]; - if (flags & VOFLAG_HIDDEN) { - [s->window orderOut:nil]; - } else { - [s->window makeKeyAndOrderFront:nil]; - [NSApp activateIgnoringOtherApps:YES]; - } + if (flags & VOFLAG_HIDDEN) { + [s->window orderOut:nil]; + } else { + [s->window makeKeyAndOrderFront:nil]; + [NSApp activateIgnoringOtherApps:YES]; + } - if (flags & VOFLAG_FULLSCREEN && !vo->opts->fs) - vo_cocoa_fullscreen(vo); + if (flags & VOFLAG_FULLSCREEN && !vo->opts->fs) + vo_cocoa_fullscreen(vo); - vo_set_level(vo, opts->ontop); + vo_set_level(vo, opts->ontop); + + resize_window(vo); - resize_window(vo); + if (s->window_title) + [s->window_title release]; - if (s->window_title) - [s->window_title release]; + s->window_title = + [[NSString alloc] initWithUTF8String:vo_get_window_title(vo)]; + [s->window setTitle: s->window_title]; + }); - s->window_title = - [[NSString alloc] initWithUTF8String:vo_get_window_title(vo)]; - [s->window setTitle: s->window_title]; + [vo->cocoa->glContext makeCurrentContext]; - return 0; + return rv; +} + +static bool resize_callback_registered(struct vo *vo) +{ + struct vo_cocoa_state *s = vo->cocoa; + return s->resize_redraw; +} + +void vo_cocoa_set_current_context(struct vo *vo, bool current) +{ + struct vo_cocoa_state *s = vo->cocoa; + if (current) { + [s->lock lock]; + [s->glContext makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + [s->lock unlock]; + } } void vo_cocoa_swap_buffers(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - [s->glContext flushBuffer]; + if (s->did_async_resize && resize_callback_registered(vo)) { + // when in live resize the GL view asynchronously updates itself from + // it's drawRect: implementation and calls flushBuffer. This means the + // backbuffer is probably in an inconsistent state, so we skip one + // flushBuffer call here on the playloop thread. + s->did_async_resize = NO; + } else { + [s->glContext flushBuffer]; + } } static void vo_cocoa_display_cursor(struct vo *vo, int requested_state) @@ -516,6 +611,9 @@ int vo_cocoa_check_events(struct vo *vo) s->cursor_timer = ms_time; } + int key = cocoa_sync_get_key(s->input_queue); + if (key >= 0) mplayer_put_key(vo->key_fifo, key); + if (s->did_resize) { s->did_resize = NO; resize_window(vo); @@ -527,9 +625,17 @@ int vo_cocoa_check_events(struct vo *vo) void vo_cocoa_fullscreen(struct vo *vo) { + // This is the secondary thread, unlock since we are going to invoke a + // method synchronously on the GUI thread using Cocoa. + vo_cocoa_set_current_context(vo, false); + struct vo_cocoa_state *s = vo->cocoa; - [s->window fullscreen]; - resize_window(vo); + [s->window performSelectorOnMainThread:@selector(fullscreen) + withObject:nil + waitUntilDone:YES]; + + // Now lock again! + vo_cocoa_set_current_context(vo, true); } int vo_cocoa_swap_interval(int enabled) @@ -567,34 +673,30 @@ int vo_cocoa_cgl_color_size(struct vo *vo) } @implementation GLMPlayerWindow -- (void)setVideoOutput:(struct vo *)vo -{ - _vo = vo; -} - +@synthesize videoOutput = _video_output; - (void)windowDidResize:(NSNotification *) notification { - if (_vo) { - struct vo_cocoa_state *s = _vo->cocoa; + if (self.videoOutput) { + struct vo_cocoa_state *s = self.videoOutput->cocoa; s->did_resize = YES; } } - - (void)fullscreen { - struct vo_cocoa_state *s = _vo->cocoa; - struct mp_vo_opts *opts = _vo->opts; + struct vo_cocoa_state *s = self.videoOutput->cocoa; + struct mp_vo_opts *opts = self.videoOutput->opts; + if (!opts->fs) { - update_screen_info(_vo); - if (current_screen_has_dock_or_menubar(_vo)) + update_screen_info(self.videoOutput); + if (current_screen_has_dock_or_menubar(self.videoOutput)) [NSApp setPresentationOptions:NSApplicationPresentationHideDock| NSApplicationPresentationHideMenuBar]; s->windowed_frame = [self frame]; [self setHasShadow:NO]; [self setStyleMask:s->fullscreen_mask]; [self setFrame:s->fsscreen_frame display:YES animate:NO]; - opts->fs = true; - vo_cocoa_display_cursor(_vo, 0); + opts->fs = VO_TRUE; + vo_cocoa_display_cursor(self.videoOutput, 0); [self setMovableByWindowBackground: NO]; } else { [NSApp setPresentationOptions:NSApplicationPresentationDefault]; @@ -608,9 +710,11 @@ int vo_cocoa_cgl_color_size(struct vo *vo) } [self setContentAspectRatio:s->current_video_size]; opts->fs = false; - vo_cocoa_display_cursor(_vo, 1); + vo_cocoa_display_cursor(self.videoOutput, 1); [self setMovableByWindowBackground: YES]; } + + resize_window(self.videoOutput); } - (BOOL)canBecomeMainWindow { return YES; } @@ -620,7 +724,8 @@ int vo_cocoa_cgl_color_size(struct vo *vo) - (BOOL)resignFirstResponder { return YES; } - (BOOL)windowShouldClose:(id)sender { - mplayer_put_key(_vo->key_fifo, MP_KEY_CLOSE_WIN); + struct vo_cocoa_state *s = self.videoOutput->cocoa; + cocoa_async_put_key(s->input_queue, MP_KEY_CLOSE_WIN); // We have to wait for MPlayer to handle this, // otherwise we are in trouble if the // MP_KEY_CLOSE_WIN handler is disabled @@ -631,8 +736,8 @@ int vo_cocoa_cgl_color_size(struct vo *vo) { // this is only valid as a starting value. it will be rewritten in the // -fullscreen method. - if (_vo) { - return !_vo->opts->fs; + if (self.videoOutput) { + return !self.videoOutput->opts->fs; } else { return NO; } @@ -640,6 +745,7 @@ int vo_cocoa_cgl_color_size(struct vo *vo) - (void)keyDown:(NSEvent *)theEvent { + struct vo_cocoa_state *s = self.videoOutput->cocoa; NSString *chars; if (RightAltPressed(theEvent)) @@ -658,14 +764,15 @@ int vo_cocoa_cgl_color_size(struct vo *vo) key |= MP_KEY_MODIFIER_ALT; if ([theEvent modifierFlags] & NSCommandKeyMask) key |= MP_KEY_MODIFIER_META; - mplayer_put_key(_vo->key_fifo, key); + + cocoa_async_put_key(s->input_queue, key); } } - (void)mouseMoved: (NSEvent *) theEvent { - if (_vo->opts->fs) - vo_cocoa_display_cursor(_vo, 1); + if (self.videoOutput->opts->fs) + vo_cocoa_display_cursor(self.videoOutput, 1); } - (void)mouseDragged:(NSEvent *)theEvent @@ -705,7 +812,7 @@ int vo_cocoa_cgl_color_size(struct vo *vo) - (void)scrollWheel:(NSEvent *)theEvent { - struct vo_cocoa_state *s = _vo->cocoa; + struct vo_cocoa_state *s = self.videoOutput->cocoa; CGFloat delta; // Use the dimention with the most delta as the scrolling one @@ -721,23 +828,24 @@ int vo_cocoa_cgl_color_size(struct vo *vo) static const CGFloat threshold = 10; while (s->accumulated_scroll >= threshold) { s->accumulated_scroll -= threshold; - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN3); + cocoa_async_put_key(s->input_queue, MP_MOUSE_BTN3); } while (s->accumulated_scroll <= -threshold) { s->accumulated_scroll += threshold; - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN4); + cocoa_async_put_key(s->input_queue, MP_MOUSE_BTN4); } } else { if (delta > 0) - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN3); + cocoa_async_put_key(s->input_queue, MP_MOUSE_BTN3); else - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN4); + cocoa_async_put_key(s->input_queue, MP_MOUSE_BTN4); } } - (void)mouseEvent:(NSEvent *)theEvent { if ([theEvent buttonNumber] >= 0 && [theEvent buttonNumber] <= 9) { + struct vo_cocoa_state *s = self.videoOutput->cocoa; int buttonNumber = [theEvent buttonNumber]; // Fix to mplayer defined button order: left, middle, right if (buttonNumber == 1) buttonNumber = 2; @@ -746,18 +854,21 @@ int vo_cocoa_cgl_color_size(struct vo *vo) case NSLeftMouseDown: case NSRightMouseDown: case NSOtherMouseDown: - mplayer_put_key(_vo->key_fifo, - (MP_MOUSE_BTN0 + buttonNumber) | MP_KEY_STATE_DOWN); + cocoa_async_put_key( + s->input_queue, + (MP_MOUSE_BTN0 + buttonNumber) | MP_KEY_STATE_DOWN); // Looks like Cocoa doesn't create MouseUp events when we are // doing the second click in a double click. Put in the key_fifo // the key that would be put from the MouseUp handling code. if([theEvent clickCount] == 2) - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN0 + buttonNumber); + cocoa_async_put_key(s->input_queue, + MP_MOUSE_BTN0 + buttonNumber); break; case NSLeftMouseUp: case NSRightMouseUp: case NSOtherMouseUp: - mplayer_put_key(_vo->key_fifo, MP_MOUSE_BTN0 + buttonNumber); + cocoa_async_put_key(s->input_queue, + MP_MOUSE_BTN0 + buttonNumber); break; } } @@ -771,10 +882,10 @@ int vo_cocoa_cgl_color_size(struct vo *vo) - (void)mulSize:(float)multiplier { - if (!_vo->opts->fs) { + if (!self.videoOutput->opts->fs) { NSSize size = { - .width = _vo->aspdat.prew * multiplier, - .height = _vo->aspdat.preh * multiplier + .width = self.videoOutput->cocoa->aspdat.prew * multiplier, + .height = self.videoOutput->cocoa->aspdat.preh * multiplier }; [self setContentSize:size keepCentered:YES]; } @@ -847,7 +958,14 @@ int vo_cocoa_cgl_color_size(struct vo *vo) @implementation GLMPlayerOpenGLView - (void)drawRect: (NSRect)rect { - [[NSColor clearColor] set]; - NSRectFill([self bounds]); + GLMPlayerWindow *window = (GLMPlayerWindow *)[self window]; + struct vo *vo = [window videoOutput]; + if (vo && resize_callback_registered(vo)) { + NSSize size = to_pixels(vo, [self bounds]).size; + resize_redraw(vo, size.width, size.height); + } else { + [[NSColor clearColor] set]; + NSRectFill([self bounds]); + } } @end diff --git a/video/out/gl_cocoa.c b/video/out/gl_cocoa.c index 81be21da58..dd8b5a865a 100644 --- a/video/out/gl_cocoa.c +++ b/video/out/gl_cocoa.c @@ -51,6 +51,11 @@ static void swapGlBuffers_cocoa(MPGLContext *ctx) vo_cocoa_swap_buffers(ctx->vo); } +static void set_current_cocoa(MPGLContext *ctx, bool current) +{ + vo_cocoa_set_current_context(ctx->vo, current); +} + void mpgl_set_backend_cocoa(MPGLContext *ctx) { ctx->config_window = config_window_cocoa; @@ -63,5 +68,8 @@ void mpgl_set_backend_cocoa(MPGLContext *ctx) ctx->vo_init = vo_cocoa_init; ctx->pause = vo_cocoa_pause; ctx->resume = vo_cocoa_resume; + ctx->register_resize_callback = vo_cocoa_register_resize_callback; ctx->vo_uninit = vo_cocoa_uninit; + ctx->set_current = set_current_cocoa; + ctx->set_current = set_current_cocoa; } diff --git a/video/out/gl_common.h b/video/out/gl_common.h index 18d1cf8879..1956b73616 100644 --- a/video/out/gl_common.h +++ b/video/out/gl_common.h @@ -131,6 +131,13 @@ typedef struct MPGLContext { void (*border)(struct vo *vo); void (*update_xinerama_info)(struct vo *vo); + // An optional function to register a resize callback in the backend that + // can be called on separate thread to handle resize events immediately + // (without waiting for vo_check_events, which will come later for the + // proper resize) + void (*register_resize_callback)(struct vo *vo, + void (*cb)(struct vo *vo, int w, int h)); + // For free use by the backend. void *priv; } MPGLContext; diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index 5d59912856..b5cdca7a82 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -156,6 +156,13 @@ static bool config_window(struct gl_priv *p, uint32_t d_width, return mpgl_config_window(p->glctx, mpgl_caps, d_width, d_height, flags); } +static void video_resize_redraw_callback(struct vo *vo, int w, int h) +{ + struct gl_priv *p = vo->priv; + gl_video_resize_redraw(p->renderer, w, h); + +} + static int config(struct vo *vo, uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, uint32_t format) @@ -169,6 +176,10 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, return -1; } + if (p->glctx->register_resize_callback) { + p->glctx->register_resize_callback(vo, video_resize_redraw_callback); + } + gl_video_config(p->renderer, format, width, height, p->vo->aspdat.prew, p->vo->aspdat.preh); @@ -353,6 +364,8 @@ static int preinit(struct vo *vo, const char *arg) if (!config_window(p, 320, 200, VOFLAG_HIDDEN)) goto err_out; + mpgl_set_context(p->glctx); + if (p->gl->SwapInterval) p->gl->SwapInterval(p->swap_interval); -- cgit v1.2.3