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 ...@@ -32,7 +32,7 @@ vlc_SOURCES = winvlc.c
noinst_DATA += vlc_win32_rc.rc noinst_DATA += vlc_win32_rc.rc
endif endif
if HAVE_DARWIN 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_LDFLAGS = $(LDFLAGS_vlc) -Wl,-framework,CoreFoundation,-framework,Cocoa
vlc_osx_LDADD = ../lib/libvlc.la vlc_osx_LDADD = ../lib/libvlc.la
endif 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$ * $Id$
* *
* Authors: Vincent Seguin <seguin@via.ecp.fr> * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
* Samuel Hocevar <sam@zoy.org> * David Fuhrmann <dfuhrmann at videolan dot org>
* Gildas Bazin <gbazin@videolan.org>
* Derk-Jan Hartman <hartman at videolan dot org>
* Lots of other people, see the libvlc AUTHORS file
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -30,70 +27,81 @@ ...@@ -30,70 +27,81 @@
#endif #endif
#include <vlc/vlc.h> #include <vlc/vlc.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <locale.h> #include <locale.h>
#include <signal.h> #include <signal.h>
#ifdef HAVE_PTHREAD_H #include <string.h>
# include <pthread.h>
#endif
#include <unistd.h>
#include <TargetConditionals.h>
#import <CoreFoundation/CoreFoundation.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; (void)data;
if (sigaction (signum, NULL, &sa)) dispatch_async(dispatch_get_main_queue(), ^{
return false; /*
return ((sa.sa_flags & SA_SIGINFO) * Stop the main loop. When using the CoreFoundation mainloop, simply
? (void *)sa.sa_sigaction : (void *)sa.sa_handler) == SIG_IGN; * CFRunLoopStop can be used.
} *
* But this does not work when having an interface.
static void vlc_kill (void *data) * 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
pthread_t *ps = data; * in the case of code inside a action method. But here, this is
pthread_kill (*ps, SIGTERM); * 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. * 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 /* The so-called POSIX-compliant MacOS X reportedly processes SIGPIPE even
* if it is blocked in all thread. * if it is blocked in all thread.
* Note: this is NOT an excuse for not protecting against SIGPIPE. If * Note: this is NOT an excuse for not protecting against SIGPIPE. If
* LibVLC runs outside of VLC, we cannot rely on this code snippet. */ * 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. */ /* Restore SIGCHLD in case our parent process ignores it. */
signal (SIGCHLD, SIG_DFL); signal(SIGCHLD, SIG_DFL);
#ifndef NDEBUG #ifndef NDEBUG
/* Activate malloc checking routines to detect heap corruptions. */ /* Activate malloc checking routines to detect heap corruptions. */
setenv ("MALLOC_CHECK_", "2", 1); setenv("MALLOC_CHECK_", "2", 1);
#endif #endif
#ifdef TOP_BUILDDIR #ifdef TOP_BUILDDIR
setenv ("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1); setenv("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1);
setenv ("VLC_DATA_PATH", TOP_SRCDIR"/share", 1); setenv("VLC_DATA_PATH", TOP_SRCDIR"/share", 1);
#endif #endif
#ifndef ALLOW_RUN_AS_ROOT #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" "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" "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]); "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[] ) ...@@ -101,34 +109,31 @@ int main( int i_argc, const char *ppsz_argv[] )
} }
#endif #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. /* This message clutters error logs. It is printed only on a TTY.
* Fortunately, LibVLC prints version info with -vv anyway. */ * 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()); libvlc_get_version(), libvlc_get_changeset());
sigset_t set; sigset_t set;
sigemptyset (&set); sigemptyset(&set);
/* VLC uses sigwait() to dequeue interesting signals. /*
* For this to work, those signals must be blocked in all threads, * The darwin version of VLC used GCD to dequeue interesting signals.
* including the thread calling sigwait() (see the man page for details). * For this to work, those signals must be blocked.
* *
* There are two advantages to sigwait() over traditional signal handlers: * There are two advantages over traditional signal handlers:
* - delivery is synchronous: no need to worry about async-safety, * - 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. * - EINTR is not generated: other threads need not handle that error.
* That being said, some LibVLC programs do not use sigwait(). Therefore * That being said, some LibVLC programs do not use sigwait(). Therefore
* EINTR must still be handled cleanly, notably from poll() calls. * EINTR must still be handled cleanly, notably from poll() calls.
* *
* Signals that request a clean shutdown, and force an unclean shutdown * Signals that request a clean shutdown.
* if they are triggered again 2+ seconds later.
* We have to handle SIGTERM cleanly because of daemon mode. */ * We have to handle SIGTERM cleanly because of daemon mode. */
sigaddset (&set, SIGINT); sigaddset(&set, SIGINT);
sigaddset (&set, SIGHUP); sigaddset(&set, SIGTERM);
sigaddset (&set, SIGQUIT);
sigaddset (&set, SIGTERM);
/* SIGPIPE can happen and would crash the process. On modern systems, /* SIGPIPE can happen and would crash the process. On modern systems,
* the MSG_NOSIGNAL flag protects socket write operations against SIGPIPE. * the MSG_NOSIGNAL flag protects socket write operations against SIGPIPE.
...@@ -138,18 +143,46 @@ int main( int i_argc, const char *ppsz_argv[] ) ...@@ -138,18 +143,46 @@ int main( int i_argc, const char *ppsz_argv[] )
* LibVLC code assumes that SIGPIPE is blocked. Other LibVLC applications * LibVLC code assumes that SIGPIPE is blocked. Other LibVLC applications
* shall block it (or handle it somehow) too. * shall block it (or handle it somehow) too.
*/ */
sigaddset (&set, SIGPIPE); sigaddset(&set, SIGPIPE);
/* SIGCHLD must be dequeued to clean up zombie child processes. /* SIGCHLD must be dequeued to clean up zombie child processes.
* Furthermore the handler must not be set to SIG_IGN (see above). * Furthermore the handler must not be set to SIG_IGN (see above).
* We cannot pragmatically handle EINTR, short reads and short writes * We cannot pragmatically handle EINTR, short reads and short writes
* in every code paths (including underlying libraries). So we just * in every code paths (including underlying libraries). So we just
* block SIGCHLD in all threads, and dequeue it below. */ * block SIGCHLD in all threads, and dequeue it below. */
sigaddset (&set, SIGCHLD); sigaddset(&set, SIGCHLD);
/* Block all these signals */ /* 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]; const char *argv[i_argc + 2];
int argc = 0; int argc = 0;
...@@ -157,7 +190,6 @@ int main( int i_argc, const char *ppsz_argv[] ) ...@@ -157,7 +190,6 @@ int main( int i_argc, const char *ppsz_argv[] )
argv[argc++] = "--media-library"; argv[argc++] = "--media-library";
/* overwrite system language on Mac */ /* overwrite system language on Mac */
#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR // TARGET_OS_MAC is unspecific
char *lang = NULL; char *lang = NULL;
for (int i = 0; i < i_argc; i++) { for (int i = 0; i < i_argc; i++) {
...@@ -192,7 +224,6 @@ int main( int i_argc, const char *ppsz_argv[] ) ...@@ -192,7 +224,6 @@ int main( int i_argc, const char *ppsz_argv[] )
CFRelease(language); CFRelease(language);
} }
} }
#endif
ppsz_argv++; i_argc--; /* skip executable path */ ppsz_argv++; i_argc--; /* skip executable path */
...@@ -200,60 +231,51 @@ int main( int i_argc, const char *ppsz_argv[] ) ...@@ -200,60 +231,51 @@ int main( int i_argc, const char *ppsz_argv[] )
* is the PSN - process serial number (a unique PID-ish thingie) * is the PSN - process serial number (a unique PID-ish thingie)
* still ok for real Darwin & when run from command line * still ok for real Darwin & when run from command line
* for example -psn_0_9306113 */ * 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--; 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; argc += i_argc;
argv[argc] = NULL; argv[argc] = NULL;
vlc_enable_override ();
pthread_t self = pthread_self ();
/* Initialize libvlc */ /* Initialize libvlc */
libvlc_instance_t *vlc = libvlc_new (argc, argv); libvlc_instance_t *vlc = libvlc_new(argc, argv);
if (vlc == NULL) if (vlc == NULL)
return 1; return 1;
int ret = 1; int ret = 1;
libvlc_set_exit_handler (vlc, vlc_kill, &self); libvlc_set_exit_handler(vlc, vlc_terminate, NULL);
libvlc_set_app_id (vlc, "org.VideoLAN.VLC", PACKAGE_VERSION, PACKAGE_NAME); 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_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; 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, } else {
* unblock it after all our child threads are created. */ [NSApp run];
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);
ret = 0; ret = 0;
/* Cleanup */ /* Cleanup */
out: out:
libvlc_release (vlc); dispatch_release(sigIntSource);
dispatch_release(sigTermSource);
dispatch_release(sigChldSource);
libvlc_release(vlc);
return ret; return ret;
} }
...@@ -77,7 +77,6 @@ ...@@ -77,7 +77,6 @@
/***************************************************************************** /*****************************************************************************
* Local prototypes. * Local prototypes.
*****************************************************************************/ *****************************************************************************/
static void Run (intf_thread_t *p_intf);
static void updateProgressPanel (void *, const char *, float); static void updateProgressPanel (void *, const char *, float);
static bool checkProgressPanel (void *); static bool checkProgressPanel (void *);
...@@ -104,6 +103,10 @@ static bool b_intf_starting = false; ...@@ -104,6 +103,10 @@ static bool b_intf_starting = false;
static vlc_mutex_t start_mutex = VLC_STATIC_MUTEX; static vlc_mutex_t start_mutex = VLC_STATIC_MUTEX;
static vlc_cond_t start_cond = VLC_STATIC_COND; 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 * OpenIntf: initialize interface
*****************************************************************************/ *****************************************************************************/
...@@ -114,13 +117,36 @@ int OpenIntf (vlc_object_t *p_this) ...@@ -114,13 +117,36 @@ int OpenIntf (vlc_object_t *p_this)
intf_thread_t *p_intf = (intf_thread_t*) p_this; intf_thread_t *p_intf = (intf_thread_t*) p_this;
msg_Dbg(p_intf, "Starting macosx interface"); 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]; [o_pool release];
return VLC_SUCCESS; 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); static int WindowControl(vout_window_t *, int i_query, va_list);
...@@ -306,48 +332,6 @@ void WindowClose(vout_window_t *p_wnd) ...@@ -306,48 +332,6 @@ void WindowClose(vout_window_t *p_wnd)
[o_pool release]; [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 -
#pragma mark Variables Callback #pragma mark Variables Callback
......
...@@ -148,7 +148,7 @@ static const char *const continue_playback_list_text[] = { ...@@ -148,7 +148,7 @@ static const char *const continue_playback_list_text[] = {
vlc_module_begin() vlc_module_begin()
set_description(N_("Mac OS X interface")) set_description(N_("Mac OS X interface"))
set_capability("interface", 200) set_capability("interface", 200)
set_callbacks(OpenIntf, NULL) set_callbacks(OpenIntf, CloseIntf)
set_category(CAT_INTERFACE) set_category(CAT_INTERFACE)
set_subcategory(SUBCAT_INTERFACE_MAIN) set_subcategory(SUBCAT_INTERFACE_MAIN)
cannot_unload_broken_library() 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