Use input_AddSlave instead of input_AddSubtitleOSD
[vlc.git] / modules / gui / macosx / VLCCoreInteraction.m
blob970895df4ce7ed497cc911c45eddfc2559881a4f
1 /*****************************************************************************
2  * CoreInteraction.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2011-2015 Felix Paul Kühne
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *
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.
13  *
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.
18  *
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 "VLCCoreInteraction.h"
25 #import "VLCMain.h"
26 #import "VLCOpenWindowController.h"
27 #import "VLCPlaylist.h"
28 #import <math.h>
29 #import <vlc_playlist.h>
30 #import <vlc_input.h>
31 #import <vlc_actions.h>
32 #import <vlc_vout.h>
33 #import <vlc_vout_osd.h>
34 #import <vlc/vlc.h>
35 #import <vlc_strings.h>
36 #import <vlc_url.h>
37 #import <vlc_modules.h>
38 #import <vlc_charset.h>
39 #include <vlc_plugin.h>
40 #import "SPMediaKeyTap.h"
41 #import "AppleRemote.h"
42 #import "VLCInputManager.h"
44 #import "NSSound+VLCAdditions.h"
46 static int BossCallback(vlc_object_t *p_this, const char *psz_var,
47                         vlc_value_t oldval, vlc_value_t new_val, void *param)
49     @autoreleasepool {
50         dispatch_async(dispatch_get_main_queue(), ^{
51             [[VLCCoreInteraction sharedInstance] pause];
52             [[NSApplication sharedApplication] hide:nil];
53         });
55         return VLC_SUCCESS;
56     }
59 @interface VLCCoreInteraction ()
61     int i_currentPlaybackRate;
62     mtime_t timeA, timeB;
64     float f_maxVolume;
66     /* media key support */
67     BOOL b_mediaKeySupport;
68     BOOL b_mediakeyJustJumped;
69     SPMediaKeyTap *_mediaKeyController;
70     BOOL b_mediaKeyTrapEnabled;
72     AppleRemote *_remote;
73     BOOL b_remote_button_hold; /* true as long as the user holds the left,right,plus or minus on the remote control */
75     NSArray *_usedHotkeys;
77 @end
79 @implementation VLCCoreInteraction
81 #pragma mark - Initialization
83 + (VLCCoreInteraction *)sharedInstance
85     static VLCCoreInteraction *sharedInstance = nil;
86     static dispatch_once_t pred;
88     dispatch_once(&pred, ^{
89         sharedInstance = [VLCCoreInteraction new];
90     });
92     return sharedInstance;
95 - (instancetype)init
97     self = [super init];
98     if (self) {
99         intf_thread_t *p_intf = getIntf();
101         /* init media key support */
102         b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
103         if (b_mediaKeySupport) {
104             _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
105             [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
106                                                                      [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
107                                                                      nil]];
108         }
109         [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name:VLCMediaKeySupportSettingChangedNotification object: nil];
111         /* init Apple Remote support */
112         _remote = [[AppleRemote alloc] init];
113         [_remote setClickCountEnabledButtons: kRemoteButtonPlay];
114         [_remote setDelegate: self];
116         var_AddCallback(p_intf->obj.libvlc, "intf-boss", BossCallback, (__bridge void *)self);
117     }
118     return self;
121 - (void)dealloc
123     intf_thread_t *p_intf = getIntf();
124     var_DelCallback(p_intf->obj.libvlc, "intf-boss", BossCallback, (__bridge void *)self);
125     [[NSNotificationCenter defaultCenter] removeObserver: self];
129 #pragma mark - Playback Controls
131 - (void)playOrPause
133     input_thread_t *p_input = pl_CurrentInput(getIntf());
134     playlist_t *p_playlist = pl_Get(getIntf());
136     if (p_input) {
137         playlist_TogglePause(p_playlist);
138         vlc_object_release(p_input);
140     } else {
141         PLRootType root = [[[[VLCMain sharedInstance] playlist] model] currentRootType];
142         if ([[[VLCMain sharedInstance] playlist] isSelectionEmpty] && (root == ROOT_TYPE_PLAYLIST || root == ROOT_TYPE_MEDIALIBRARY))
143             [[[VLCMain sharedInstance] open] openFileGeneric];
144         else
145             [[[VLCMain sharedInstance] playlist] playItem:nil];
146     }
149 - (void)pause
151     playlist_t *p_playlist = pl_Get(getIntf());
153     playlist_Pause(p_playlist);
156 - (void)stop
158     playlist_Stop(pl_Get(getIntf()));
161 - (void)faster
163     var_TriggerCallback(pl_Get(getIntf()), "rate-faster");
166 - (void)slower
168     var_TriggerCallback(pl_Get(getIntf()), "rate-slower");
171 - (void)normalSpeed
173     var_SetFloat(pl_Get(getIntf()), "rate", 1.);
176 - (void)toggleRecord
178     intf_thread_t *p_intf = getIntf();
179     if (!p_intf)
180         return;
182     input_thread_t * p_input;
183     p_input = pl_CurrentInput(p_intf);
184     if (p_input) {
185         var_ToggleBool(p_input, "record");
186         vlc_object_release(p_input);
187     }
190 - (void)setPlaybackRate:(int)i_value
192     playlist_t * p_playlist = pl_Get(getIntf());
194     double speed = pow(2, (double)i_value / 17);
195     int rate = INPUT_RATE_DEFAULT / speed;
196     if (i_currentPlaybackRate != rate)
197         var_SetFloat(p_playlist, "rate", (float)INPUT_RATE_DEFAULT / (float)rate);
198     i_currentPlaybackRate = rate;
201 - (int)playbackRate
203     float f_rate;
205     intf_thread_t *p_intf = getIntf();
206     if (!p_intf)
207         return 0;
209     input_thread_t * p_input;
210     p_input = pl_CurrentInput(p_intf);
211     if (p_input) {
212         f_rate = var_GetFloat(p_input, "rate");
213         vlc_object_release(p_input);
214     }
215     else
216     {
217         playlist_t * p_playlist = pl_Get(getIntf());
218         f_rate = var_GetFloat(p_playlist, "rate");
219     }
221     double value = 17 * log(f_rate) / log(2.);
222     int returnValue = (int) ((value > 0) ? value + .5 : value - .5);
224     if (returnValue < -34)
225         returnValue = -34;
226     else if (returnValue > 34)
227         returnValue = 34;
229     i_currentPlaybackRate = returnValue;
230     return returnValue;
233 - (void)previous
235     playlist_Prev(pl_Get(getIntf()));
238 - (void)next
240     playlist_Next(pl_Get(getIntf()));
243 - (int)durationOfCurrentPlaylistItem
245     intf_thread_t *p_intf = getIntf();
246     if (!p_intf)
247         return 0;
249     input_thread_t * p_input = pl_CurrentInput(p_intf);
250     int64_t i_duration = -1;
251     if (!p_input)
252         return i_duration;
254     input_Control(p_input, INPUT_GET_LENGTH, &i_duration);
255     vlc_object_release(p_input);
257     return (int)(i_duration / 1000000);
260 - (NSURL*)URLOfCurrentPlaylistItem
262     intf_thread_t *p_intf = getIntf();
263     if (!p_intf)
264         return nil;
266     input_thread_t *p_input = pl_CurrentInput(p_intf);
267     if (!p_input)
268         return nil;
270     input_item_t *p_item = input_GetItem(p_input);
271     if (!p_item) {
272         vlc_object_release(p_input);
273         return nil;
274     }
276     char *psz_uri = input_item_GetURI(p_item);
277     if (!psz_uri) {
278         vlc_object_release(p_input);
279         return nil;
280     }
282     NSURL *o_url;
283     o_url = [NSURL URLWithString:toNSStr(psz_uri)];
284     free(psz_uri);
285     vlc_object_release(p_input);
287     return o_url;
290 - (NSString*)nameOfCurrentPlaylistItem
292     intf_thread_t *p_intf = getIntf();
293     if (!p_intf)
294         return nil;
296     input_thread_t *p_input = pl_CurrentInput(p_intf);
297     if (!p_input)
298         return nil;
300     input_item_t *p_item = input_GetItem(p_input);
301     if (!p_item) {
302         vlc_object_release(p_input);
303         return nil;
304     }
306     char *psz_uri = input_item_GetURI(p_item);
307     if (!psz_uri) {
308         vlc_object_release(p_input);
309         return nil;
310     }
312     NSString *o_name = @"";
313     char *format = var_InheritString(getIntf(), "input-title-format");
314     if (format) {
315         char *formated = vlc_strfinput(p_input, format);
316         free(format);
317         o_name = toNSStr(formated);
318         free(formated);
319     }
321     NSURL * o_url = [NSURL URLWithString:toNSStr(psz_uri)];
322     free(psz_uri);
324     if ([o_name isEqualToString:@""]) {
325         if ([o_url isFileURL])
326             o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
327         else
328             o_name = [o_url absoluteString];
329     }
330     vlc_object_release(p_input);
331     return o_name;
334 - (void)forward
336     //LEGACY SUPPORT
337     [self forwardShort];
340 - (void)backward
342     //LEGACY SUPPORT
343     [self backwardShort];
346 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
348     input_thread_t *p_input = pl_CurrentInput(getIntf());
349     if (!p_input)
350         return;
352     int i_interval = var_InheritInteger( p_input, p_value );
353     if (i_interval > 0) {
354         mtime_t val = CLOCK_FREQ * i_interval;
355         if (!b_value)
356             val = val * -1;
357         var_SetInteger( p_input, "time-offset", val );
358     }
359     vlc_object_release(p_input);
362 - (void)forwardExtraShort
364     [self jumpWithValue:"extrashort-jump-size" forward:YES];
367 - (void)backwardExtraShort
369     [self jumpWithValue:"extrashort-jump-size" forward:NO];
372 - (void)forwardShort
374     [self jumpWithValue:"short-jump-size" forward:YES];
377 - (void)backwardShort
379     [self jumpWithValue:"short-jump-size" forward:NO];
382 - (void)forwardMedium
384     [self jumpWithValue:"medium-jump-size" forward:YES];
387 - (void)backwardMedium
389     [self jumpWithValue:"medium-jump-size" forward:NO];
392 - (void)forwardLong
394     [self jumpWithValue:"long-jump-size" forward:YES];
397 - (void)backwardLong
399     [self jumpWithValue:"long-jump-size" forward:NO];
402 - (void)shuffle
404     intf_thread_t *p_intf = getIntf();
405     if (!p_intf)
406         return;
408     vlc_value_t val;
409     playlist_t * p_playlist = pl_Get(p_intf);
410     vout_thread_t *p_vout = getVout();
412     var_Get(p_playlist, "random", &val);
413     val.b_bool = !val.b_bool;
414     var_Set(p_playlist, "random", val);
415     if (val.b_bool) {
416         if (p_vout) {
417             vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random On"));
418             vlc_object_release(p_vout);
419         }
420         config_PutInt(p_playlist, "random", 1);
421     }
422     else
423     {
424         if (p_vout) {
425             vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random Off"));
426             vlc_object_release(p_vout);
427         }
428         config_PutInt(p_playlist, "random", 0);
429     }
432 - (void)repeatAll
434     intf_thread_t *p_intf = getIntf();
435     if (!p_intf)
436         return;
438     playlist_t * p_playlist = pl_Get(p_intf);
440     var_SetBool(p_playlist, "repeat", NO);
441     var_SetBool(p_playlist, "loop", YES);
442     config_PutInt(p_playlist, "repeat", NO);
443     config_PutInt(p_playlist, "loop", YES);
445     vout_thread_t *p_vout = getVout();
446     if (p_vout) {
447         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat All"));
448         vlc_object_release(p_vout);
449     }
452 - (void)repeatOne
454     intf_thread_t *p_intf = getIntf();
455     if (!p_intf)
456         return;
458     playlist_t * p_playlist = pl_Get(p_intf);
460     var_SetBool(p_playlist, "repeat", YES);
461     var_SetBool(p_playlist, "loop", NO);
462     config_PutInt(p_playlist, "repeat", YES);
463     config_PutInt(p_playlist, "loop", NO);
465     vout_thread_t *p_vout = getVout();
466     if (p_vout) {
467         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat One"));
468         vlc_object_release(p_vout);
469     }
472 - (void)repeatOff
474     intf_thread_t *p_intf = getIntf();
475     if (!p_intf)
476         return;
478     playlist_t * p_playlist = pl_Get(p_intf);
480     var_SetBool(p_playlist, "repeat", NO);
481     var_SetBool(p_playlist, "loop", NO);
482     config_PutInt(p_playlist, "repeat", NO);
483     config_PutInt(p_playlist, "loop", NO);
485     vout_thread_t *p_vout = getVout();
486     if (p_vout) {
487         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat Off"));
488         vlc_object_release(p_vout);
489     }
492 - (void)setAtoB
494     if (!timeA) {
495         input_thread_t * p_input = pl_CurrentInput(getIntf());
496         if (p_input) {
497             timeA = var_GetInteger(p_input, "time");
498             vlc_object_release(p_input);
499         }
500     } else if (!timeB) {
501         input_thread_t * p_input = pl_CurrentInput(getIntf());
502         if (p_input) {
503             timeB = var_GetInteger(p_input, "time");
504             vlc_object_release(p_input);
505         }
506     } else
507         [self resetAtoB];
510 - (void)resetAtoB
512     timeA = 0;
513     timeB = 0;
516 - (void)updateAtoB
518     if (timeB) {
519         input_thread_t * p_input = pl_CurrentInput(getIntf());
520         if (p_input) {
521             mtime_t currentTime = var_GetInteger(p_input, "time");
522             if ( currentTime >= timeB || currentTime < timeA)
523                 var_SetInteger(p_input, "time", timeA);
524             vlc_object_release(p_input);
525         }
526     }
529 - (void)volumeUp
531     intf_thread_t *p_intf = getIntf();
532     if (!p_intf)
533         return;
535     playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
538 - (void)volumeDown
540     intf_thread_t *p_intf = getIntf();
541     if (!p_intf)
542         return;
544     playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
547 - (void)toggleMute
549     intf_thread_t *p_intf = getIntf();
550     if (!p_intf)
551         return;
553     playlist_MuteToggle(pl_Get(p_intf));
556 - (BOOL)mute
558     intf_thread_t *p_intf = getIntf();
559     if (!p_intf)
560         return NO;
562     BOOL b_is_muted = NO;
563     b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
565     return b_is_muted;
568 - (int)volume
570     intf_thread_t *p_intf = getIntf();
571     if (!p_intf)
572         return 0;
574     float volume = playlist_VolumeGet(pl_Get(p_intf));
576     return lroundf(volume * AOUT_VOLUME_DEFAULT);
579 - (void)setVolume: (int)i_value
581     intf_thread_t *p_intf = getIntf();
582     if (!p_intf)
583         return;
585     if (i_value >= self.maxVolume)
586         i_value = self.maxVolume;
588     float f_value = i_value / (float)AOUT_VOLUME_DEFAULT;
590     playlist_VolumeSet(pl_Get(p_intf), f_value);
593 - (float)maxVolume
595     if (f_maxVolume == 0.) {
596         f_maxVolume = (float)var_InheritInteger(getIntf(), "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
597     }
599     return f_maxVolume;
602 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
604     input_thread_t * p_input = pl_CurrentInput(getIntf());
605     if (!p_input)
606         return;
608     NSUInteger count = [paths count];
610     for (int i = 0; i < count ; i++) {
611         char *mrl = vlc_path2uri([[[paths objectAtIndex:i] path] UTF8String], NULL);
612         if (!mrl)
613             continue;
614         msg_Dbg(getIntf(), "loading subs from %s", mrl);
616         int i_result = input_AddSlave(p_input, SLAVE_TYPE_SPU, mrl, true, true);
617         if (i_result != VLC_SUCCESS)
618             msg_Warn(getIntf(), "unable to load subtitles from '%s'", mrl);
619         free(mrl);
620     }
621     vlc_object_release(p_input);
624 - (void)showPosition
626     input_thread_t *p_input = pl_CurrentInput(getIntf());
627     if (p_input != NULL) {
628         vout_thread_t *p_vout = input_GetVout(p_input);
629         if (p_vout != NULL) {
630             var_SetInteger(getIntf()->obj.libvlc, "key-action", ACTIONID_POSITION);
631             vlc_object_release(p_vout);
632         }
633         vlc_object_release(p_input);
634     }
637 #pragma mark - Drop support for files into the video, controls bar or drop box
639 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
641     NSArray *items = [[[VLCMain sharedInstance] playlist] createItemsFromExternalPasteboard:[sender draggingPasteboard]];
643     if (items.count == 0)
644         return NO;
646     [[[VLCMain sharedInstance] playlist] addPlaylistItems:items tryAsSubtitle:YES];
647     return YES;
650 #pragma mark - video output stuff
652 - (void)setAspectRatioIsLocked:(BOOL)b_value
654     config_PutInt(getIntf(), "macosx-lock-aspect-ratio", b_value);
657 - (BOOL)aspectRatioIsLocked
659     return config_GetInt(getIntf(), "macosx-lock-aspect-ratio");
662 - (void)toggleFullscreen
664     intf_thread_t *p_intf = getIntf();
665     if (!p_intf)
666         return;
668     vout_thread_t *p_vout = getVoutForActiveWindow();
669     if (p_vout) {
670         BOOL b_fs = var_ToggleBool(p_vout, "fullscreen");
671         var_SetBool(pl_Get(p_intf), "fullscreen", b_fs);
672         vlc_object_release(p_vout);
673     } else { // e.g. lion fullscreen toggle
674         BOOL b_fs = var_ToggleBool(pl_Get(p_intf), "fullscreen");
675         [[[VLCMain sharedInstance] voutController] setFullscreen:b_fs forWindow:nil withAnimation:YES];
676     }
679 #pragma mark - uncommon stuff
681 - (BOOL)fixIntfSettings
683     NSMutableString * o_workString;
684     NSRange returnedRange;
685     NSRange fullRange;
686     BOOL b_needsRestart = NO;
688     #define fixpref(pref) \
689     o_workString = [[NSMutableString alloc] initWithFormat:@"%s", config_GetPsz(getIntf(), pref)]; \
690     if ([o_workString length] > 0) \
691     { \
692         returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
693         if (returnedRange.location != NSNotFound) \
694         { \
695             if ([o_workString isEqualToString:@"macosx"]) \
696                 [o_workString setString:@""]; \
697             fullRange = NSMakeRange(0, [o_workString length]); \
698             [o_workString replaceOccurrencesOfString:@":macosx" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
699             fullRange = NSMakeRange(0, [o_workString length]); \
700             [o_workString replaceOccurrencesOfString:@"macosx:" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
701             \
702             config_PutPsz(getIntf(), pref, [o_workString UTF8String]); \
703             b_needsRestart = YES; \
704         } \
705     }
707     fixpref("control");
708     fixpref("extraintf");
709     #undef fixpref
711     return b_needsRestart;
714 #pragma mark - video filter handling
716 - (const char *)getFilterType:(const char *)psz_name
718     module_t *p_obj = module_find(psz_name);
719     if (!p_obj) {
720         return NULL;
721     }
723     if (module_provides(p_obj, "video splitter")) {
724         return "video-splitter";
725     } else if (module_provides(p_obj, "video filter")) {
726         return "video-filter";
727     } else if (module_provides(p_obj, "sub source")) {
728         return "sub-source";
729     } else if (module_provides(p_obj, "sub filter")) {
730         return "sub-filter";
731     } else {
732         msg_Err(getIntf(), "Unknown video filter type.");
733         return NULL;
734     }
737 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
739     intf_thread_t *p_intf = getIntf();
740     if (!p_intf)
741         return;
742     playlist_t *p_playlist = pl_Get(p_intf);
743     char *psz_string, *psz_parser;
745     const char *psz_filter_type = [self getFilterType:psz_name];
746     if (!psz_filter_type) {
747         msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
748         return;
749     }
751     msg_Dbg(p_intf, "will turn filter '%s' %s", psz_name, b_on ? "on" : "off");
753     psz_string = var_InheritString(p_playlist, psz_filter_type);
755     if (b_on) {
756         if (psz_string == NULL) {
757             psz_string = strdup(psz_name);
758         } else if (strstr(psz_string, psz_name) == NULL) {
759             char *psz_tmp = strdup([[NSString stringWithFormat: @"%s:%s", psz_string, psz_name] UTF8String]);
760             free(psz_string);
761             psz_string = psz_tmp;
762         }
763     } else {
764         if (!psz_string)
765             return;
767         psz_parser = strstr(psz_string, psz_name);
768         if (psz_parser) {
769             if (*(psz_parser + strlen(psz_name)) == ':') {
770                 memmove(psz_parser, psz_parser + strlen(psz_name) + 1,
771                         strlen(psz_parser + strlen(psz_name) + 1) + 1);
772             } else {
773                 *psz_parser = '\0';
774             }
776             /* Remove trailing : : */
777             if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
778                 *(psz_string + strlen(psz_string) -1) = '\0';
779         } else {
780             free(psz_string);
781             return;
782         }
783     }
784     var_SetString(p_playlist, psz_filter_type, psz_string);
786     /* Try to set non splitter filters on the fly */
787     if (strcmp(psz_filter_type, "video-splitter")) {
788         NSArray<NSValue *> *vouts = getVouts();
789         if (vouts)
790             for (NSValue * val in vouts) {
791                 vout_thread_t *p_vout = [val pointerValue];
792                 var_SetString(p_vout, psz_filter_type, psz_string);
793                 vlc_object_release(p_vout);
794             }
795     }
797     free(psz_string);
800 - (void)setVideoFilterProperty: (char const *)psz_property
801                      forFilter: (char const *)psz_filter
802                      withValue: (vlc_value_t)value
804     NSArray<NSValue *> *vouts = getVouts();
805     intf_thread_t *p_intf = getIntf();
806     playlist_t *p_playlist = pl_Get(p_intf);
807     if (!p_intf)
808         return;
809     int i_type;
810     bool b_is_command = false;
811     char const *psz_filter_type = [self getFilterType: psz_filter];
812     if (!psz_filter_type) {
813         msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_filter);
814         return;
815     }
817     if (vouts && [vouts count])
818     {
819         i_type = var_Type((vout_thread_t *)[[vouts firstObject] pointerValue], psz_property);
820         b_is_command = i_type & VLC_VAR_ISCOMMAND;
821     }
822     if (!i_type)
823         i_type = config_GetType(psz_property);
825     i_type &= VLC_VAR_CLASS;
826     if (i_type == VLC_VAR_BOOL)
827         var_SetBool(p_playlist, psz_property, value.b_bool);
828     else if (i_type == VLC_VAR_INTEGER)
829         var_SetInteger(p_playlist, psz_property, value.i_int);
830     else if (i_type == VLC_VAR_FLOAT)
831         var_SetFloat(p_playlist, psz_property, value.f_float);
832     else if (i_type == VLC_VAR_STRING)
833         var_SetString(p_playlist, psz_property, EnsureUTF8(value.psz_string));
834     else
835     {
836         msg_Err(p_intf,
837                 "Module %s's %s variable is of an unsupported type ( %d )",
838                 psz_filter, psz_property, i_type);
839         b_is_command = false;
840     }
842     if (b_is_command)
843         if (vouts)
844             for (NSValue *ptr in vouts)
845             {
846                 vout_thread_t *p_vout = [ptr pointerValue];
847                 var_SetChecked(p_vout, psz_property, i_type, value);
848 #ifndef NDEBUG
849                 int i_cur_type = var_Type(p_vout, psz_property);
850                 assert((i_cur_type & VLC_VAR_CLASS) == i_type);
851                 assert(i_cur_type & VLC_VAR_ISCOMMAND);
852 #endif
853             }
855     if (vouts)
856         for (NSValue *ptr in vouts)
857             vlc_object_release((vout_thread_t *)[ptr pointerValue]);
860 #pragma mark -
861 #pragma mark Media Key support
863 - (void)resetMediaKeyJump
865     b_mediakeyJustJumped = NO;
868 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
870     intf_thread_t *p_intf = getIntf();
871     if (!p_intf)
872         return;
874     b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
875     if (b_mediaKeySupport && !_mediaKeyController)
876         _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
878     VLCMain *main = [VLCMain sharedInstance];
879     if (b_mediaKeySupport && ([[[main playlist] model] hasChildren] ||
880                               [[main inputManager] hasInput])) {
881         if (!b_mediaKeyTrapEnabled) {
882             b_mediaKeyTrapEnabled = YES;
883             msg_Dbg(p_intf, "Enable media key support");
884             [_mediaKeyController startWatchingMediaKeys];
885         }
886     } else {
887         if (b_mediaKeyTrapEnabled) {
888             b_mediaKeyTrapEnabled = NO;
889             msg_Dbg(p_intf, "Disable media key support");
890             [_mediaKeyController stopWatchingMediaKeys];
891         }
892     }
895 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
897     if (b_mediaKeySupport) {
898         assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
900         int keyCode = (([event data1] & 0xFFFF0000) >> 16);
901         int keyFlags = ([event data1] & 0x0000FFFF);
902         int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
903         int keyRepeat = (keyFlags & 0x1);
905         if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
906             [self playOrPause];
908         if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
909             if (keyState == 0 && keyRepeat == 0)
910                 [self next];
911             else if (keyRepeat == 1) {
912                 [self forwardShort];
913                 b_mediakeyJustJumped = YES;
914                 [self performSelector:@selector(resetMediaKeyJump)
915                            withObject: NULL
916                            afterDelay:0.25];
917             }
918         }
920         if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
921             if (keyState == 0 && keyRepeat == 0)
922                 [self previous];
923             else if (keyRepeat == 1) {
924                 [self backwardShort];
925                 b_mediakeyJustJumped = YES;
926                 [self performSelector:@selector(resetMediaKeyJump)
927                            withObject: NULL
928                            afterDelay:0.25];
929             }
930         }
931     }
934 #pragma mark -
935 #pragma mark Apple Remote Control
937 - (void)startListeningWithAppleRemote
939     [_remote startListening: self];
942 - (void)stopListeningWithAppleRemote
944     [_remote stopListening:self];
947 #pragma mark - menu navigation
948 - (void)menuFocusActivate
950     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
951     if (p_input_thread == NULL)
952         return;
954     input_Control(p_input_thread, INPUT_NAV_ACTIVATE, NULL );
955     vlc_object_release(p_input_thread);
958 - (void)moveMenuFocusLeft
960     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
961     if (p_input_thread == NULL)
962         return;
964     input_Control(p_input_thread, INPUT_NAV_LEFT, NULL );
965     vlc_object_release(p_input_thread);
968 - (void)moveMenuFocusRight
970     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
971     if (p_input_thread == NULL)
972         return;
974     input_Control(p_input_thread, INPUT_NAV_RIGHT, NULL );
975     vlc_object_release(p_input_thread);
978 - (void)moveMenuFocusUp
980     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
981     if (p_input_thread == NULL)
982         return;
984     input_Control(p_input_thread, INPUT_NAV_UP, NULL );
985     vlc_object_release(p_input_thread);
988 - (void)moveMenuFocusDown
990     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
991     if (p_input_thread == NULL)
992         return;
994     input_Control(p_input_thread, INPUT_NAV_DOWN, NULL );
995     vlc_object_release(p_input_thread);
998 /* Helper method for the remote control interface in order to trigger forward/backward and volume
999  increase/decrease as long as the user holds the left/right, plus/minus button */
1000 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1002     intf_thread_t *p_intf = getIntf();
1003     if (!p_intf)
1004         return;
1006     if (b_remote_button_hold) {
1007         switch([buttonIdentifierNumber intValue]) {
1008             case kRemoteButtonRight_Hold:
1009                 [self forward];
1010                 break;
1011             case kRemoteButtonLeft_Hold:
1012                 [self backward];
1013                 break;
1014             case kRemoteButtonVolume_Plus_Hold:
1015                 if (p_intf)
1016                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1017                 break;
1018             case kRemoteButtonVolume_Minus_Hold:
1019                 if (p_intf)
1020                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1021                 break;
1022         }
1023         if (b_remote_button_hold) {
1024             /* trigger event */
1025             [self performSelector:@selector(executeHoldActionForRemoteButton:)
1026                        withObject:buttonIdentifierNumber
1027                        afterDelay:0.25];
1028         }
1029     }
1032 /* Apple Remote callback */
1033 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1034                pressedDown: (BOOL) pressedDown
1035                 clickCount: (unsigned int) count
1037     intf_thread_t *p_intf = getIntf();
1038     if (!p_intf)
1039         return;
1041     switch(buttonIdentifier) {
1042         case k2009RemoteButtonFullscreen:
1043             [self toggleFullscreen];
1044             break;
1045         case k2009RemoteButtonPlay:
1046             [self playOrPause];
1047             break;
1048         case kRemoteButtonPlay:
1049             if (count >= 2)
1050                 [self toggleFullscreen];
1051             else
1052                 [self playOrPause];
1053             break;
1054         case kRemoteButtonVolume_Plus:
1055             if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1056                 [NSSound increaseSystemVolume];
1057             else
1058                 if (p_intf)
1059                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1060             break;
1061         case kRemoteButtonVolume_Minus:
1062             if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1063                 [NSSound decreaseSystemVolume];
1064             else
1065                 if (p_intf)
1066                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1067             break;
1068         case kRemoteButtonRight:
1069             if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1070                 [self forward];
1071             else
1072                 [self next];
1073             break;
1074         case kRemoteButtonLeft:
1075             if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1076                 [self backward];
1077             else
1078                 [self previous];
1079             break;
1080         case kRemoteButtonRight_Hold:
1081         case kRemoteButtonLeft_Hold:
1082         case kRemoteButtonVolume_Plus_Hold:
1083         case kRemoteButtonVolume_Minus_Hold:
1084             /* simulate an event as long as the user holds the button */
1085             b_remote_button_hold = pressedDown;
1086             if (pressedDown) {
1087                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1088                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1089                            withObject:buttonIdentifierNumber];
1090             }
1091             break;
1092         case kRemoteButtonMenu:
1093             [self showPosition];
1094             break;
1095         case kRemoteButtonPlay_Sleep:
1096         {
1097             NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1098             [script executeAndReturnError:nil];
1099             break;
1100         }
1101         default:
1102             /* Add here whatever you want other buttons to do */
1103             break;
1104     }
1107 #pragma mark -
1108 #pragma mark Key Shortcuts
1110 /*****************************************************************************
1111  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1112  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1113  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1114  *****************************************************************************/
1116 - (BOOL)keyEvent:(NSEvent *)o_event
1118     BOOL eventHandled = NO;
1119     NSString * characters = [o_event charactersIgnoringModifiers];
1120     if ([characters length] > 0) {
1121         unichar key = [characters characterAtIndex: 0];
1123         if (key) {
1124             input_thread_t * p_input = pl_CurrentInput(getIntf());
1125             if (p_input != NULL) {
1126                 vout_thread_t *p_vout = input_GetVout(p_input);
1128                 if (p_vout != NULL) {
1129                     /* Escape */
1130                     if (key == (unichar) 0x1b) {
1131                         if (var_GetBool(p_vout, "fullscreen")) {
1132                             [self toggleFullscreen];
1133                             eventHandled = YES;
1134                         }
1135                     }
1136                     vlc_object_release(p_vout);
1137                 }
1138                 vlc_object_release(p_input);
1139             }
1140         }
1141     }
1142     return eventHandled;
1145 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1147     intf_thread_t *p_intf = getIntf();
1148     if (!p_intf)
1149         return NO;
1151     unichar key = 0;
1152     vlc_value_t val;
1153     unsigned int i_pressed_modifiers = 0;
1155     val.i_int = 0;
1156     i_pressed_modifiers = [o_event modifierFlags];
1158     if (i_pressed_modifiers & NSControlKeyMask)
1159         val.i_int |= KEY_MODIFIER_CTRL;
1161     if (i_pressed_modifiers & NSAlternateKeyMask)
1162         val.i_int |= KEY_MODIFIER_ALT;
1164     if (i_pressed_modifiers & NSShiftKeyMask)
1165         val.i_int |= KEY_MODIFIER_SHIFT;
1167     if (i_pressed_modifiers & NSCommandKeyMask)
1168         val.i_int |= KEY_MODIFIER_COMMAND;
1170     NSString * characters = [o_event charactersIgnoringModifiers];
1171     if ([characters length] > 0) {
1172         key = [[characters lowercaseString] characterAtIndex: 0];
1174         /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1175         if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1176             [self toggleFullscreen];
1177             return YES;
1178         }
1180         if (!b_force) {
1181             switch(key) {
1182                 case NSDeleteCharacter:
1183                 case NSDeleteFunctionKey:
1184                 case NSDeleteCharFunctionKey:
1185                 case NSBackspaceCharacter:
1186                 case NSUpArrowFunctionKey:
1187                 case NSDownArrowFunctionKey:
1188                 case NSEnterCharacter:
1189                 case NSCarriageReturnCharacter:
1190                     return NO;
1191             }
1192         }
1194         val.i_int |= CocoaKeyToVLC(key);
1196         BOOL b_found_key = NO;
1197         for (NSUInteger i = 0; i < [_usedHotkeys count]; i++) {
1198             NSString *str = [_usedHotkeys objectAtIndex:i];
1199             unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1201             if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1202                 (i_keyModifiers & NSShiftKeyMask)     == (i_pressed_modifiers & NSShiftKeyMask) &&
1203                 (i_keyModifiers & NSControlKeyMask)   == (i_pressed_modifiers & NSControlKeyMask) &&
1204                 (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1205                 (i_keyModifiers & NSCommandKeyMask)   == (i_pressed_modifiers & NSCommandKeyMask)) {
1206                 b_found_key = YES;
1207                 break;
1208             }
1209         }
1211         if (b_found_key) {
1212             var_SetInteger(p_intf->obj.libvlc, "key-pressed", val.i_int);
1213             return YES;
1214         }
1215     }
1217     return NO;
1220 - (void)updateCurrentlyUsedHotkeys
1222     NSMutableArray *mutArray = [[NSMutableArray alloc] init];
1223     /* Get the main Module */
1224     module_t *p_main = module_get_main();
1225     assert(p_main);
1226     unsigned confsize;
1227     module_config_t *p_config;
1229     p_config = module_config_get (p_main, &confsize);
1231     for (size_t i = 0; i < confsize; i++) {
1232         module_config_t *p_item = p_config + i;
1234         if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1235             && !strncmp(p_item->psz_name , "key-", 4)
1236             && !EMPTY_STR(p_item->psz_text)) {
1237             if (p_item->value.psz)
1238                 [mutArray addObject:toNSStr(p_item->value.psz)];
1239         }
1240     }
1241     module_config_free (p_config);
1243     _usedHotkeys = [[NSArray alloc] initWithArray:mutArray copyItems:YES];
1246 @end