diff options
Diffstat (limited to 'video/out/cocoa_common.m')
-rw-r--r-- | video/out/cocoa_common.m | 194 |
1 files changed, 144 insertions, 50 deletions
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m index 164663c332..0420b0dda2 100644 --- a/video/out/cocoa_common.m +++ b/video/out/cocoa_common.m @@ -69,7 +69,6 @@ struct vo_cocoa_state { NSOpenGLContext *nsgl_ctx; NSScreen *current_screen; - double screen_fps; NSInteger window_level; int fullscreen; @@ -87,6 +86,11 @@ struct vo_cocoa_state { uint32_t old_dwidth; uint32_t old_dheight; + CVDisplayLinkRef link; + pthread_mutex_t sync_lock; + pthread_cond_t sync_wakeup; + uint64_t sync_counter; + pthread_mutex_t lock; pthread_cond_t wakeup; @@ -113,14 +117,36 @@ static void run_on_main_thread(struct vo *vo, void(^block)(void)) dispatch_sync(dispatch_get_main_queue(), block); } +static NSRect calculate_window_geometry(struct vo *vo, NSRect rect) +{ + struct vo_cocoa_state *s = vo->cocoa; + struct mp_vo_opts *opts = vo->opts; + + NSRect screenFrame = [s->current_screen frame]; + rect.origin.y = screenFrame.size.height - (rect.origin.y + rect.size.height); + + if(!opts->hidpi_window_scale) { + NSRect oldRect = rect; + rect = [s->current_screen convertRectFromBacking:rect]; + + CGFloat x_per = screenFrame.size.width - oldRect.size.width; + CGFloat y_per = screenFrame.size.height - oldRect.size.height; + if (x_per > 0) x_per = oldRect.origin.x/x_per; + if (y_per > 0) y_per = oldRect.origin.y/y_per; + + rect.origin.x = (screenFrame.size.width - rect.size.width)*x_per; + rect.origin.y = (screenFrame.size.height - rect.size.height)*y_per; + } + + return rect; +} + static void queue_new_video_size(struct vo *vo, int w, int h) { struct vo_cocoa_state *s = vo->cocoa; struct mp_vo_opts *opts = vo->opts; - id<MpvSizing> win = (id<MpvSizing>) s->window; - NSRect r = NSMakeRect(0, 0, w, h); - if(!opts->hidpi_window_scale) - r = [s->current_screen convertRectFromBacking:r]; + id<MpvWindowUpdate> win = (id<MpvWindowUpdate>) s->window; + NSRect r = calculate_window_geometry(vo, NSMakeRect(0, 0, w, h)); [win queueNewVideoSize:NSMakeSize(r.size.width, r.size.height)]; } @@ -283,6 +309,26 @@ static void vo_cocoa_update_screen_info(struct vo *vo) } } +static void vo_cocoa_init_displaylink(struct vo *vo) +{ + struct vo_cocoa_state *s = vo->cocoa; + + NSDictionary* sinfo = [s->current_screen deviceDescription]; + NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID did = [sid longValue]; + + CVDisplayLinkCreateWithCGDisplay(did, &s->link); + CVDisplayLinkSetOutputCallback(s->link, &displayLinkCallback, vo); + CVDisplayLinkStart(s->link); +} + +static void vo_cocoa_uninit_displaylink(struct vo_cocoa_state *s) +{ + if (CVDisplayLinkIsRunning(s->link)) + CVDisplayLinkStop(s->link); + CVDisplayLinkRelease(s->link); +} + void vo_cocoa_init(struct vo *vo) { struct vo_cocoa_state *s = talloc_zero(NULL, struct vo_cocoa_state); @@ -299,8 +345,11 @@ void vo_cocoa_init(struct vo *vo) } pthread_mutex_init(&s->lock, NULL); pthread_cond_init(&s->wakeup, NULL); + pthread_mutex_init(&s->sync_lock, NULL); + pthread_cond_init(&s->sync_wakeup, NULL); vo->cocoa = s; vo_cocoa_update_screen_info(vo); + vo_cocoa_init_displaylink(vo); cocoa_init_light_sensor(vo); cocoa_add_screen_reconfiguration_observer(vo); if (!s->embedded) { @@ -338,8 +387,22 @@ void vo_cocoa_uninit(struct vo *vo) pthread_cond_signal(&s->wakeup); pthread_mutex_unlock(&s->lock); + // close window beforehand to prevent undefined behavior when in fullscreen + // that resets the desktop to space 1 + run_on_main_thread(vo, ^{ + // if using --wid + libmpv there's no window to release + if (s->window) { + [s->window setDelegate:nil]; + [s->window close]; + } + }); + run_on_main_thread(vo, ^{ enable_power_management(s); + vo_cocoa_uninit_displaylink(s); + pthread_mutex_lock(&s->sync_lock); + pthread_cond_signal(&s->sync_wakeup); + pthread_mutex_unlock(&s->sync_lock); cocoa_uninit_light_sensor(s); cocoa_rm_screen_reconfiguration_observer(vo); @@ -353,60 +416,61 @@ void vo_cocoa_uninit(struct vo *vo) [s->view removeFromSuperview]; [s->view release]; - // if using --wid + libmpv there's no window to release - if (s->window) { - [s->window setDelegate:nil]; - [s->window close]; - } - if (!s->embedded) [s->blankCursor release]; + pthread_cond_destroy(&s->sync_wakeup); + pthread_mutex_destroy(&s->sync_lock); pthread_cond_destroy(&s->wakeup); pthread_mutex_destroy(&s->lock); talloc_free(s); }); } -static void vo_cocoa_update_screen_fps(struct vo *vo) +static void vo_cocoa_update_displaylink(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - NSDictionary* sinfo = [s->current_screen deviceDescription]; - NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"]; - CGDirectDisplayID did = [sid longValue]; + vo_cocoa_uninit_displaylink(s); + vo_cocoa_init_displaylink(vo); +} - CVDisplayLinkRef link; - CVDisplayLinkCreateWithCGDisplay(did, &link); - CVDisplayLinkSetOutputCallback(link, &displayLinkCallback, NULL); - CVDisplayLinkStart(link); - CVDisplayLinkSetCurrentCGDisplay(link, did); +static double vo_cocoa_update_screen_fps(struct vo *vo) +{ + struct vo_cocoa_state *s = vo->cocoa; + double actual_fps = CVDisplayLinkGetActualOutputVideoRefreshPeriod(s->link); + const CVTime t = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(s->link); - double display_period = CVDisplayLinkGetActualOutputVideoRefreshPeriod(link); + if (!(t.flags & kCVTimeIsIndefinite)) { + double nominal_fps = (t.timeScale / (double) t.timeValue); - if (display_period > 0) { - s->screen_fps = 1/display_period; - } else { - // Fallback to using Nominal refresh rate from DisplayLink, - // CVDisplayLinkGet *Actual* OutputVideoRefreshPeriod seems to - // return 0 on some Apple devices. Use the nominal refresh period - // instead. - const CVTime t = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); - if (!(t.flags & kCVTimeIsIndefinite)) { - s->screen_fps = (t.timeScale / (double) t.timeValue); - MP_VERBOSE(vo, "Falling back to %f for display sync.\n", s->screen_fps); + if (actual_fps > 0) + actual_fps = 1/actual_fps; + + if (fabs(actual_fps - nominal_fps) > 0.1) { + MP_VERBOSE(vo, "Falling back to nominal display " + "refresh rate: %fHz\n", nominal_fps); + return nominal_fps; + } else { + return actual_fps; } } - CVDisplayLinkRelease(link); - - flag_events(vo, VO_EVENT_WIN_STATE); + MP_WARN(vo, "Falling back to standard display refresh rate: 60Hz\n"); + return 60.0; } static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { + struct vo *vo = displayLinkContext; + struct vo_cocoa_state *s = vo->cocoa; + + pthread_mutex_lock(&s->sync_lock); + s->sync_counter += 1; + pthread_cond_signal(&s->sync_wakeup); + pthread_mutex_unlock(&s->sync_lock); return kCVReturnSuccess; } @@ -422,8 +486,10 @@ static void vo_set_level(struct vo *vo, int ontop) s->window_level = NSNormalWindowLevel; } - [[s->view window] setLevel:s->window_level]; - [s->window setLevel:s->window_level]; + [s->window setLevel:s->window_level]; + NSWindowCollectionBehavior behavior = [s->window collectionBehavior] & + ~NSWindowCollectionBehaviorTransient; + [s->window setCollectionBehavior:behavior|NSWindowCollectionBehaviorManaged]; } static int vo_cocoa_ontop(struct vo *vo) @@ -472,10 +538,8 @@ static void create_ui(struct vo *vo, struct mp_rect *win, int geo_flags) if (s->embedded) { parent = (NSView *) (intptr_t) opts->WinID; } else { - NSRect wr = - NSMakeRect(win->x0, win->y0, win->x1 - win->x0, win->y1 - win->y0); - if(!opts->hidpi_window_scale) - wr = [s->current_screen convertRectFromBacking:wr]; + NSRect wr = calculate_window_geometry(vo, + NSMakeRect(win->x0, win->y0, win->x1 - win->x0, win->y1 - win->y0)); s->window = create_window(wr, s->current_screen, opts->border, adapter); parent = [s->window contentView]; } @@ -532,13 +596,36 @@ static int cocoa_set_window_title(struct vo *vo) return VO_TRUE; } +static int vo_cocoa_window_border(struct vo *vo) +{ + struct vo_cocoa_state *s = vo->cocoa; + if (s->embedded) + return VO_NOTIMPL; + + struct mp_vo_opts *opts = vo->opts; + id<MpvWindowUpdate> win = (id<MpvWindowUpdate>) s->window; + [win updateBorder:opts->border]; + if (opts->border) + cocoa_set_window_title(vo); + + return VO_TRUE; +} + static void cocoa_screen_reconfiguration_observer( CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *ctx) { if (flags & kCGDisplaySetModeFlag) { struct vo *vo = ctx; - MP_WARN(vo, "detected display mode change, updating screen info\n"); - vo_cocoa_update_screen_fps(vo); + struct vo_cocoa_state *s = vo->cocoa; + + NSDictionary* sinfo = [s->current_screen deviceDescription]; + NSNumber* sid = [sinfo objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID did = [sid longValue]; + + if (did == display) { + MP_VERBOSE(vo, "detected display mode change, updating screen refresh rate\n"); + flag_events(vo, VO_EVENT_WIN_STATE); + } } } @@ -569,8 +656,6 @@ int vo_cocoa_config_window(struct vo *vo) struct mp_vo_opts *opts = vo->opts; run_on_main_thread(vo, ^{ - vo_cocoa_update_screen_fps(vo); - NSRect r = [s->current_screen frame]; struct mp_rect screenrc = {0, 0, r.size.width, r.size.height}; struct vo_win_geometry geo; @@ -666,6 +751,13 @@ void vo_cocoa_swap_buffers(struct vo *vo) if (skip) return; + pthread_mutex_lock(&s->sync_lock); + uint64_t old_counter = s->sync_counter; + while(old_counter == s->sync_counter) { + pthread_cond_wait(&s->sync_wakeup, &s->sync_lock); + } + pthread_mutex_unlock(&s->sync_lock); + pthread_mutex_lock(&s->lock); s->frame_w = vo->dwidth; s->frame_h = vo->dheight; @@ -727,6 +819,8 @@ static int vo_cocoa_control_on_main_thread(struct vo *vo, int request, void *arg return VO_TRUE; case VOCTRL_ONTOP: return vo_cocoa_ontop(vo); + case VOCTRL_BORDER: + return vo_cocoa_window_border(vo); case VOCTRL_GET_UNFS_WINDOW_SIZE: { int *sz = arg; NSSize size = [s->view frame].size; @@ -764,10 +858,8 @@ static int vo_cocoa_control_on_main_thread(struct vo *vo, int request, void *arg vo_cocoa_control_get_icc_profile(vo, arg); return VO_TRUE; case VOCTRL_GET_DISPLAY_FPS: - if (s->screen_fps > 0.0) { - *(double *)arg = s->screen_fps; - return VO_TRUE; - } + *(double *)arg = vo_cocoa_update_screen_fps(vo); + return VO_TRUE; break; case VOCTRL_GET_AMBIENT_LUX: if (s->light_sensor != IO_OBJECT_NULL) { @@ -885,7 +977,8 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) - (void)windowDidChangeScreen:(NSNotification *)notification { vo_cocoa_update_screen_info(self.vout); - vo_cocoa_update_screen_fps(self.vout); + vo_cocoa_update_displaylink(self.vout); + flag_events(self.vout, VO_EVENT_WIN_STATE); } - (void)windowDidEnterFullScreen:(NSNotification *)notification @@ -915,6 +1008,7 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) - (void)didChangeWindowedScreenProfile:(NSNotification *)notification { + vo_cocoa_update_screen_info(self.vout); flag_events(self.vout, VO_EVENT_ICC_PROFILE_CHANGED); } |