Commit cc07bce0 authored by David Fuhrmann's avatar David Fuhrmann

darwinvlc/macosx: rework binary, start main loop in darwinvlc

This moves the main loop out of the macosx interface module.
Instead, the open callback only initializes the interface and the
close callback cleans up stuff. The mainloop is now started in
darwinvlc.m. In case the mac interface is not used, a CoreFoundation
mainloop is solely started to wait for termination events.

Additionally, this cleans up darwinvlc.m, and fixes signal
handling, which was dead code so far (in case the interface was used).
Now, GCD is used to catch SIGINT and SIGTERM in order to allow
ordinary shutdown.

refs #14362
close #6354
parent 021f50d9
......@@ -32,7 +32,7 @@ vlc_SOURCES = winvlc.c
noinst_DATA += vlc_win32_rc.rc
endif
if HAVE_DARWIN
vlc_osx_SOURCES = darwinvlc.m override.c
vlc_osx_SOURCES = darwinvlc.m
vlc_osx_LDFLAGS = $(LDFLAGS_vlc) -Wl,-framework,CoreFoundation,-framework,Cocoa
vlc_osx_LDADD = ../lib/libvlc.la
endif
......
/*****************************************************************************
* darwinvlc.c: the darwin-specific VLC player
* darwinvlc.m: OS X specific main executable for VLC media player
*****************************************************************************
* Copyright (C) 1998-2013 the VideoLAN team
* Copyright (C) 2013-2015 VLC authors and VideoLAN
* $Id$
*
* Authors: Vincent Seguin <seguin@via.ecp.fr>
* Samuel Hocevar <sam@zoy.org>
* Gildas Bazin <gbazin@videolan.org>
* Derk-Jan Hartman <hartman at videolan dot org>
* Lots of other people, see the libvlc AUTHORS file
* Authors: Felix Paul Kühne <fkuehne at videolan dot org>
* David Fuhrmann <dfuhrmann at videolan dot org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -30,70 +27,81 @@
#endif
#include <vlc/vlc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <locale.h>
#include <signal.h>
#ifdef HAVE_PTHREAD_H
# include <pthread.h>
#endif
#include <unistd.h>
#include <TargetConditionals.h>
#include <string.h>
#import <CoreFoundation/CoreFoundation.h>
#import <Cocoa/Cocoa.h>
extern void vlc_enable_override (void);
static bool signal_ignored (int signum)
/**
* Handler called when VLC asks to terminate the program.
*/
static void vlc_terminate(void *data)
{
struct sigaction sa;
if (sigaction (signum, NULL, &sa))
return false;
return ((sa.sa_flags & SA_SIGINFO)
? (void *)sa.sa_sigaction : (void *)sa.sa_handler) == SIG_IGN;
}
static void vlc_kill (void *data)
{
pthread_t *ps = data;
pthread_kill (*ps, SIGTERM);
}
(void)data;
dispatch_async(dispatch_get_main_queue(), ^{
/*
* Stop the main loop. When using the CoreFoundation mainloop, simply
* CFRunLoopStop can be used.
*
* But this does not work when having an interface.
* In this case, [NSApp stop:nil] needs to be used, but the used flag is only
* evaluated at the end of main loop event processing. This is always true
* in the case of code inside a action method. But here, this is
* not true and thus we need to send an dummy event to make sure the stop
* flag is actually processed by the main loop.
*/
if (NSApp == nil) {
CFRunLoopStop(CFRunLoopGetCurrent());
} else {
[NSApp stop:nil];
NSEvent* event = [NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
timestamp:0.0
windowNumber:0
context:nil
subtype:0
data1:0
data2:0];
[NSApp postEvent:event atStart:YES];
}
static void exit_timeout (int signum)
{
(void) signum;
signal (SIGINT, SIG_DFL);
});
}
/*****************************************************************************
* main: parse command line, start interface and spawn threads.
*****************************************************************************/
int main( int i_argc, const char *ppsz_argv[] )
int main(int i_argc, const char *ppsz_argv[])
{
/* The so-called POSIX-compliant MacOS X reportedly processes SIGPIPE even
* if it is blocked in all thread.
* Note: this is NOT an excuse for not protecting against SIGPIPE. If
* LibVLC runs outside of VLC, we cannot rely on this code snippet. */
signal (SIGPIPE, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
/* Restore SIGCHLD in case our parent process ignores it. */
signal (SIGCHLD, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
#ifndef NDEBUG
/* Activate malloc checking routines to detect heap corruptions. */
setenv ("MALLOC_CHECK_", "2", 1);
setenv("MALLOC_CHECK_", "2", 1);
#endif
#ifdef TOP_BUILDDIR
setenv ("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1);
setenv ("VLC_DATA_PATH", TOP_SRCDIR"/share", 1);
setenv("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1);
setenv("VLC_DATA_PATH", TOP_SRCDIR"/share", 1);
#endif
#ifndef ALLOW_RUN_AS_ROOT
if (geteuid () == 0)
if (geteuid() == 0)
{
fprintf (stderr, "VLC is not supposed to be run as root. Sorry.\n"
fprintf(stderr, "VLC is not supposed to be run as root. Sorry.\n"
"If you need to use real-time priorities and/or privileged TCP ports\n"
"you can use %s-wrapper (make sure it is Set-UID root and\n"
"cannot be run by non-trusted users first).\n", ppsz_argv[0]);
......@@ -101,34 +109,31 @@ int main( int i_argc, const char *ppsz_argv[] )
}
#endif
setlocale (LC_ALL, "");
setlocale(LC_ALL, "");
if (isatty (STDERR_FILENO))
if (isatty(STDERR_FILENO))
/* This message clutters error logs. It is printed only on a TTY.
* Fortunately, LibVLC prints version info with -vv anyway. */
fprintf (stderr, "VLC media player %s (revision %s)\n",
fprintf(stderr, "VLC media player %s (revision %s)\n",
libvlc_get_version(), libvlc_get_changeset());
sigset_t set;
sigemptyset (&set);
/* VLC uses sigwait() to dequeue interesting signals.
* For this to work, those signals must be blocked in all threads,
* including the thread calling sigwait() (see the man page for details).
sigemptyset(&set);
/*
* The darwin version of VLC used GCD to dequeue interesting signals.
* For this to work, those signals must be blocked.
*
* There are two advantages to sigwait() over traditional signal handlers:
* - delivery is synchronous: no need to worry about async-safety,
* There are two advantages over traditional signal handlers:
* - handling is done on a separate thread: no need to worry about async-safety,
* - EINTR is not generated: other threads need not handle that error.
* That being said, some LibVLC programs do not use sigwait(). Therefore
* EINTR must still be handled cleanly, notably from poll() calls.
*
* Signals that request a clean shutdown, and force an unclean shutdown
* if they are triggered again 2+ seconds later.
* Signals that request a clean shutdown.
* We have to handle SIGTERM cleanly because of daemon mode. */
sigaddset (&set, SIGINT);
sigaddset (&set, SIGHUP);
sigaddset (&set, SIGQUIT);
sigaddset (&set, SIGTERM);
sigaddset(&set, SIGINT);
sigaddset(&set, SIGTERM);
/* SIGPIPE can happen and would crash the process. On modern systems,
* the MSG_NOSIGNAL flag protects socket write operations against SIGPIPE.
......@@ -138,18 +143,46 @@ int main( int i_argc, const char *ppsz_argv[] )
* LibVLC code assumes that SIGPIPE is blocked. Other LibVLC applications
* shall block it (or handle it somehow) too.
*/
sigaddset (&set, SIGPIPE);
sigaddset(&set, SIGPIPE);
/* SIGCHLD must be dequeued to clean up zombie child processes.
* Furthermore the handler must not be set to SIG_IGN (see above).
* We cannot pragmatically handle EINTR, short reads and short writes
* in every code paths (including underlying libraries). So we just
* block SIGCHLD in all threads, and dequeue it below. */
sigaddset (&set, SIGCHLD);
sigaddset(&set, SIGCHLD);
/* Block all these signals */
pthread_sigmask (SIG_SETMASK, &set, NULL);
pthread_sigmask(SIG_SETMASK, &set, NULL);
/* Handle signals with GCD */
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t sigIntSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGINT, 0, queue);
dispatch_source_t sigTermSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGTERM, 0, queue);
dispatch_source_t sigChldSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGCHLD, 0, queue);
if (!sigIntSource || !sigTermSource || !sigChldSource)
abort();
dispatch_source_set_event_handler(sigIntSource, ^{
vlc_terminate(nil);
});
dispatch_source_set_event_handler(sigTermSource, ^{
vlc_terminate(nil);
});
dispatch_source_set_event_handler(sigChldSource, ^{
int status;
while(waitpid(-1, &status, WNOHANG) > 0)
;
});
dispatch_resume(sigIntSource);
dispatch_resume(sigTermSource);
dispatch_resume(sigChldSource);
/* Handle parameters */
const char *argv[i_argc + 2];
int argc = 0;
......@@ -157,7 +190,6 @@ int main( int i_argc, const char *ppsz_argv[] )
argv[argc++] = "--media-library";
/* overwrite system language on Mac */
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR // TARGET_OS_MAC is unspecific
char *lang = NULL;
for (int i = 0; i < i_argc; i++) {
......@@ -192,7 +224,6 @@ int main( int i_argc, const char *ppsz_argv[] )
CFRelease(language);
}
}
#endif
ppsz_argv++; i_argc--; /* skip executable path */
......@@ -200,60 +231,51 @@ int main( int i_argc, const char *ppsz_argv[] )
* is the PSN - process serial number (a unique PID-ish thingie)
* still ok for real Darwin & when run from command line
* for example -psn_0_9306113 */
if (i_argc >= 1 && !strncmp (*ppsz_argv, "-psn" , 4))
if (i_argc >= 1 && !strncmp(*ppsz_argv, "-psn" , 4))
ppsz_argv++, i_argc--;
memcpy (argv + argc, ppsz_argv, i_argc * sizeof (*argv));
memcpy (argv + argc, ppsz_argv, i_argc * sizeof(*argv));
argc += i_argc;
argv[argc] = NULL;
vlc_enable_override ();
pthread_t self = pthread_self ();
/* Initialize libvlc */
libvlc_instance_t *vlc = libvlc_new (argc, argv);
libvlc_instance_t *vlc = libvlc_new(argc, argv);
if (vlc == NULL)
return 1;
int ret = 1;
libvlc_set_exit_handler (vlc, vlc_kill, &self);
libvlc_set_app_id (vlc, "org.VideoLAN.VLC", PACKAGE_VERSION, PACKAGE_NAME);
libvlc_set_user_agent (vlc, "VLC media player", "VLC/"PACKAGE_VERSION);
libvlc_set_exit_handler(vlc, vlc_terminate, NULL);
libvlc_set_app_id(vlc, "org.VideoLAN.VLC", PACKAGE_VERSION, PACKAGE_NAME);
libvlc_set_user_agent(vlc, "VLC media player", "VLC/"PACKAGE_VERSION);
libvlc_add_intf (vlc, "hotkeys,none");
libvlc_add_intf(vlc, "hotkeys,none");
libvlc_playlist_play (vlc, -1, 0, NULL);
if (libvlc_add_intf (vlc, NULL))
if (libvlc_add_intf(vlc, NULL))
goto out;
libvlc_playlist_play(vlc, -1, 0, NULL);
/*
* Run the main loop. If the mac interface is not initialized, only the CoreFoundation
* runloop is used. Otherwise, [NSApp run] needs to be called, which setups more stuff
* before actually starting the loop.
*/
@autoreleasepool {
if(NSApp == nil) {
CFRunLoopRun();
/* Qt4 insists on catching SIGCHLD via signal handler. To work around that,
* unblock it after all our child threads are created. */
sigdelset (&set, SIGCHLD);
pthread_sigmask (SIG_SETMASK, &set, NULL);
/* Do not dequeue SIGHUP if it is ignored (nohup) */
if (signal_ignored (SIGHUP))
sigdelset (&set, SIGHUP);
/* Ignore SIGPIPE */
sigdelset (&set, SIGPIPE);
int signum;
sigwait (&set, &signum);
/* Restore default signal behaviour after 3 seconds */
sigemptyset (&set);
sigaddset (&set, SIGINT);
sigaddset (&set, SIGALRM);
signal (SIGINT, SIG_IGN);
signal (SIGALRM, exit_timeout);
pthread_sigmask (SIG_UNBLOCK, &set, NULL);
alarm (3);
} else {
[NSApp run];
}
}
ret = 0;
/* Cleanup */
out:
libvlc_release (vlc);
dispatch_release(sigIntSource);
dispatch_release(sigTermSource);
dispatch_release(sigChldSource);
libvlc_release(vlc);
return ret;
}
......@@ -77,7 +77,6 @@
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
static void Run (intf_thread_t *p_intf);
static void updateProgressPanel (void *, const char *, float);
static bool checkProgressPanel (void *);
......@@ -104,6 +103,10 @@ static bool b_intf_starting = false;
static vlc_mutex_t start_mutex = VLC_STATIC_MUTEX;
static vlc_cond_t start_cond = VLC_STATIC_COND;
static NSLock * o_appLock = nil; // controls access to f_appExit
static NSLock * o_vout_provider_lock = nil;
/*****************************************************************************
* OpenIntf: initialize interface
*****************************************************************************/
......@@ -114,13 +117,36 @@ int OpenIntf (vlc_object_t *p_this)
intf_thread_t *p_intf = (intf_thread_t*) p_this;
msg_Dbg(p_intf, "Starting macosx interface");
Run(p_intf);
[VLCApplication sharedApplication];
o_appLock = [[NSLock alloc] init];
o_vout_provider_lock = [[NSLock alloc] init];
[[VLCMain sharedInstance] setIntf: p_intf];
vlc_mutex_lock(&start_mutex);
b_intf_starting = true;
vlc_cond_signal(&start_cond);
vlc_mutex_unlock(&start_mutex);
[NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
[o_pool release];
return VLC_SUCCESS;
}
static NSLock * o_vout_provider_lock = nil;
void CloseIntf (vlc_object_t *p_this)
{
NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
msg_Dbg(p_this, "Closing macosx interface");
[[VLCMain sharedInstance] applicationWillTerminate:nil];
[o_appLock release];
[o_vout_provider_lock release];
o_vout_provider_lock = nil;
[o_pool release];
}
static int WindowControl(vout_window_t *, int i_query, va_list);
......@@ -306,48 +332,6 @@ void WindowClose(vout_window_t *p_wnd)
[o_pool release];
}
/* Used to abort the app.exec() on OSX after libvlc_Quit is called */
#include "../../../lib/libvlc_internal.h" /* libvlc_SetExitHandler */
static void QuitVLC( void *obj )
{
[[VLCApplication sharedApplication] performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
}
/*****************************************************************************
* Run: main loop
*****************************************************************************/
static NSLock * o_appLock = nil; // controls access to f_appExit
static void Run(intf_thread_t *p_intf)
{
NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
[VLCApplication sharedApplication];
o_appLock = [[NSLock alloc] init];
o_vout_provider_lock = [[NSLock alloc] init];
libvlc_SetExitHandler(p_intf->p_libvlc, QuitVLC, p_intf);
[[VLCMain sharedInstance] setIntf: p_intf];
vlc_mutex_lock(&start_mutex);
b_intf_starting = true;
vlc_cond_signal(&start_cond);
vlc_mutex_unlock(&start_mutex);
[NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
[NSApp run];
msg_Dbg(p_intf, "Run loop has been stopped");
[[VLCMain sharedInstance] applicationWillTerminate:nil];
[o_appLock release];
[o_vout_provider_lock release];
o_vout_provider_lock = nil;
[o_pool release];
raise(SIGTERM);
}
#pragma mark -
#pragma mark Variables Callback
......
......@@ -148,7 +148,7 @@ static const char *const continue_playback_list_text[] = {
vlc_module_begin()
set_description(N_("Mac OS X interface"))
set_capability("interface", 200)
set_callbacks(OpenIntf, NULL)
set_callbacks(OpenIntf, CloseIntf)
set_category(CAT_INTERFACE)
set_subcategory(SUBCAT_INTERFACE_MAIN)
cannot_unload_broken_library()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment