macOS: Use dark appearance for panel modals
[vlc.git] / modules / gui / macosx / VLCCoreInteraction.m
blob912ff8bc2566f2171628709abb0a87f51a7c312d
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_keys.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     vlc_object_release(p_input);
286     return o_url;
289 - (NSString*)nameOfCurrentPlaylistItem
291     intf_thread_t *p_intf = getIntf();
292     if (!p_intf)
293         return nil;
295     input_thread_t *p_input = pl_CurrentInput(p_intf);
296     if (!p_input)
297         return nil;
299     input_item_t *p_item = input_GetItem(p_input);
300     if (!p_item) {
301         vlc_object_release(p_input);
302         return nil;
303     }
305     char *psz_uri = input_item_GetURI(p_item);
306     if (!psz_uri) {
307         vlc_object_release(p_input);
308         return nil;
309     }
311     NSString *o_name = @"";
312     char *format = var_InheritString(getIntf(), "input-title-format");
313     if (format) {
314         char *formated = vlc_strfinput(p_input, format);
315         free(format);
316         o_name = toNSStr(formated);
317         free(formated);
318     }
320     NSURL * o_url = [NSURL URLWithString:toNSStr(psz_uri)];
321     free(psz_uri);
323     if ([o_name isEqualToString:@""]) {
324         if ([o_url isFileURL])
325             o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
326         else
327             o_name = [o_url absoluteString];
328     }
329     vlc_object_release(p_input);
330     return o_name;
333 - (void)forward
335     //LEGACY SUPPORT
336     [self forwardShort];
339 - (void)backward
341     //LEGACY SUPPORT
342     [self backwardShort];
345 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
347     input_thread_t *p_input = pl_CurrentInput(getIntf());
348     if (!p_input)
349         return;
351     int i_interval = var_InheritInteger( p_input, p_value );
352     if (i_interval > 0) {
353         mtime_t val = CLOCK_FREQ * i_interval;
354         if (!b_value)
355             val = val * -1;
356         var_SetInteger( p_input, "time-offset", val );
357     }
358     vlc_object_release(p_input);
361 - (void)forwardExtraShort
363     [self jumpWithValue:"extrashort-jump-size" forward:YES];
366 - (void)backwardExtraShort
368     [self jumpWithValue:"extrashort-jump-size" forward:NO];
371 - (void)forwardShort
373     [self jumpWithValue:"short-jump-size" forward:YES];
376 - (void)backwardShort
378     [self jumpWithValue:"short-jump-size" forward:NO];
381 - (void)forwardMedium
383     [self jumpWithValue:"medium-jump-size" forward:YES];
386 - (void)backwardMedium
388     [self jumpWithValue:"medium-jump-size" forward:NO];
391 - (void)forwardLong
393     [self jumpWithValue:"long-jump-size" forward:YES];
396 - (void)backwardLong
398     [self jumpWithValue:"long-jump-size" forward:NO];
401 - (void)shuffle
403     intf_thread_t *p_intf = getIntf();
404     if (!p_intf)
405         return;
407     vlc_value_t val;
408     playlist_t * p_playlist = pl_Get(p_intf);
409     vout_thread_t *p_vout = getVout();
411     var_Get(p_playlist, "random", &val);
412     val.b_bool = !val.b_bool;
413     var_Set(p_playlist, "random", val);
414     if (val.b_bool) {
415         if (p_vout) {
416             vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random On"));
417             vlc_object_release(p_vout);
418         }
419         config_PutInt(p_playlist, "random", 1);
420     }
421     else
422     {
423         if (p_vout) {
424             vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random Off"));
425             vlc_object_release(p_vout);
426         }
427         config_PutInt(p_playlist, "random", 0);
428     }
431 - (void)repeatAll
433     intf_thread_t *p_intf = getIntf();
434     if (!p_intf)
435         return;
437     playlist_t * p_playlist = pl_Get(p_intf);
439     var_SetBool(p_playlist, "repeat", NO);
440     var_SetBool(p_playlist, "loop", YES);
441     config_PutInt(p_playlist, "repeat", NO);
442     config_PutInt(p_playlist, "loop", YES);
444     vout_thread_t *p_vout = getVout();
445     if (p_vout) {
446         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat All"));
447         vlc_object_release(p_vout);
448     }
451 - (void)repeatOne
453     intf_thread_t *p_intf = getIntf();
454     if (!p_intf)
455         return;
457     playlist_t * p_playlist = pl_Get(p_intf);
459     var_SetBool(p_playlist, "repeat", YES);
460     var_SetBool(p_playlist, "loop", NO);
461     config_PutInt(p_playlist, "repeat", YES);
462     config_PutInt(p_playlist, "loop", NO);
464     vout_thread_t *p_vout = getVout();
465     if (p_vout) {
466         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat One"));
467         vlc_object_release(p_vout);
468     }
471 - (void)repeatOff
473     intf_thread_t *p_intf = getIntf();
474     if (!p_intf)
475         return;
477     playlist_t * p_playlist = pl_Get(p_intf);
479     var_SetBool(p_playlist, "repeat", NO);
480     var_SetBool(p_playlist, "loop", NO);
481     config_PutInt(p_playlist, "repeat", NO);
482     config_PutInt(p_playlist, "loop", NO);
484     vout_thread_t *p_vout = getVout();
485     if (p_vout) {
486         vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat Off"));
487         vlc_object_release(p_vout);
488     }
491 - (void)setAtoB
493     if (!timeA) {
494         input_thread_t * p_input = pl_CurrentInput(getIntf());
495         if (p_input) {
496             timeA = var_GetInteger(p_input, "time");
497             vlc_object_release(p_input);
498         }
499     } else if (!timeB) {
500         input_thread_t * p_input = pl_CurrentInput(getIntf());
501         if (p_input) {
502             timeB = var_GetInteger(p_input, "time");
503             vlc_object_release(p_input);
504         }
505     } else
506         [self resetAtoB];
509 - (void)resetAtoB
511     timeA = 0;
512     timeB = 0;
515 - (void)updateAtoB
517     if (timeB) {
518         input_thread_t * p_input = pl_CurrentInput(getIntf());
519         if (p_input) {
520             mtime_t currentTime = var_GetInteger(p_input, "time");
521             if ( currentTime >= timeB || currentTime < timeA)
522                 var_SetInteger(p_input, "time", timeA);
523             vlc_object_release(p_input);
524         }
525     }
528 - (void)volumeUp
530     intf_thread_t *p_intf = getIntf();
531     if (!p_intf)
532         return;
534     playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
537 - (void)volumeDown
539     intf_thread_t *p_intf = getIntf();
540     if (!p_intf)
541         return;
543     playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
546 - (void)toggleMute
548     intf_thread_t *p_intf = getIntf();
549     if (!p_intf)
550         return;
552     playlist_MuteToggle(pl_Get(p_intf));
555 - (BOOL)mute
557     intf_thread_t *p_intf = getIntf();
558     if (!p_intf)
559         return NO;
561     BOOL b_is_muted = NO;
562     b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
564     return b_is_muted;
567 - (int)volume
569     intf_thread_t *p_intf = getIntf();
570     if (!p_intf)
571         return 0;
573     float volume = playlist_VolumeGet(pl_Get(p_intf));
575     return lroundf(volume * AOUT_VOLUME_DEFAULT);
578 - (void)setVolume: (int)i_value
580     intf_thread_t *p_intf = getIntf();
581     if (!p_intf)
582         return;
584     if (i_value >= self.maxVolume)
585         i_value = self.maxVolume;
587     float f_value = i_value / (float)AOUT_VOLUME_DEFAULT;
589     playlist_VolumeSet(pl_Get(p_intf), f_value);
592 - (float)maxVolume
594     if (f_maxVolume == 0.) {
595         f_maxVolume = (float)var_InheritInteger(getIntf(), "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
596     }
598     return f_maxVolume;
601 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
603     input_thread_t * p_input = pl_CurrentInput(getIntf());
604     if (!p_input)
605         return;
607     NSUInteger count = [paths count];
609     for (int i = 0; i < count ; i++) {
610         const char *path = [[[paths objectAtIndex:i] path] UTF8String];
611         msg_Dbg(getIntf(), "loading subs from %s", path);
613         int i_result = input_AddSubtitleOSD(p_input, path, true, true);
614         if (i_result != VLC_SUCCESS)
615             msg_Warn(getIntf(), "unable to load subtitles from '%s'", path);
616     }
617     vlc_object_release(p_input);
620 - (void)showPosition
622     input_thread_t *p_input = pl_CurrentInput(getIntf());
623     if (p_input != NULL) {
624         vout_thread_t *p_vout = input_GetVout(p_input);
625         if (p_vout != NULL) {
626             var_SetInteger(getIntf()->obj.libvlc, "key-action", ACTIONID_POSITION);
627             vlc_object_release(p_vout);
628         }
629         vlc_object_release(p_input);
630     }
633 #pragma mark - Drop support for files into the video, controls bar or drop box
635 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
637     NSArray *items = [[[VLCMain sharedInstance] playlist] createItemsFromExternalPasteboard:[sender draggingPasteboard]];
639     if (items.count == 0)
640         return NO;
642     [[[VLCMain sharedInstance] playlist] addPlaylistItems:items tryAsSubtitle:YES];
643     return YES;
646 #pragma mark - video output stuff
648 - (void)setAspectRatioIsLocked:(BOOL)b_value
650     config_PutInt(getIntf(), "macosx-lock-aspect-ratio", b_value);
653 - (BOOL)aspectRatioIsLocked
655     return config_GetInt(getIntf(), "macosx-lock-aspect-ratio");
658 - (void)toggleFullscreen
660     intf_thread_t *p_intf = getIntf();
661     if (!p_intf)
662         return;
664     vout_thread_t *p_vout = getVoutForActiveWindow();
665     if (p_vout) {
666         BOOL b_fs = var_ToggleBool(p_vout, "fullscreen");
667         var_SetBool(pl_Get(p_intf), "fullscreen", b_fs);
668         vlc_object_release(p_vout);
669     } else { // e.g. lion fullscreen toggle
670         BOOL b_fs = var_ToggleBool(pl_Get(p_intf), "fullscreen");
671         [[[VLCMain sharedInstance] voutController] setFullscreen:b_fs forWindow:nil withAnimation:YES];
672     }
675 #pragma mark - uncommon stuff
677 - (BOOL)fixPreferences
679     NSMutableString * o_workString;
680     NSRange returnedRange;
681     NSRange fullRange;
682     BOOL b_needsRestart = NO;
684     #define fixpref(pref) \
685     o_workString = [[NSMutableString alloc] initWithFormat:@"%s", config_GetPsz(getIntf(), pref)]; \
686     if ([o_workString length] > 0) \
687     { \
688         returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
689         if (returnedRange.location != NSNotFound) \
690         { \
691             if ([o_workString isEqualToString:@"macosx"]) \
692                 [o_workString setString:@""]; \
693             fullRange = NSMakeRange(0, [o_workString length]); \
694             [o_workString replaceOccurrencesOfString:@":macosx" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
695             fullRange = NSMakeRange(0, [o_workString length]); \
696             [o_workString replaceOccurrencesOfString:@"macosx:" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
697             \
698             config_PutPsz(getIntf(), pref, [o_workString UTF8String]); \
699             b_needsRestart = YES; \
700         } \
701     }
703     fixpref("control");
704     fixpref("extraintf");
705     #undef fixpref
707     return b_needsRestart;
710 #pragma mark - video filter handling
712 - (const char *)getFilterType:(const char *)psz_name
714     module_t *p_obj = module_find(psz_name);
715     if (!p_obj) {
716         return NULL;
717     }
719     if (module_provides(p_obj, "video splitter")) {
720         return "video-splitter";
721     } else if (module_provides(p_obj, "video filter")) {
722         return "video-filter";
723     } else if (module_provides(p_obj, "sub source")) {
724         return "sub-source";
725     } else if (module_provides(p_obj, "sub filter")) {
726         return "sub-filter";
727     } else {
728         msg_Err(getIntf(), "Unknown video filter type.");
729         return NULL;
730     }
733 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
735     intf_thread_t *p_intf = getIntf();
736     if (!p_intf)
737         return;
738     char *psz_string, *psz_parser;
740     const char *psz_filter_type = [self getFilterType:psz_name];
741     if (!psz_filter_type) {
742         msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
743         return;
744     }
746     msg_Dbg(p_intf, "will set filter '%s'", psz_name);
749     psz_string = config_GetPsz(p_intf, psz_filter_type);
751     if (b_on) {
752         if (psz_string == NULL) {
753             psz_string = strdup(psz_name);
754         } else if (strstr(psz_string, psz_name) == NULL) {
755             char *psz_tmp = strdup([[NSString stringWithFormat: @"%s:%s", psz_string, psz_name] UTF8String]);
756             free(psz_string);
757             psz_string = psz_tmp;
758         }
759     } else {
760         if (!psz_string)
761             return;
763         psz_parser = strstr(psz_string, psz_name);
764         if (psz_parser) {
765             if (*(psz_parser + strlen(psz_name)) == ':') {
766                 memmove(psz_parser, psz_parser + strlen(psz_name) + 1,
767                         strlen(psz_parser + strlen(psz_name) + 1) + 1);
768             } else {
769                 *psz_parser = '\0';
770             }
772             /* Remove trailing : : */
773             if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
774                 *(psz_string + strlen(psz_string) -1) = '\0';
775         } else {
776             free(psz_string);
777             return;
778         }
779     }
780     config_PutPsz(p_intf, psz_filter_type, psz_string);
782     /* Try to set on the fly */
783     if (!strcmp(psz_filter_type, "video-splitter")) {
784         playlist_t *p_playlist = pl_Get(p_intf);
785         var_SetString(p_playlist, psz_filter_type, psz_string);
786     } else {
787         vout_thread_t *p_vout = getVout();
788         if (p_vout) {
789             var_SetString(p_vout, psz_filter_type, psz_string);
790             vlc_object_release(p_vout);
791         }
792     }
794     free(psz_string);
797 - (void)restartFilterIfNeeded: (const char *)psz_filter option: (const char *)psz_name
799     vout_thread_t *p_vout = getVout();
800     intf_thread_t *p_intf = getIntf();
801     if (!p_intf)
802         return;
804     if (p_vout == NULL)
805         return;
806     else
807         vlc_object_release(p_vout);
809     vlc_object_t *p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
810     if (p_filter) {
812         /* we cannot rely on the p_filter existence.
813          This filter might be just
814          disabled, but the object still exists. Therefore, the string
815          is checked, additionally.
816          */
817         const char *psz_filter_type = [self getFilterType:psz_filter];
818         if (!psz_filter_type) {
819             msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
820             goto out;
821         }
823         char *psz_string = config_GetPsz(p_intf, psz_filter_type);
824         if (!psz_string) {
825             goto out;
826         }
827         if (strstr(psz_string, psz_filter) == NULL) {
828             free(psz_string);
829             goto out;
830         }
831         free(psz_string);
833         int i_type;
834         i_type = var_Type(p_filter, psz_name);
835         if (i_type == 0)
836             i_type = config_GetType(p_intf, psz_name);
838         if (!(i_type & VLC_VAR_ISCOMMAND)) {
839             msg_Warn(p_intf, "Brute-restarting filter '%s', because the last changed option isn't a command", psz_name);
841             [self setVideoFilter: psz_filter on: NO];
842             [self setVideoFilter: psz_filter on: YES];
843         } else
844             msg_Dbg(p_intf, "restart not needed");
846         out:
847         vlc_object_release(p_filter);
848     }
851 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter integer: (int)i_value
853     vout_thread_t *p_vout = getVout();
854     vlc_object_t *p_filter;
855     intf_thread_t *p_intf = getIntf();
856     if (!p_intf)
857         return;
859     config_PutInt(p_intf, psz_name, i_value);
861     if (p_vout) {
862         p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
864         if (!p_filter) {
865             msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
866             vlc_object_release(p_vout);
867             return;
868         }
869         var_SetInteger(p_filter, psz_name, i_value);
870         vlc_object_release(p_vout);
871         vlc_object_release(p_filter);
873         [self restartFilterIfNeeded: psz_filter option: psz_name];
874     }
877 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter float: (float)f_value
879     vout_thread_t *p_vout = getVout();
880     vlc_object_t *p_filter;
881     intf_thread_t *p_intf = getIntf();
882     if (!p_intf)
883         return;
885     config_PutFloat(p_intf, psz_name, f_value);
887     if (p_vout) {
888         p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
890         if (!p_filter) {
891             msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
892             vlc_object_release(p_vout);
893             return;
894         }
895         var_SetFloat(p_filter, psz_name, f_value);
896         vlc_object_release(p_vout);
897         vlc_object_release(p_filter);
899         [self restartFilterIfNeeded: psz_filter option: psz_name];
900     }
903 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter string: (const char *)psz_value
905     char *psz_new_value = strdup(psz_value);
906     vout_thread_t *p_vout = getVout();
907     vlc_object_t *p_filter;
908     intf_thread_t *p_intf = getIntf();
909     if (!p_intf)
910         return;
912     config_PutPsz(p_intf, psz_name, EnsureUTF8(psz_new_value));
914     if (p_vout) {
915         p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
917         if (!p_filter) {
918             msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
919             vlc_object_release(p_vout);
920             return;
921         }
922         var_SetString(p_filter, psz_name, EnsureUTF8(psz_new_value));
923         vlc_object_release(p_vout);
924         vlc_object_release(p_filter);
926         [self restartFilterIfNeeded: psz_filter option: psz_name];
927     }
929     free(psz_new_value);
932 - (void)setVideoFilterProperty: (const char *)psz_name forFilter: (const char *)psz_filter boolean: (BOOL)b_value
934     vout_thread_t *p_vout = getVout();
935     vlc_object_t *p_filter;
936     intf_thread_t *p_intf = getIntf();
937     if (!p_intf)
938         return;
940     config_PutInt(p_intf, psz_name, b_value);
942     if (p_vout) {
943         p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
945         if (!p_filter) {
946             msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
947             vlc_object_release(p_vout);
948             return;
949         }
950         var_SetBool(p_filter, psz_name, b_value);
951         vlc_object_release(p_vout);
952         vlc_object_release(p_filter);
953     }
956 #pragma mark -
957 #pragma mark Media Key support
959 - (void)resetMediaKeyJump
961     b_mediakeyJustJumped = NO;
964 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
966     intf_thread_t *p_intf = getIntf();
967     if (!p_intf)
968         return;
970     b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
971     if (b_mediaKeySupport && !_mediaKeyController)
972         _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
974     VLCMain *main = [VLCMain sharedInstance];
975     if (b_mediaKeySupport && ([[[main playlist] model] hasChildren] ||
976                               [[main inputManager] hasInput])) {
977         if (!b_mediaKeyTrapEnabled) {
978             b_mediaKeyTrapEnabled = YES;
979             msg_Dbg(p_intf, "Enable media key support");
980             [_mediaKeyController startWatchingMediaKeys];
981         }
982     } else {
983         if (b_mediaKeyTrapEnabled) {
984             b_mediaKeyTrapEnabled = NO;
985             msg_Dbg(p_intf, "Disable media key support");
986             [_mediaKeyController stopWatchingMediaKeys];
987         }
988     }
991 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
993     if (b_mediaKeySupport) {
994         assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
996         int keyCode = (([event data1] & 0xFFFF0000) >> 16);
997         int keyFlags = ([event data1] & 0x0000FFFF);
998         int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
999         int keyRepeat = (keyFlags & 0x1);
1001         if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
1002             [self playOrPause];
1004         if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
1005             if (keyState == 0 && keyRepeat == 0)
1006                 [self next];
1007             else if (keyRepeat == 1) {
1008                 [self forwardShort];
1009                 b_mediakeyJustJumped = YES;
1010                 [self performSelector:@selector(resetMediaKeyJump)
1011                            withObject: NULL
1012                            afterDelay:0.25];
1013             }
1014         }
1016         if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
1017             if (keyState == 0 && keyRepeat == 0)
1018                 [self previous];
1019             else if (keyRepeat == 1) {
1020                 [self backwardShort];
1021                 b_mediakeyJustJumped = YES;
1022                 [self performSelector:@selector(resetMediaKeyJump)
1023                            withObject: NULL
1024                            afterDelay:0.25];
1025             }
1026         }
1027     }
1030 #pragma mark -
1031 #pragma mark Apple Remote Control
1033 - (void)startListeningWithAppleRemote
1035     [_remote startListening: self];
1038 - (void)stopListeningWithAppleRemote
1040     [_remote stopListening:self];
1043 #pragma mark - menu navigation
1044 - (void)menuFocusActivate
1046     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1047     if (p_input_thread == NULL)
1048         return;
1050     input_Control(p_input_thread, INPUT_NAV_ACTIVATE, NULL );
1051     vlc_object_release(p_input_thread);
1054 - (void)moveMenuFocusLeft
1056     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1057     if (p_input_thread == NULL)
1058         return;
1060     input_Control(p_input_thread, INPUT_NAV_LEFT, NULL );
1061     vlc_object_release(p_input_thread);
1064 - (void)moveMenuFocusRight
1066     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1067     if (p_input_thread == NULL)
1068         return;
1070     input_Control(p_input_thread, INPUT_NAV_RIGHT, NULL );
1071     vlc_object_release(p_input_thread);
1074 - (void)moveMenuFocusUp
1076     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1077     if (p_input_thread == NULL)
1078         return;
1080     input_Control(p_input_thread, INPUT_NAV_UP, NULL );
1081     vlc_object_release(p_input_thread);
1084 - (void)moveMenuFocusDown
1086     input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1087     if (p_input_thread == NULL)
1088         return;
1090     input_Control(p_input_thread, INPUT_NAV_DOWN, NULL );
1091     vlc_object_release(p_input_thread);
1094 /* Helper method for the remote control interface in order to trigger forward/backward and volume
1095  increase/decrease as long as the user holds the left/right, plus/minus button */
1096 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1098     intf_thread_t *p_intf = getIntf();
1099     if (!p_intf)
1100         return;
1102     if (b_remote_button_hold) {
1103         switch([buttonIdentifierNumber intValue]) {
1104             case kRemoteButtonRight_Hold:
1105                 [self forward];
1106                 break;
1107             case kRemoteButtonLeft_Hold:
1108                 [self backward];
1109                 break;
1110             case kRemoteButtonVolume_Plus_Hold:
1111                 if (p_intf)
1112                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1113                 break;
1114             case kRemoteButtonVolume_Minus_Hold:
1115                 if (p_intf)
1116                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1117                 break;
1118         }
1119         if (b_remote_button_hold) {
1120             /* trigger event */
1121             [self performSelector:@selector(executeHoldActionForRemoteButton:)
1122                        withObject:buttonIdentifierNumber
1123                        afterDelay:0.25];
1124         }
1125     }
1128 /* Apple Remote callback */
1129 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1130                pressedDown: (BOOL) pressedDown
1131                 clickCount: (unsigned int) count
1133     intf_thread_t *p_intf = getIntf();
1134     if (!p_intf)
1135         return;
1137     switch(buttonIdentifier) {
1138         case k2009RemoteButtonFullscreen:
1139             [self toggleFullscreen];
1140             break;
1141         case k2009RemoteButtonPlay:
1142             [self playOrPause];
1143             break;
1144         case kRemoteButtonPlay:
1145             if (count >= 2)
1146                 [self toggleFullscreen];
1147             else
1148                 [self playOrPause];
1149             break;
1150         case kRemoteButtonVolume_Plus:
1151             if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1152                 [NSSound increaseSystemVolume];
1153             else
1154                 if (p_intf)
1155                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1156             break;
1157         case kRemoteButtonVolume_Minus:
1158             if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1159                 [NSSound decreaseSystemVolume];
1160             else
1161                 if (p_intf)
1162                     var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1163             break;
1164         case kRemoteButtonRight:
1165             if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1166                 [self forward];
1167             else
1168                 [self next];
1169             break;
1170         case kRemoteButtonLeft:
1171             if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1172                 [self backward];
1173             else
1174                 [self previous];
1175             break;
1176         case kRemoteButtonRight_Hold:
1177         case kRemoteButtonLeft_Hold:
1178         case kRemoteButtonVolume_Plus_Hold:
1179         case kRemoteButtonVolume_Minus_Hold:
1180             /* simulate an event as long as the user holds the button */
1181             b_remote_button_hold = pressedDown;
1182             if (pressedDown) {
1183                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1184                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1185                            withObject:buttonIdentifierNumber];
1186             }
1187             break;
1188         case kRemoteButtonMenu:
1189             [self showPosition];
1190             break;
1191         case kRemoteButtonPlay_Sleep:
1192         {
1193             NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1194             [script executeAndReturnError:nil];
1195             break;
1196         }
1197         default:
1198             /* Add here whatever you want other buttons to do */
1199             break;
1200     }
1203 #pragma mark -
1204 #pragma mark Key Shortcuts
1206 /*****************************************************************************
1207  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1208  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1209  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1210  *****************************************************************************/
1212 - (BOOL)keyEvent:(NSEvent *)o_event
1214     BOOL eventHandled = NO;
1215     NSString * characters = [o_event charactersIgnoringModifiers];
1216     if ([characters length] > 0) {
1217         unichar key = [characters characterAtIndex: 0];
1219         if (key) {
1220             input_thread_t * p_input = pl_CurrentInput(getIntf());
1221             if (p_input != NULL) {
1222                 vout_thread_t *p_vout = input_GetVout(p_input);
1224                 if (p_vout != NULL) {
1225                     /* Escape */
1226                     if (key == (unichar) 0x1b) {
1227                         if (var_GetBool(p_vout, "fullscreen")) {
1228                             [self toggleFullscreen];
1229                             eventHandled = YES;
1230                         }
1231                     }
1232                     vlc_object_release(p_vout);
1233                 }
1234                 vlc_object_release(p_input);
1235             }
1236         }
1237     }
1238     return eventHandled;
1241 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1243     intf_thread_t *p_intf = getIntf();
1244     if (!p_intf)
1245         return NO;
1247     unichar key = 0;
1248     vlc_value_t val;
1249     unsigned int i_pressed_modifiers = 0;
1251     val.i_int = 0;
1252     i_pressed_modifiers = [o_event modifierFlags];
1254     if (i_pressed_modifiers & NSControlKeyMask)
1255         val.i_int |= KEY_MODIFIER_CTRL;
1257     if (i_pressed_modifiers & NSAlternateKeyMask)
1258         val.i_int |= KEY_MODIFIER_ALT;
1260     if (i_pressed_modifiers & NSShiftKeyMask)
1261         val.i_int |= KEY_MODIFIER_SHIFT;
1263     if (i_pressed_modifiers & NSCommandKeyMask)
1264         val.i_int |= KEY_MODIFIER_COMMAND;
1266     NSString * characters = [o_event charactersIgnoringModifiers];
1267     if ([characters length] > 0) {
1268         key = [[characters lowercaseString] characterAtIndex: 0];
1270         /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1271         if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1272             [self toggleFullscreen];
1273             return YES;
1274         }
1276         if (!b_force) {
1277             switch(key) {
1278                 case NSDeleteCharacter:
1279                 case NSDeleteFunctionKey:
1280                 case NSDeleteCharFunctionKey:
1281                 case NSBackspaceCharacter:
1282                 case NSUpArrowFunctionKey:
1283                 case NSDownArrowFunctionKey:
1284                 case NSEnterCharacter:
1285                 case NSCarriageReturnCharacter:
1286                     return NO;
1287             }
1288         }
1290         val.i_int |= CocoaKeyToVLC(key);
1292         BOOL b_found_key = NO;
1293         for (NSUInteger i = 0; i < [_usedHotkeys count]; i++) {
1294             NSString *str = [_usedHotkeys objectAtIndex:i];
1295             unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1297             if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1298                 (i_keyModifiers & NSShiftKeyMask)     == (i_pressed_modifiers & NSShiftKeyMask) &&
1299                 (i_keyModifiers & NSControlKeyMask)   == (i_pressed_modifiers & NSControlKeyMask) &&
1300                 (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1301                 (i_keyModifiers & NSCommandKeyMask)   == (i_pressed_modifiers & NSCommandKeyMask)) {
1302                 b_found_key = YES;
1303                 break;
1304             }
1305         }
1307         if (b_found_key) {
1308             var_SetInteger(p_intf->obj.libvlc, "key-pressed", val.i_int);
1309             return YES;
1310         }
1311     }
1313     return NO;
1316 - (void)updateCurrentlyUsedHotkeys
1318     NSMutableArray *mutArray = [[NSMutableArray alloc] init];
1319     /* Get the main Module */
1320     module_t *p_main = module_get_main();
1321     assert(p_main);
1322     unsigned confsize;
1323     module_config_t *p_config;
1325     p_config = module_config_get (p_main, &confsize);
1327     for (size_t i = 0; i < confsize; i++) {
1328         module_config_t *p_item = p_config + i;
1330         if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1331             && !strncmp(p_item->psz_name , "key-", 4)
1332             && !EMPTY_STR(p_item->psz_text)) {
1333             if (p_item->value.psz)
1334                 [mutArray addObject:toNSStr(p_item->value.psz)];
1335         }
1336     }
1337     module_config_free (p_config);
1339     _usedHotkeys = [[NSArray alloc] initWithArray:mutArray copyItems:YES];
1342 @end