1 /*****************************************************************************
2 * CoreInteraction.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2011-2014 Felix Paul Kühne
7 * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #import "CoreInteraction.h"
29 #import <vlc_playlist.h>
33 #import <vlc_vout_osd.h>
35 #import <vlc_strings.h>
37 #import <vlc_modules.h>
38 #import <vlc_charset.h>
41 @implementation VLCCoreInteraction
42 static VLCCoreInteraction *_o_sharedInstance = nil;
44 + (VLCCoreInteraction *)sharedInstance
46 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
49 #pragma mark - Initialization
53 if (_o_sharedInstance) {
55 return _o_sharedInstance;
57 _o_sharedInstance = [super init];
59 return _o_sharedInstance;
64 [[NSNotificationCenter defaultCenter] removeObserver: self];
69 #pragma mark - Playback Controls
73 input_thread_t * p_input;
74 p_input = pl_CurrentInput(VLCIntf);
75 playlist_t * p_playlist = pl_Get(VLCIntf);
78 playlist_Pause(p_playlist);
79 vlc_object_release(p_input);
84 empty = playlist_IsEmpty(p_playlist);
87 if ([[[VLCMain sharedInstance] playlist] isSelectionEmpty] && ([[[VLCMain sharedInstance] playlist] currentPlaylistRoot] == p_playlist->p_local_category || [[[VLCMain sharedInstance] playlist] currentPlaylistRoot] == p_playlist->p_ml_category))
88 [[[VLCMain sharedInstance] open] openFileGeneric];
90 [[[VLCMain sharedInstance] playlist] playItem:nil];
96 playlist_t *p_playlist = pl_Get(VLCIntf);
99 bool b_playlist_playing = playlist_Status(p_playlist) == PLAYLIST_RUNNING;
102 if (b_playlist_playing)
103 playlist_Pause(p_playlist);
108 playlist_Stop(pl_Get(VLCIntf));
113 var_TriggerCallback(pl_Get(VLCIntf), "rate-faster");
118 var_TriggerCallback(pl_Get(VLCIntf), "rate-slower");
123 var_SetFloat(pl_Get(VLCIntf), "rate", 1.);
128 intf_thread_t *p_intf = VLCIntf;
132 input_thread_t * p_input;
133 p_input = pl_CurrentInput(p_intf);
135 var_ToggleBool(p_input, "record");
136 vlc_object_release(p_input);
140 - (void)setPlaybackRate:(int)i_value
142 playlist_t * p_playlist = pl_Get(VLCIntf);
144 double speed = pow(2, (double)i_value / 17);
145 int rate = INPUT_RATE_DEFAULT / speed;
146 if (i_currentPlaybackRate != rate)
147 var_SetFloat(p_playlist, "rate", (float)INPUT_RATE_DEFAULT / (float)rate);
148 i_currentPlaybackRate = rate;
155 intf_thread_t *p_intf = VLCIntf;
159 input_thread_t * p_input;
160 p_input = pl_CurrentInput(p_intf);
162 f_rate = var_GetFloat(p_input, "rate");
163 vlc_object_release(p_input);
167 playlist_t * p_playlist = pl_Get(VLCIntf);
168 f_rate = var_GetFloat(p_playlist, "rate");
171 double value = 17 * log(f_rate) / log(2.);
172 int returnValue = (int) ((value > 0) ? value + .5 : value - .5);
174 if (returnValue < -34)
176 else if (returnValue > 34)
179 i_currentPlaybackRate = returnValue;
185 playlist_Prev(pl_Get(VLCIntf));
190 playlist_Next(pl_Get(VLCIntf));
193 - (int)durationOfCurrentPlaylistItem
195 intf_thread_t *p_intf = VLCIntf;
199 input_thread_t * p_input = pl_CurrentInput(p_intf);
200 int64_t i_duration = -1;
204 input_Control(p_input, INPUT_GET_LENGTH, &i_duration);
205 vlc_object_release(p_input);
207 return (int)(i_duration / 1000000);
210 - (NSURL*)URLOfCurrentPlaylistItem
212 intf_thread_t *p_intf = VLCIntf;
216 input_thread_t *p_input = pl_CurrentInput(p_intf);
220 input_item_t *p_item = input_GetItem(p_input);
222 vlc_object_release(p_input);
226 char *psz_uri = input_item_GetURI(p_item);
228 vlc_object_release(p_input);
233 o_url = [NSURL URLWithString:[NSString stringWithUTF8String:psz_uri]];
234 vlc_object_release(p_input);
239 - (NSString*)nameOfCurrentPlaylistItem
241 intf_thread_t *p_intf = VLCIntf;
245 input_thread_t *p_input = pl_CurrentInput(p_intf);
249 input_item_t *p_item = input_GetItem(p_input);
251 vlc_object_release(p_input);
255 char *psz_uri = input_item_GetURI(p_item);
257 vlc_object_release(p_input);
262 char *format = var_InheritString(VLCIntf, "input-title-format");
263 char *formated = str_format_meta(p_input, format);
265 o_name = [NSString stringWithUTF8String:formated];
268 NSURL * o_url = [NSURL URLWithString:[NSString stringWithUTF8String:psz_uri]];
271 if ([o_name isEqualToString:@""]) {
272 if ([o_url isFileURL])
273 o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
275 o_name = [o_url absoluteString];
277 vlc_object_release(p_input);
290 [self backwardShort];
293 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
295 input_thread_t *p_input = pl_CurrentInput(VLCIntf);
299 int i_interval = var_InheritInteger( p_input, p_value );
300 if (i_interval > 0) {
301 mtime_t val = CLOCK_FREQ * i_interval;
304 var_SetTime( p_input, "time-offset", val );
306 vlc_object_release(p_input);
309 - (void)forwardExtraShort
311 [self jumpWithValue:"extrashort-jump-size" forward:YES];
314 - (void)backwardExtraShort
316 [self jumpWithValue:"extrashort-jump-size" forward:NO];
321 [self jumpWithValue:"short-jump-size" forward:YES];
324 - (void)backwardShort
326 [self jumpWithValue:"short-jump-size" forward:NO];
329 - (void)forwardMedium
331 [self jumpWithValue:"medium-jump-size" forward:YES];
334 - (void)backwardMedium
336 [self jumpWithValue:"medium-jump-size" forward:NO];
341 [self jumpWithValue:"long-jump-size" forward:YES];
346 [self jumpWithValue:"long-jump-size" forward:NO];
351 intf_thread_t *p_intf = VLCIntf;
356 playlist_t * p_playlist = pl_Get(p_intf);
357 vout_thread_t *p_vout = getVout();
359 var_Get(p_playlist, "random", &val);
360 val.b_bool = !val.b_bool;
361 var_Set(p_playlist, "random", val);
364 vout_OSDMessage(p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Random On"));
365 vlc_object_release(p_vout);
367 config_PutInt(p_playlist, "random", 1);
372 vout_OSDMessage(p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Random Off"));
373 vlc_object_release(p_vout);
375 config_PutInt(p_playlist, "random", 0);
381 intf_thread_t *p_intf = VLCIntf;
385 playlist_t * p_playlist = pl_Get(p_intf);
387 var_SetBool(p_playlist, "repeat", NO);
388 var_SetBool(p_playlist, "loop", YES);
389 config_PutInt(p_playlist, "repeat", NO);
390 config_PutInt(p_playlist, "loop", YES);
392 vout_thread_t *p_vout = getVout();
394 vout_OSDMessage(p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Repeat All"));
395 vlc_object_release(p_vout);
401 intf_thread_t *p_intf = VLCIntf;
405 playlist_t * p_playlist = pl_Get(p_intf);
407 var_SetBool(p_playlist, "repeat", YES);
408 var_SetBool(p_playlist, "loop", NO);
409 config_PutInt(p_playlist, "repeat", YES);
410 config_PutInt(p_playlist, "loop", NO);
412 vout_thread_t *p_vout = getVout();
414 vout_OSDMessage(p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Repeat One"));
415 vlc_object_release(p_vout);
421 intf_thread_t *p_intf = VLCIntf;
425 playlist_t * p_playlist = pl_Get(p_intf);
427 var_SetBool(p_playlist, "repeat", NO);
428 var_SetBool(p_playlist, "loop", NO);
429 config_PutInt(p_playlist, "repeat", NO);
430 config_PutInt(p_playlist, "loop", NO);
432 vout_thread_t *p_vout = getVout();
434 vout_OSDMessage(p_vout, SPU_DEFAULT_CHANNEL, "%s", _("Repeat Off"));
435 vlc_object_release(p_vout);
442 input_thread_t * p_input = pl_CurrentInput(VLCIntf);
444 timeA = var_GetTime(p_input, "time");
445 vlc_object_release(p_input);
448 input_thread_t * p_input = pl_CurrentInput(VLCIntf);
450 timeB = var_GetTime(p_input, "time");
451 vlc_object_release(p_input);
466 input_thread_t * p_input = pl_CurrentInput(VLCIntf);
468 mtime_t currentTime = var_GetTime(p_input, "time");
469 if ( currentTime >= timeB || currentTime < timeA)
470 var_SetTime(p_input, "time", timeA);
471 vlc_object_release(p_input);
478 intf_thread_t *p_intf = VLCIntf;
482 playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
487 intf_thread_t *p_intf = VLCIntf;
491 playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
496 intf_thread_t *p_intf = VLCIntf;
500 playlist_MuteToggle(pl_Get(p_intf));
505 intf_thread_t *p_intf = VLCIntf;
509 BOOL b_is_muted = NO;
510 b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
517 intf_thread_t *p_intf = VLCIntf;
521 float volume = playlist_VolumeGet(pl_Get(p_intf));
523 return lroundf(volume * AOUT_VOLUME_DEFAULT);
526 - (void)setVolume: (int)i_value
528 intf_thread_t *p_intf = VLCIntf;
532 if (i_value >= self.maxVolume)
533 i_value = self.maxVolume;
535 float f_value = i_value / (float)AOUT_VOLUME_DEFAULT;
537 playlist_VolumeSet(pl_Get(p_intf), f_value);
542 if (f_maxVolume == 0.) {
543 f_maxVolume = (float)var_InheritInteger(VLCIntf, "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
549 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
551 input_thread_t * p_input = pl_CurrentInput(VLCIntf);
555 NSUInteger count = [paths count];
557 for (int i = 0; i < count ; i++) {
558 const char *path = [[[paths objectAtIndex:i] path] UTF8String];
559 msg_Dbg(VLCIntf, "loading subs from %s", path);
561 int i_result = input_AddSubtitleOSD(p_input, path, true, true);
562 if (i_result != VLC_SUCCESS)
563 msg_Warn(VLCIntf, "unable to load subtitles from '%s'", path);
565 vlc_object_release(p_input);
568 #pragma mark - drag and drop support for VLCVoutView, VLCDragDropView and VLCThreePartDropView
569 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
571 NSPasteboard *o_paste = [sender draggingPasteboard];
572 NSArray *o_types = [NSArray arrayWithObject:NSFilenamesPboardType];
573 NSString *o_desired_type = [o_paste availableTypeFromArray:o_types];
574 NSData *o_carried_data = [o_paste dataForType:o_desired_type];
575 BOOL b_autoplay = config_GetInt(VLCIntf, "macosx-autoplay");
577 if (o_carried_data) {
578 if ([o_desired_type isEqualToString:NSFilenamesPboardType]) {
579 NSArray *o_array = [NSArray array];
580 NSArray *o_values = [[o_paste propertyListForType: NSFilenamesPboardType] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
581 NSUInteger count = [o_values count];
583 input_thread_t * p_input = pl_CurrentInput(VLCIntf);
585 if (count == 1 && p_input) {
586 int i_result = input_AddSubtitleOSD(p_input, [[o_values objectAtIndex:0] UTF8String], true, true);
587 vlc_object_release(p_input);
588 if (i_result == VLC_SUCCESS)
592 vlc_object_release(p_input);
594 for (NSUInteger i = 0; i < count; i++) {
596 char *psz_uri = vlc_path2uri([[o_values objectAtIndex:i] UTF8String], NULL);
600 o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
603 o_array = [o_array arrayByAddingObject: o_dic];
606 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:NO];
608 [[[VLCMain sharedInstance] playlist] appendArray: o_array atPos: -1 enqueue:YES];
616 #pragma mark - video output stuff
618 - (void)setAspectRatioIsLocked:(BOOL)b_value
620 config_PutInt(VLCIntf, "macosx-lock-aspect-ratio", b_value);
623 - (BOOL)aspectRatioIsLocked
625 return config_GetInt(VLCIntf, "macosx-lock-aspect-ratio");
628 - (void)toggleFullscreen
630 intf_thread_t *p_intf = VLCIntf;
634 vout_thread_t *p_vout = getVoutForActiveWindow();
636 BOOL b_fs = var_ToggleBool(p_vout, "fullscreen");
637 var_SetBool(pl_Get(p_intf), "fullscreen", b_fs);
638 vlc_object_release(p_vout);
639 } else { // e.g. lion fullscreen toggle
640 BOOL b_fs = var_ToggleBool(pl_Get(p_intf), "fullscreen");
641 [[[VLCMain sharedInstance] voutController] setFullscreen:b_fs forWindow:nil withAnimation:YES];
645 #pragma mark - uncommon stuff
647 - (BOOL)fixPreferences
649 NSMutableString * o_workString;
650 NSRange returnedRange;
652 BOOL b_needsRestart = NO;
654 #define fixpref(pref) \
655 o_workString = [[NSMutableString alloc] initWithFormat:@"%s", config_GetPsz(VLCIntf, pref)]; \
656 if ([o_workString length] > 0) \
658 returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
659 if (returnedRange.location != NSNotFound) \
661 if ([o_workString isEqualToString:@"macosx"]) \
662 [o_workString setString:@""]; \
663 fullRange = NSMakeRange(0, [o_workString length]); \
664 [o_workString replaceOccurrencesOfString:@":macosx" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
665 fullRange = NSMakeRange(0, [o_workString length]); \
666 [o_workString replaceOccurrencesOfString:@"macosx:" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
668 config_PutPsz(VLCIntf, pref, [o_workString UTF8String]); \
669 b_needsRestart = YES; \
672 [o_workString release]
675 fixpref("extraintf");
678 return b_needsRestart;
681 #pragma mark - video filter handling
683 - (const char *)getFilterType:(const char *)psz_name
685 module_t *p_obj = module_find(psz_name);
690 if (module_provides(p_obj, "video splitter")) {
691 return "video-splitter";
692 } else if (module_provides(p_obj, "video filter2")) {
693 return "video-filter";
694 } else if (module_provides(p_obj, "sub source")) {
696 } else if (module_provides(p_obj, "sub filter")) {
699 msg_Err(VLCIntf, "Unknown video filter type.");
704 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
706 intf_thread_t *p_intf = VLCIntf;
709 char *psz_string, *psz_parser;
711 const char *psz_filter_type = [self getFilterType:psz_name];
712 if (!psz_filter_type) {
713 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
717 msg_Dbg(p_intf, "will set filter '%s'", psz_name);
720 psz_string = config_GetPsz(p_intf, psz_filter_type);
723 if (psz_string == NULL) {
724 psz_string = strdup(psz_name);
725 } else if (strstr(psz_string, psz_name) == NULL) {
726 char *psz_tmp = strdup([[NSString stringWithFormat: @"%s:%s", psz_string, psz_name] UTF8String]);
728 psz_string = psz_tmp;
734 psz_parser = strstr(psz_string, psz_name);
736 if (*(psz_parser + strlen(psz_name)) == ':') {
737 memmove(psz_parser, psz_parser + strlen(psz_name) + 1,
738 strlen(psz_parser + strlen(psz_name) + 1) + 1);
743 /* Remove trailing : : */
744 if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
745 *(psz_string + strlen(psz_string) -1) = '\0';
751 config_PutPsz(p_intf, psz_filter_type, psz_string);
753 /* Try to set on the fly */
754 if (!strcmp(psz_filter_type, "video-splitter")) {
755 playlist_t *p_playlist = pl_Get(p_intf);
756 var_SetString(p_playlist, psz_filter_type, psz_string);
758 vout_thread_t *p_vout = getVout();
760 var_SetString(p_vout, psz_filter_type, psz_string);
761 vlc_object_release(p_vout);
768 - (void)restartFilterIfNeeded: (const char *)psz_filter option: (const char *)psz_name
770 vout_thread_t *p_vout = getVout();
771 intf_thread_t *p_intf = VLCIntf;
778 vlc_object_release(p_vout);
780 vlc_object_t *p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
783 /* we cannot rely on the p_filter existence.
784 This filter might be just
785 disabled, but the object still exists. Therefore, the string
786 is checked, additionally.
788 const char *psz_filter_type = [self getFilterType:psz_filter];
789 if (!psz_filter_type) {
790 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
794 char *psz_string = config_GetPsz(p_intf, psz_filter_type);
798 if (strstr(psz_string, psz_filter) == NULL) {
805 i_type = var_Type(p_filter, psz_name);
807 i_type = config_GetType(p_intf, psz_name);
809 if (!(i_type & VLC_VAR_ISCOMMAND)) {
810 msg_Warn(p_intf, "Brute-restarting filter '%s', because the last changed option isn't a command", psz_name);
812 [self setVideoFilter: psz_filter on: NO];
813 [self setVideoFilter: psz_filter on: YES];
815 msg_Dbg(p_intf, "restart not needed");
818 vlc_object_release(p_filter);
822 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter integer: (int)i_value
824 vout_thread_t *p_vout = getVout();
825 vlc_object_t *p_filter;
826 intf_thread_t *p_intf = VLCIntf;
830 config_PutInt(p_intf, psz_name, i_value);
833 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
836 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
837 vlc_object_release(p_vout);
840 var_SetInteger(p_filter, psz_name, i_value);
841 vlc_object_release(p_vout);
842 vlc_object_release(p_filter);
844 [self restartFilterIfNeeded: psz_filter option: psz_name];
848 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter float: (float)f_value
850 vout_thread_t *p_vout = getVout();
851 vlc_object_t *p_filter;
852 intf_thread_t *p_intf = VLCIntf;
856 config_PutFloat(p_intf, psz_name, f_value);
859 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
862 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
863 vlc_object_release(p_vout);
866 var_SetFloat(p_filter, psz_name, f_value);
867 vlc_object_release(p_vout);
868 vlc_object_release(p_filter);
870 [self restartFilterIfNeeded: psz_filter option: psz_name];
874 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter string: (const char *)psz_value
876 char *psz_new_value = strdup(psz_value);
877 vout_thread_t *p_vout = getVout();
878 vlc_object_t *p_filter;
879 intf_thread_t *p_intf = VLCIntf;
883 config_PutPsz(p_intf, psz_name, EnsureUTF8(psz_new_value));
886 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
889 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
890 vlc_object_release(p_vout);
893 var_SetString(p_filter, psz_name, EnsureUTF8(psz_new_value));
894 vlc_object_release(p_vout);
895 vlc_object_release(p_filter);
897 [self restartFilterIfNeeded: psz_filter option: psz_name];
903 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter boolean: (BOOL)b_value
905 vout_thread_t *p_vout = getVout();
906 vlc_object_t *p_filter;
907 intf_thread_t *p_intf = VLCIntf;
911 config_PutInt(p_intf, psz_name, b_value);
914 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
917 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
918 vlc_object_release(p_vout);
921 var_SetBool(p_filter, psz_name, b_value);
922 vlc_object_release(p_vout);
923 vlc_object_release(p_filter);