mirror of
https://github.com/mpv-player/mpv
synced 2025-01-03 05:22:23 +00:00
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.
This commit is contained in:
parent
afdc9c4ae2
commit
134f3e97bf
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -85,10 +85,7 @@ static bool RightAltPressed(NSEvent *event)
|
||||
CFSTR("PreventUserIdleDisplaySleep")
|
||||
#endif
|
||||
|
||||
@interface GLMPlayerWindow : NSWindow <NSWindowDelegate> {
|
||||
struct vo *_vo;
|
||||
}
|
||||
- (void)setVideoOutput:(struct vo *)vo;
|
||||
@interface GLMPlayerWindow : NSWindow <NSWindowDelegate>
|
||||
- (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];
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
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;
|
||||
|
||||
_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)
|
||||
{
|
||||
__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;
|
||||
}
|
||||
|
||||
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)
|
||||
rv = -1;
|
||||
} else {
|
||||
update_window(vo);
|
||||
}
|
||||
|
||||
[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_FULLSCREEN && !vo->opts->fs)
|
||||
vo_cocoa_fullscreen(vo);
|
||||
|
||||
vo_set_level(vo, opts->ontop);
|
||||
|
||||
resize_window(vo);
|
||||
|
||||
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];
|
||||
});
|
||||
|
||||
[vo->cocoa->glContext makeCurrentContext];
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static bool resize_callback_registered(struct vo *vo)
|
||||
{
|
||||
struct vo_cocoa_state *s = vo->cocoa;
|
||||
struct mp_vo_opts *opts = vo->opts;
|
||||
return s->resize_redraw;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!(s->window || s->glContext)) {
|
||||
if (create_window(vo, d_width, d_height, flags, gl3profile) < 0)
|
||||
return -1;
|
||||
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 {
|
||||
update_window(vo);
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
[s->lock unlock];
|
||||
}
|
||||
|
||||
[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_FULLSCREEN && !vo->opts->fs)
|
||||
vo_cocoa_fullscreen(vo);
|
||||
|
||||
vo_set_level(vo, opts->ontop);
|
||||
|
||||
resize_window(vo);
|
||||
|
||||
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];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user