core: remove the 360 video viewpoint zoom
[vlc.git] / modules / gui / macosx / VLCInputManager.m
blobf8bf908f1a986445a24db1aa5f0c145bd26787b9
1 /*****************************************************************************
2  * VLCInputManager.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2015 VLC authors and VideoLAN
5  * $Id$
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
22 #import "VLCInputManager.h"
24 #import "VLCCoreInteraction.h"
25 #import "CompatibilityFixes.h"
26 #import "VLCExtensionsManager.h"
27 #import "VLCMain.h"
28 #import "VLCMainMenu.h"
29 #import "VLCMainWindow.h"
30 #import "VLCPlaylist.h"
31 #import "VLCPlaylistInfo.h"
32 #import "VLCResumeDialogController.h"
33 #import "VLCTrackSynchronizationWindowController.h"
34 #import "VLCVoutView.h"
36 #import "iTunes.h"
37 #import "Spotify.h"
39 #pragma mark Callbacks
41 static int InputThreadChanged(vlc_object_t *p_this, const char *psz_var,
42                               vlc_value_t oldval, vlc_value_t new_val, void *param)
44     @autoreleasepool {
45         VLCInputManager *inputManager = (__bridge VLCInputManager *)param;
46         [inputManager performSelectorOnMainThread:@selector(inputThreadChanged) withObject:nil waitUntilDone:NO];
47     }
49     return VLC_SUCCESS;
53 static int InputEvent(vlc_object_t *p_this, const char *psz_var,
54                       vlc_value_t oldval, vlc_value_t new_val, void *param)
56     @autoreleasepool {
57         VLCInputManager *inputManager = (__bridge VLCInputManager *)param;
59         switch (new_val.i_int) {
60             case INPUT_EVENT_STATE:
61                 [inputManager performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
62                 break;
63             case INPUT_EVENT_RATE:
64                 [[[VLCMain sharedInstance] mainMenu] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
65                 break;
66             case INPUT_EVENT_POSITION:
67                 [[[VLCMain sharedInstance] mainWindow] performSelectorOnMainThread:@selector(updateTimeSlider) withObject: nil waitUntilDone:NO];
68                 [[[VLCMain sharedInstance] statusBarIcon] performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];
69                 break;
70             case INPUT_EVENT_TITLE:
71             case INPUT_EVENT_CHAPTER:
72                 [inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
73                 break;
74             case INPUT_EVENT_CACHE:
75                 [inputManager performSelectorOnMainThread:@selector(updateMainWindow) withObject:nil waitUntilDone:NO];
76                 break;
77             case INPUT_EVENT_STATISTICS:
78                 dispatch_async(dispatch_get_main_queue(), ^{
79                     [[[VLCMain sharedInstance] currentMediaInfoPanel] updateStatistics];
80                 });
81                 break;
82             case INPUT_EVENT_ES:
83                 break;
84             case INPUT_EVENT_TELETEXT:
85                 break;
86             case INPUT_EVENT_AOUT:
87                 break;
88             case INPUT_EVENT_VOUT:
89                 break;
90             case INPUT_EVENT_ITEM_META:
91             case INPUT_EVENT_ITEM_INFO:
92                 [inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
93                 [inputManager performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
94                 [inputManager performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO];
95                 break;
96             case INPUT_EVENT_BOOKMARK:
97                 break;
98             case INPUT_EVENT_RECORD:
99                 dispatch_async(dispatch_get_main_queue(), ^{
100                     [[[VLCMain sharedInstance] mainMenu] updateRecordState: var_InheritBool(p_this, "record")];
101                 });
102                 break;
103             case INPUT_EVENT_PROGRAM:
104                 [inputManager performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
105                 break;
106             case INPUT_EVENT_ITEM_EPG:
107                 break;
108             case INPUT_EVENT_SIGNAL:
109                 break;
111             case INPUT_EVENT_ITEM_NAME:
112                 [inputManager performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
113                 break;
115             case INPUT_EVENT_AUDIO_DELAY:
116             case INPUT_EVENT_SUBTITLE_DELAY:
117                 [inputManager performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
118                 break;
120             case INPUT_EVENT_DEAD:
121                 [inputManager performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
122                 [[[VLCMain sharedInstance] mainWindow] performSelectorOnMainThread:@selector(updateTimeSlider) withObject:nil waitUntilDone:NO];
123                 break;
125             default:
126                 break;
127         }
129         return VLC_SUCCESS;
130     }
133 #pragma mark -
134 #pragma mark InputManager implementation
136 @interface VLCInputManager()
138     __weak VLCMain *o_main;
140     input_thread_t *p_current_input;
141     dispatch_queue_t informInputChangedQueue;
143     /* sleep management */
144     IOPMAssertionID systemSleepAssertionID;
145     IOPMAssertionID userActivityAssertionID;
147     /* iTunes/Spotify play/pause support */
148     BOOL b_has_itunes_paused;
149     BOOL b_has_spotify_paused;
150     NSTimer *o_itunes_play_timer;
152 @end
154 @implementation VLCInputManager
156 - (id)initWithMain:(VLCMain *)o_mainObj
158     self = [super init];
159     if(self) {
160         msg_Dbg(getIntf(), "Initializing input manager");
162         o_main = o_mainObj;
163         var_AddCallback(pl_Get(getIntf()), "input-current", InputThreadChanged, (__bridge void *)self);
165         informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);
167     }
168     return self;
171 - (void)dealloc
173     msg_Dbg(getIntf(), "Deinitializing input manager");
174     if (p_current_input) {
175         /* continue playback where you left off */
176         [[o_main playlist] storePlaybackPositionForItem:p_current_input];
178         var_DelCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
179         vlc_object_release(p_current_input);
180         p_current_input = NULL;
181     }
183     var_DelCallback(pl_Get(getIntf()), "input-current", InputThreadChanged, (__bridge void *)self);
185 #if !OS_OBJECT_USE_OBJC
186     dispatch_release(informInputChangedQueue);
187 #endif
190 - (void)inputThreadChanged
192     if (p_current_input) {
193         var_DelCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
194         vlc_object_release(p_current_input);
195         p_current_input = NULL;
197         [[o_main mainMenu] setRateControlsEnabled: NO];
199         [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
200                                                             object:nil];
201     }
203     // Cancel pending resume dialogs
204     [[[VLCMain sharedInstance] resumeDialog] cancel];
206     input_thread_t *p_input_changed = NULL;
208     // object is hold here and released then it is dead
209     p_current_input = playlist_CurrentInput(pl_Get(getIntf()));
210     if (p_current_input) {
211         var_AddCallback(p_current_input, "intf-event", InputEvent, (__bridge void *)self);
212         [self playbackStatusUpdated];
213         [[o_main mainMenu] setRateControlsEnabled: YES];
215         if ([o_main activeVideoPlayback] && [[[o_main mainWindow] videoView] isHidden]) {
216             [[o_main mainWindow] changePlaylistState: psPlaylistItemChangedEvent];
217         }
219         p_input_changed = vlc_object_hold(p_current_input);
221         [[o_main playlist] currentlyPlayingItemChanged];
223         [[o_main playlist] continuePlaybackWhereYouLeftOff:p_current_input];
225         [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification
226                                                             object:nil];
227     }
229     [self updateMetaAndInfo];
231     [self updateMainWindow];
232     [self updateDelays];
233     [self updateMainMenu];
235     /*
236      * Due to constraints within NSAttributedString's main loop runtime handling
237      * and other issues, we need to inform the extension manager on a separate thread.
238      * The serial queue ensures that changed inputs are propagated in the same order as they arrive.
239      */
240     dispatch_async(informInputChangedQueue, ^{
241         [[o_main extensionsManager] inputChanged:p_input_changed];
242         if (p_input_changed)
243             vlc_object_release(p_input_changed);
244     });
247 - (void)playbackStatusUpdated
249     intf_thread_t *p_intf = getIntf();
250     int state = -1;
251     if (p_current_input) {
252         state = var_GetInteger(p_current_input, "state");
253     }
255     int i_control_itunes = var_InheritInteger(p_intf, "macosx-control-itunes");
256     // cancel itunes timer if next item starts playing
257     if (state > -1 && state != END_S && i_control_itunes > 0) {
258         if (o_itunes_play_timer) {
259             [o_itunes_play_timer invalidate];
260             o_itunes_play_timer = nil;
261         }
262     }
264     if (state == PLAYING_S) {
265         if (i_control_itunes > 0) {
266             // pause iTunes
267             if (!b_has_itunes_paused) {
268                 iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
269                 if (iTunesApp && [iTunesApp isRunning]) {
270                     if ([iTunesApp playerState] == iTunesEPlSPlaying) {
271                         msg_Dbg(p_intf, "pausing iTunes");
272                         [iTunesApp pause];
273                         b_has_itunes_paused = YES;
274                     }
275                 }
276             }
278             // pause Spotify
279             if (!b_has_spotify_paused) {
280                 SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
282                 if (spotifyApp) {
283                     if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
284                         if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePlaying) {
285                             msg_Dbg(p_intf, "pausing Spotify");
286                             [spotifyApp pause];
287                             b_has_spotify_paused = YES;
288                         }
289                     }
290                 }
291             }
292         }
294         BOOL shouldDisableScreensaver = var_InheritBool(p_intf, "disable-screensaver");
296         /* Declare user activity.
297          This wakes the display if it is off, and postpones display sleep according to the users system preferences
298          Available from 10.7.3 */
299         if ([o_main activeVideoPlayback] && &IOPMAssertionDeclareUserActivity && shouldDisableScreensaver)
300         {
301             CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
302             IOReturn success = IOPMAssertionDeclareUserActivity(reasonForActivity,
303                                              kIOPMUserActiveLocal,
304                                              &userActivityAssertionID);
305             CFRelease(reasonForActivity);
307             if (success != kIOReturnSuccess)
308                 msg_Warn(getIntf(), "failed to declare user activity");
310         }
312         /* prevent the system from sleeping */
313         if (systemSleepAssertionID > 0) {
314             msg_Dbg(getIntf(), "releasing old sleep blocker (%i)" , systemSleepAssertionID);
315             IOPMAssertionRelease(systemSleepAssertionID);
316         }
318         IOReturn success;
319         /* work-around a bug in 10.7.4 and 10.7.5, so check for 10.7.x < 10.7.4 and 10.8 */
320         if (NSAppKitVersionNumber < 1115.2) {
321             /* fall-back on the 10.5 mode, which also works on 10.7.4 and 10.7.5 */
322             if ([o_main activeVideoPlayback] && shouldDisableScreensaver)
323                 success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
324             else
325                 success = IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
326         } else {
327             CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
328             if ([o_main activeVideoPlayback] && shouldDisableScreensaver)
329                 success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
330             else
331                 success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
332             CFRelease(reasonForActivity);
333         }
335         if (success == kIOReturnSuccess)
336             msg_Dbg(getIntf(), "prevented sleep through IOKit (%i)", systemSleepAssertionID);
337         else
338             msg_Warn(getIntf(), "failed to prevent system sleep through IOKit");
340         [[o_main mainMenu] setPause];
341         [[o_main mainWindow] setPause];
342     } else {
343         [[o_main mainMenu] setSubmenusEnabled: FALSE];
344         [[o_main mainMenu] setPlay];
345         [[o_main mainWindow] setPlay];
347         /* allow the system to sleep again */
348         if (systemSleepAssertionID > 0) {
349             msg_Dbg(getIntf(), "releasing sleep blocker (%i)" , systemSleepAssertionID);
350             IOPMAssertionRelease(systemSleepAssertionID);
351         }
353         if (state == END_S || state == -1) {
354             /* continue playback where you left off */
355             if (p_current_input)
356                 [[o_main playlist] storePlaybackPositionForItem:p_current_input];
358             if (i_control_itunes > 0) {
359                 if (o_itunes_play_timer) {
360                     [o_itunes_play_timer invalidate];
361                 }
362                 o_itunes_play_timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
363                                                                        target: self
364                                                                      selector: @selector(resumeItunesPlayback:)
365                                                                      userInfo: nil
366                                                                       repeats: NO];
367             }
368         }
369     }
371     [self updateMainWindow];
372     [self sendDistributedNotificationWithUpdatedPlaybackStatus];
376 - (void)resumeItunesPlayback:(id)sender
378     intf_thread_t *p_intf = getIntf();
379     if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) {
380         if (b_has_itunes_paused) {
381             iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
382             if (iTunesApp && [iTunesApp isRunning]) {
383                 if ([iTunesApp playerState] == iTunesEPlSPaused) {
384                     msg_Dbg(p_intf, "unpausing iTunes");
385                     [iTunesApp playpause];
386                 }
387             }
388         }
390         if (b_has_spotify_paused) {
391             SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"];
392             if (spotifyApp) {
393                 if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) {
394                     if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePaused) {
395                         msg_Dbg(p_intf, "unpausing Spotify");
396                         [spotifyApp play];
397                     }
398                 }
399             }
400         }
401     }
403     b_has_itunes_paused = NO;
404     b_has_spotify_paused = NO;
405     o_itunes_play_timer = nil;
408 - (void)updateMetaAndInfo
410     if (!p_current_input) {
411         [[[VLCMain sharedInstance] currentMediaInfoPanel] updatePanelWithItem:nil];
412         return;
413     }
415     input_item_t *p_input_item = input_GetItem(p_current_input);
417     [[[o_main playlist] model] updateItem:p_input_item];
418     [[[VLCMain sharedInstance] currentMediaInfoPanel] updatePanelWithItem:p_input_item];
421 - (void)updateMainWindow
423     [[o_main mainWindow] updateWindow];
426 - (void)updateName
428     [[o_main mainWindow] updateName];
431 - (void)updateDelays
433     [[[VLCMain sharedInstance] trackSyncPanel] updateValues];
436 - (void)updateMainMenu
438     [[o_main mainMenu] setupMenus];
439     [[o_main mainMenu] updatePlaybackRate];
440     [[VLCCoreInteraction sharedInstance] resetAtoB];
443 - (void)sendDistributedNotificationWithUpdatedPlaybackStatus
445     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
446                                                                    object:nil
447                                                                  userInfo:nil
448                                                        deliverImmediately:YES];
451 - (BOOL)hasInput
453     return p_current_input != NULL;
456 @end