Commit 0eaddd99 authored by David's avatar David

macosx: Move input-related code to own class

parent d3b385e2
/*****************************************************************************
* InputManager.h: MacOS X interface module
*****************************************************************************
* Copyright (C) 2015 VLC authors and VideoLAN
* $Id$
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#import <Cocoa/Cocoa.h>
#include <vlc_common.h>
#import <vlc_interface.h>
#import <IOKit/pwr_mgt/IOPMLib.h> /* for sleep prevention */
@class VLCMain;
@interface InputManager : NSObject
{
intf_thread_t *p_intf;
VLCMain *o_main;
input_thread_t *p_current_input;
dispatch_queue_t informInputChangedQueue;
/* sleep management */
IOPMAssertionID systemSleepAssertionID;
IOPMAssertionID userActivityAssertionID;
/* iTunes/Spotify play/pause support */
BOOL b_has_itunes_paused;
BOOL b_has_spotify_paused;
NSTimer *o_itunes_play_timer;
}
- (id)initWithMain:(VLCMain *)o_mainObj;
- (void)inputThreadChanged;
- (void)playbackStatusUpdated;
- (void)resumeItunesPlayback:(id)sender;
- (BOOL)hasInput;
@end
/*****************************************************************************
* InputManager.m: MacOS X interface module
*****************************************************************************
* Copyright (C) 2015 VLC authors and VideoLAN
* $Id$
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#import "InputManager.h"
#import "CompatibilityFixes.h"
#import "ExtensionsManager.h"
#import "intf.h"
#import "MainMenu.h"
#import "MainWindow.h"
#import "playlist.h"
#import "playlistinfo.h"
#import "iTunes.h"
#import "Spotify.h"
#pragma mark Callbacks
static int InputThreadChanged(vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t new_val, void *param)
{
@autoreleasepool {
InputManager *inputManager = param;
[inputManager performSelectorOnMainThread:@selector(inputThreadChanged) withObject:nil waitUntilDone:NO];
}
return VLC_SUCCESS;
}
static int InputEvent(vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t new_val, void *param)
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
InputManager *inputManager = param;
switch (new_val.i_int) {
case INPUT_EVENT_STATE:
[inputManager performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_RATE:
[[[VLCMain sharedInstance] mainMenu] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_POSITION:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_TITLE:
case INPUT_EVENT_CHAPTER:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_CACHE:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
break;
case INPUT_EVENT_STATISTICS:
dispatch_async(dispatch_get_main_queue(), ^{
[[[VLCMain sharedInstance] info] updateStatistics];
});
break;
case INPUT_EVENT_ES:
break;
case INPUT_EVENT_TELETEXT:
break;
case INPUT_EVENT_AOUT:
break;
case INPUT_EVENT_VOUT:
break;
case INPUT_EVENT_ITEM_META:
case INPUT_EVENT_ITEM_INFO:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
[inputManager performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_BOOKMARK:
break;
case INPUT_EVENT_RECORD:
[[VLCMain sharedInstance] updateRecordState: var_GetBool(p_this, "record")];
break;
case INPUT_EVENT_PROGRAM:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_ITEM_EPG:
break;
case INPUT_EVENT_SIGNAL:
break;
case INPUT_EVENT_ITEM_NAME:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_AUDIO_DELAY:
case INPUT_EVENT_SUBTITLE_DELAY:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
break;
case INPUT_EVENT_DEAD:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO];
break;
default:
break;
}
[o_pool release];
return VLC_SUCCESS;
}
#pragma mark -
#pragma mark InputManager implementation
@implementation InputManager
- (id)initWithMain:(VLCMain *)o_mainObj
{
self = [super init];
if(self) {
p_intf = VLCIntf;
o_main = o_mainObj;
var_AddCallback(pl_Get(VLCIntf), "input-current", InputThreadChanged, self);
informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)dealloc
{
if (p_current_input) {
/* continue playback where you left off */
[[o_main playlist] storePlaybackPositionForItem:p_current_input];
var_DelCallback(p_current_input, "intf-event", InputEvent, self);
vlc_object_release(p_current_input);
p_current_input = NULL;
}
var_DelCallback(pl_Get(VLCIntf), "input-current", InputThreadChanged, self);
dispatch_release(informInputChangedQueue);
[super dealloc];
}
- (void)inputThreadChanged
{
if (p_current_input) {
var_DelCallback(p_current_input, "intf-event", InputEvent, self);
vlc_object_release(p_current_input);
p_current_input = NULL;
[[o_main mainMenu] setRateControlsEnabled: NO];
[[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
object:nil];
}
input_thread_t *p_input_changed = NULL;
// object is hold here and released then it is dead
p_current_input = playlist_CurrentInput(pl_Get(VLCIntf));
if (p_current_input) {
var_AddCallback(p_current_input, "intf-event", InputEvent, self);
[self playbackStatusUpdated];
[[o_main mainMenu] setRateControlsEnabled: YES];
if ([o_main activeVideoPlayback] && [[[o_main mainWindow] videoView] isHidden]) {
[[o_main mainWindow] changePlaylistState: psPlaylistItemChangedEvent];
}
p_input_changed = vlc_object_hold(p_current_input);
[[o_main playlist] currentlyPlayingItemChanged];
[[o_main playlist] continuePlaybackWhereYouLeftOff:p_current_input];
[[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
object:nil];
}
[self updateMetaAndInfo];
[[o_main mainWindow] updateWindow];
[o_main updateDelays];
[o_main updateMainMenu];
/*
* Due to constraints within NSAttributedString's main loop runtime handling
* and other issues, we need to inform the extension manager on a separate thread.
* The serial queue ensures that changed inputs are propagated in the same order as they arrive.
*/
dispatch_async(informInputChangedQueue, ^{
[[ExtensionsManager getInstance:p_intf] inputChanged:p_input_changed];
if (p_input_changed)
vlc_object_release(p_input_changed);
});
}
- (void)playbackStatusUpdated
{
int state = -1;
if (p_current_input) {
state = var_GetInteger(p_current_input, "state");
}
int i_control_itunes = var_InheritInteger(p_intf, "macosx-control-itunes");
// cancel itunes timer if next item starts playing
if (state > -1 && state != END_S && i_control_itunes > 0) {
if (o_itunes_play_timer) {
[o_itunes_play_timer invalidate];
o_itunes_play_timer = nil;
}
}
if (state == PLAYING_S) {
if (i_control_itunes > 0) {
// pause iTunes
if (!b_has_itunes_paused) {
iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
if (iTunesApp && [iTunesApp isRunning]) {
if ([iTunesApp playerState] == iTunesEPlSPlaying) {
msg_Dbg(p_intf, "pausing iTunes");
[iTunesApp pause];
b_has_itunes_paused = YES;
}
}
}
// pause Spotify
if (!b_has_spotify_paused) {
SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
if (spotifyApp) {
if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePlaying) {
msg_Dbg(p_intf, "pausing Spotify");
[spotifyApp pause];
b_has_spotify_paused = YES;
}
}
}
}
}
/* Declare user activity.
This wakes the display if it is off, and postpones display sleep according to the users system preferences
Available from 10.7.3 */
#ifdef MAC_OS_X_VERSION_10_7
if ([o_main activeVideoPlayback] && IOPMAssertionDeclareUserActivity)
{
CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
IOPMAssertionDeclareUserActivity(reasonForActivity,
kIOPMUserActiveLocal,
&userActivityAssertionID);
CFRelease(reasonForActivity);
}
#endif
/* prevent the system from sleeping */
if (systemSleepAssertionID > 0) {
msg_Dbg(VLCIntf, "releasing old sleep blocker (%i)" , systemSleepAssertionID);
IOPMAssertionRelease(systemSleepAssertionID);
}
IOReturn success;
/* work-around a bug in 10.7.4 and 10.7.5, so check for 10.7.x < 10.7.4, 10.8 and 10.6 */
if ((NSAppKitVersionNumber >= 1115.2 && NSAppKitVersionNumber < 1138.45) || OSX_MOUNTAIN_LION || OSX_MAVERICKS || OSX_YOSEMITE || OSX_SNOW_LEOPARD) {
CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
if ([o_main activeVideoPlayback])
success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
else
success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
CFRelease(reasonForActivity);
} else {
/* fall-back on the 10.5 mode, which also works on 10.7.4 and 10.7.5 */
if ([o_main activeVideoPlayback])
success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
else
success = IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
}
if (success == kIOReturnSuccess)
msg_Dbg(VLCIntf, "prevented sleep through IOKit (%i)", systemSleepAssertionID);
else
msg_Warn(VLCIntf, "failed to prevent system sleep through IOKit");
[[o_main mainMenu] setPause];
[[o_main mainWindow] setPause];
} else {
[[o_main mainMenu] setSubmenusEnabled: FALSE];
[[o_main mainMenu] setPlay];
[[o_main mainWindow] setPlay];
/* allow the system to sleep again */
if (systemSleepAssertionID > 0) {
msg_Dbg(VLCIntf, "releasing sleep blocker (%i)" , systemSleepAssertionID);
IOPMAssertionRelease(systemSleepAssertionID);
}
if (state == END_S || state == -1) {
/* continue playback where you left off */
if (p_current_input)
[[o_main playlist] storePlaybackPositionForItem:p_current_input];
if (i_control_itunes > 0) {
if (o_itunes_play_timer) {
[o_itunes_play_timer invalidate];
}
o_itunes_play_timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
target: self
selector: @selector(resumeItunesPlayback:)
userInfo: nil
repeats: NO];
}
}
}
[[VLCMain sharedInstance] updateMainWindow];
[self sendDistributedNotificationWithUpdatedPlaybackStatus];
}
- (void)resumeItunesPlayback:(id)sender
{
if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) {
if (b_has_itunes_paused) {
iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
if (iTunesApp && [iTunesApp isRunning]) {
if ([iTunesApp playerState] == iTunesEPlSPaused) {
msg_Dbg(p_intf, "unpausing iTunes");
[iTunesApp playpause];
}
}
}
if (b_has_spotify_paused) {
SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
if (spotifyApp) {
if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePaused) {
msg_Dbg(p_intf, "unpausing Spotify");
[spotifyApp play];
}
}
}
}
}
b_has_itunes_paused = NO;
b_has_spotify_paused = NO;
o_itunes_play_timer = nil;
}
- (void)updateMetaAndInfo
{
if (!p_current_input) {
[[o_main info] updatePanelWithItem:nil];
return;
}
input_item_t *p_input_item = input_GetItem(p_current_input);
[[[o_main playlist] model] updateItem:p_input_item];
[[o_main info] updatePanelWithItem:p_input_item];
}
- (void)sendDistributedNotificationWithUpdatedPlaybackStatus
{
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
object:nil
userInfo:nil
deliverImmediately:YES];
}
- (BOOL)hasInput
{
return p_current_input != NULL;
}
@end
......@@ -38,7 +38,8 @@ libmacosx_plugin_la_SOURCES = \
ExtensionsManager.h ExtensionsManager.m \
eyetv.h eyetv.m \
fspanel.h fspanel.m \
intf.m intf.h \
intf.h intf.m \
InputManager.h InputManager.m \
iTunes.h \
KeyboardBacklight.h KeyboardBacklight.m \
macosx.m \
......
......@@ -44,8 +44,6 @@
#import "VLCVoutWindowController.h"
#import "StringUtility.h"
#import <IOKit/pwr_mgt/IOPMLib.h> /* for sleep prevention */
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
......@@ -63,22 +61,24 @@ static NSString * VLCInputChangedNotification = @"VLCInputChangedNotification";
* VLCMain interface
*****************************************************************************/
@class AppleRemote;
@class VLCInfo;
@class VLCControls;
@class VLCMainMenu;
@class VLCPlaylist;
@class InputManager;
@interface VLCMain : NSObject <NSWindowDelegate, NSApplicationDelegate>
{
intf_thread_t *p_intf; /* The main intf object */
input_thread_t *p_current_input;
BOOL launched; /* finishedLaunching */
int items_at_launch; /* items in playlist after launch */
id o_mainmenu; /* VLCMainMenu */
VLCMainMenu *o_mainmenu; /* VLCMainMenu */
id o_prefs; /* VLCPrefs */
id o_sprefs; /* VLCSimplePrefs */
id o_open; /* VLCOpen */
id o_wizard; /* VLCWizard */
id o_coredialogs; /* VLCCoreDialogProvider */
id o_info; /* VLCInformation */
VLCInfo *o_info; /* VLCInformation */
id o_eyetv; /* VLCEyeTVController */
id o_bookmarks; /* VLCBookmarks */
id o_coreinteraction; /* VLCCoreInteraction */
......@@ -111,18 +111,9 @@ static NSString * VLCInputChangedNotification = @"VLCInputChangedNotification";
NSArray *o_usedHotkeys;
/* sleep management */
IOPMAssertionID systemSleepAssertionID;
IOPMAssertionID userActivityAssertionID;
VLCVoutWindowController *o_vout_controller;
/* iTunes/Spotify play/pause support */
BOOL b_has_itunes_paused;
BOOL b_has_spotify_paused;
NSTimer *o_itunes_play_timer;
dispatch_queue_t informInputChangedQueue;
InputManager *o_input_manager;
}
@property (readonly) VLCVoutWindowController* voutController;
......@@ -133,15 +124,15 @@ static NSString * VLCInputChangedNotification = @"VLCInputChangedNotification";
- (intf_thread_t *)intf;
- (void)setIntf:(intf_thread_t *)p_mainintf;
- (id)mainMenu;
- (VLCMainMenu *)mainMenu;
- (VLCMainWindow *)mainWindow;
- (id)controls;
- (id)bookmarks;
- (id)open;
- (id)simplePreferences;
- (id)preferences;
- (id)playlist;
- (id)info;
- (VLCPlaylist *)playlist;
- (VLCInfo *)info;
- (id)wizard;
- (id)coreDialogProvider;
- (id)eyeTVController;
......@@ -152,16 +143,12 @@ static NSString * VLCInputChangedNotification = @"VLCInputChangedNotification";
- (void)updateCurrentlyUsedHotkeys;
- (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force;
- (void)inputThreadChanged;
- (void)plItemUpdated;
- (void)playbackStatusUpdated;
- (void)sendDistributedNotificationWithUpdatedPlaybackStatus;
- (void)playbackModeUpdated;
- (void)updateVolume;
- (void)updatePlaybackPosition;
- (void)updateName;
- (void)updateRecordState: (BOOL)b_value;
- (void)updateMetaAndInfo;
- (void)updateMainMenu;
- (void)updateMainWindow;
- (void)showMainWindow;
......
......@@ -32,6 +32,8 @@
# include "config.h"
#endif
#import "intf.h"
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <vlc_common.h>
......@@ -45,7 +47,7 @@
#include <unistd.h> /* execl() */
#import "CompatibilityFixes.h"
#import "intf.h"
#import "InputManager.h"
#import "MainMenu.h"
#import "VideoView.h"
#import "prefs.h"
......@@ -72,9 +74,6 @@
#import <Sparkle/Sparkle.h> /* we're the update delegate */
#endif
#import "iTunes.h"
#import "Spotify.h"
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
......@@ -83,10 +82,6 @@ static void updateProgressPanel (void *, const char *, float);
static bool checkProgressPanel (void *);
static void destroyProgressPanel (void *);
static int InputEvent(vlc_object_t *, const char *,
vlc_value_t, vlc_value_t, void *);
static int InputThreadChanged(vlc_object_t *, const char *,
vlc_value_t, vlc_value_t, void *);
static int PLItemUpdated(vlc_object_t *, const char *,
vlc_value_t, vlc_value_t, void *);
......@@ -312,92 +307,6 @@ void WindowClose(vout_window_t *p_wnd)
#pragma mark -
#pragma mark Variables Callback
static int InputEvent(vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t new_val, void *param)
{
NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
switch (new_val.i_int) {
case INPUT_EVENT_STATE:
[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
break;
case INPUT_EVENT_RATE: