1 /*****************************************************************************
2 * CoreInteraction.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2011-2015 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 "VLCCoreInteraction.h"
26 #import "VLCOpenWindowController.h"
27 #import "VLCPlaylist.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>
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)
50 dispatch_async(dispatch_get_main_queue(), ^{
51 [[VLCCoreInteraction sharedInstance] pause];
52 [[NSApplication sharedApplication] hide:nil];
59 @interface VLCCoreInteraction ()
61 int i_currentPlaybackRate;
66 /* media key support */
67 BOOL b_mediaKeySupport;
68 BOOL b_mediakeyJustJumped;
69 SPMediaKeyTap *_mediaKeyController;
70 BOOL b_mediaKeyTrapEnabled;
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;
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];
92 return sharedInstance;
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,
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);
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
133 input_thread_t *p_input = pl_CurrentInput(getIntf());
134 playlist_t *p_playlist = pl_Get(getIntf());
137 playlist_TogglePause(p_playlist);
138 vlc_object_release(p_input);
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];
145 [[[VLCMain sharedInstance] playlist] playItem:nil];
151 playlist_t *p_playlist = pl_Get(getIntf());
153 playlist_Pause(p_playlist);
158 playlist_Stop(pl_Get(getIntf()));
163 var_TriggerCallback(pl_Get(getIntf()), "rate-faster");
168 var_TriggerCallback(pl_Get(getIntf()), "rate-slower");
173 var_SetFloat(pl_Get(getIntf()), "rate", 1.);
178 intf_thread_t *p_intf = getIntf();
182 input_thread_t * p_input;
183 p_input = pl_CurrentInput(p_intf);
185 var_ToggleBool(p_input, "record");
186 vlc_object_release(p_input);
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;
205 intf_thread_t *p_intf = getIntf();
209 input_thread_t * p_input;
210 p_input = pl_CurrentInput(p_intf);
212 f_rate = var_GetFloat(p_input, "rate");
213 vlc_object_release(p_input);
217 playlist_t * p_playlist = pl_Get(getIntf());
218 f_rate = var_GetFloat(p_playlist, "rate");
221 double value = 17 * log(f_rate) / log(2.);
222 int returnValue = (int) ((value > 0) ? value + .5 : value - .5);
224 if (returnValue < -34)
226 else if (returnValue > 34)
229 i_currentPlaybackRate = returnValue;
235 playlist_Prev(pl_Get(getIntf()));
240 playlist_Next(pl_Get(getIntf()));
243 - (int)durationOfCurrentPlaylistItem
245 intf_thread_t *p_intf = getIntf();
249 input_thread_t * p_input = pl_CurrentInput(p_intf);
250 int64_t i_duration = -1;
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();
266 input_thread_t *p_input = pl_CurrentInput(p_intf);
270 input_item_t *p_item = input_GetItem(p_input);
272 vlc_object_release(p_input);
276 char *psz_uri = input_item_GetURI(p_item);
278 vlc_object_release(p_input);
283 o_url = [NSURL URLWithString:toNSStr(psz_uri)];
284 vlc_object_release(p_input);
289 - (NSString*)nameOfCurrentPlaylistItem
291 intf_thread_t *p_intf = getIntf();
295 input_thread_t *p_input = pl_CurrentInput(p_intf);
299 input_item_t *p_item = input_GetItem(p_input);
301 vlc_object_release(p_input);
305 char *psz_uri = input_item_GetURI(p_item);
307 vlc_object_release(p_input);
311 NSString *o_name = @"";
312 char *format = var_InheritString(getIntf(), "input-title-format");
314 char *formated = vlc_strfinput(p_input, format);
316 o_name = toNSStr(formated);
320 NSURL * o_url = [NSURL URLWithString:toNSStr(psz_uri)];
323 if ([o_name isEqualToString:@""]) {
324 if ([o_url isFileURL])
325 o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
327 o_name = [o_url absoluteString];
329 vlc_object_release(p_input);
342 [self backwardShort];
345 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
347 input_thread_t *p_input = pl_CurrentInput(getIntf());
351 int i_interval = var_InheritInteger( p_input, p_value );
352 if (i_interval > 0) {
353 mtime_t val = CLOCK_FREQ * i_interval;
356 var_SetInteger( p_input, "time-offset", val );
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];
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];
393 [self jumpWithValue:"long-jump-size" forward:YES];
398 [self jumpWithValue:"long-jump-size" forward:NO];
403 intf_thread_t *p_intf = getIntf();
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);
416 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random On"));
417 vlc_object_release(p_vout);
419 config_PutInt(p_playlist, "random", 1);
424 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random Off"));
425 vlc_object_release(p_vout);
427 config_PutInt(p_playlist, "random", 0);
433 intf_thread_t *p_intf = getIntf();
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();
446 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat All"));
447 vlc_object_release(p_vout);
453 intf_thread_t *p_intf = getIntf();
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();
466 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat One"));
467 vlc_object_release(p_vout);
473 intf_thread_t *p_intf = getIntf();
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();
486 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat Off"));
487 vlc_object_release(p_vout);
494 input_thread_t * p_input = pl_CurrentInput(getIntf());
496 timeA = var_GetInteger(p_input, "time");
497 vlc_object_release(p_input);
500 input_thread_t * p_input = pl_CurrentInput(getIntf());
502 timeB = var_GetInteger(p_input, "time");
503 vlc_object_release(p_input);
518 input_thread_t * p_input = pl_CurrentInput(getIntf());
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);
530 intf_thread_t *p_intf = getIntf();
534 playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
539 intf_thread_t *p_intf = getIntf();
543 playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
548 intf_thread_t *p_intf = getIntf();
552 playlist_MuteToggle(pl_Get(p_intf));
557 intf_thread_t *p_intf = getIntf();
561 BOOL b_is_muted = NO;
562 b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
569 intf_thread_t *p_intf = getIntf();
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();
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);
594 if (f_maxVolume == 0.) {
595 f_maxVolume = (float)var_InheritInteger(getIntf(), "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
601 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
603 input_thread_t * p_input = pl_CurrentInput(getIntf());
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);
617 vlc_object_release(p_input);
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);
629 vlc_object_release(p_input);
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)
642 [[[VLCMain sharedInstance] playlist] addPlaylistItems:items tryAsSubtitle: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();
664 vout_thread_t *p_vout = getVoutForActiveWindow();
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];
675 #pragma mark - uncommon stuff
677 - (BOOL)fixPreferences
679 NSMutableString * o_workString;
680 NSRange returnedRange;
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) \
688 returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
689 if (returnedRange.location != NSNotFound) \
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]; \
698 config_PutPsz(getIntf(), pref, [o_workString UTF8String]); \
699 b_needsRestart = YES; \
704 fixpref("extraintf");
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);
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")) {
725 } else if (module_provides(p_obj, "sub filter")) {
728 msg_Err(getIntf(), "Unknown video filter type.");
733 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
735 intf_thread_t *p_intf = getIntf();
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);
746 msg_Dbg(p_intf, "will set filter '%s'", psz_name);
749 psz_string = config_GetPsz(p_intf, psz_filter_type);
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]);
757 psz_string = psz_tmp;
763 psz_parser = strstr(psz_string, psz_name);
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);
772 /* Remove trailing : : */
773 if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
774 *(psz_string + strlen(psz_string) -1) = '\0';
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);
787 vout_thread_t *p_vout = getVout();
789 var_SetString(p_vout, psz_filter_type, psz_string);
790 vlc_object_release(p_vout);
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();
807 vlc_object_release(p_vout);
809 vlc_object_t *p_filter = vlc_object_find_name(pl_Get(p_intf), psz_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.
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);
823 char *psz_string = config_GetPsz(p_intf, psz_filter_type);
827 if (strstr(psz_string, psz_filter) == NULL) {
834 i_type = var_Type(p_filter, psz_name);
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];
844 msg_Dbg(p_intf, "restart not needed");
847 vlc_object_release(p_filter);
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();
859 config_PutInt(p_intf, psz_name, i_value);
862 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
865 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
866 vlc_object_release(p_vout);
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];
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();
885 config_PutFloat(p_intf, psz_name, f_value);
888 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
891 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
892 vlc_object_release(p_vout);
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];
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();
912 config_PutPsz(p_intf, psz_name, EnsureUTF8(psz_new_value));
915 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
918 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
919 vlc_object_release(p_vout);
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];
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();
940 config_PutInt(p_intf, psz_name, b_value);
943 p_filter = vlc_object_find_name(pl_Get(p_intf), psz_filter);
946 msg_Warn(p_intf, "filter '%s' isn't enabled", psz_filter);
947 vlc_object_release(p_vout);
950 var_SetBool(p_filter, psz_name, b_value);
951 vlc_object_release(p_vout);
952 vlc_object_release(p_filter);
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();
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];
983 if (b_mediaKeyTrapEnabled) {
984 b_mediaKeyTrapEnabled = NO;
985 msg_Dbg(p_intf, "Disable media key support");
986 [_mediaKeyController stopWatchingMediaKeys];
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)
1004 if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
1005 if (keyState == 0 && keyRepeat == 0)
1007 else if (keyRepeat == 1) {
1008 [self forwardShort];
1009 b_mediakeyJustJumped = YES;
1010 [self performSelector:@selector(resetMediaKeyJump)
1016 if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
1017 if (keyState == 0 && keyRepeat == 0)
1019 else if (keyRepeat == 1) {
1020 [self backwardShort];
1021 b_mediakeyJustJumped = YES;
1022 [self performSelector:@selector(resetMediaKeyJump)
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)
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)
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)
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)
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)
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();
1102 if (b_remote_button_hold) {
1103 switch([buttonIdentifierNumber intValue]) {
1104 case kRemoteButtonRight_Hold:
1107 case kRemoteButtonLeft_Hold:
1110 case kRemoteButtonVolume_Plus_Hold:
1112 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1114 case kRemoteButtonVolume_Minus_Hold:
1116 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1119 if (b_remote_button_hold) {
1121 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1122 withObject:buttonIdentifierNumber
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();
1137 switch(buttonIdentifier) {
1138 case k2009RemoteButtonFullscreen:
1139 [self toggleFullscreen];
1141 case k2009RemoteButtonPlay:
1144 case kRemoteButtonPlay:
1146 [self toggleFullscreen];
1150 case kRemoteButtonVolume_Plus:
1151 if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1152 [NSSound increaseSystemVolume];
1155 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1157 case kRemoteButtonVolume_Minus:
1158 if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1159 [NSSound decreaseSystemVolume];
1162 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1164 case kRemoteButtonRight:
1165 if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1170 case kRemoteButtonLeft:
1171 if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
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;
1183 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1184 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1185 withObject:buttonIdentifierNumber];
1188 case kRemoteButtonMenu:
1189 [self showPosition];
1191 case kRemoteButtonPlay_Sleep:
1193 NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1194 [script executeAndReturnError:nil];
1198 /* Add here whatever you want other buttons to do */
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];
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) {
1226 if (key == (unichar) 0x1b) {
1227 if (var_GetBool(p_vout, "fullscreen")) {
1228 [self toggleFullscreen];
1232 vlc_object_release(p_vout);
1234 vlc_object_release(p_input);
1238 return eventHandled;
1241 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1243 intf_thread_t *p_intf = getIntf();
1249 unsigned int i_pressed_modifiers = 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];
1278 case NSDeleteCharacter:
1279 case NSDeleteFunctionKey:
1280 case NSDeleteCharFunctionKey:
1281 case NSBackspaceCharacter:
1282 case NSUpArrowFunctionKey:
1283 case NSDownArrowFunctionKey:
1284 case NSEnterCharacter:
1285 case NSCarriageReturnCharacter:
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)) {
1308 var_SetInteger(p_intf->obj.libvlc, "key-pressed", val.i_int);
1316 - (void)updateCurrentlyUsedHotkeys
1318 NSMutableArray *mutArray = [[NSMutableArray alloc] init];
1319 /* Get the main Module */
1320 module_t *p_main = module_get_main();
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)];
1337 module_config_free (p_config);
1339 _usedHotkeys = [[NSArray alloc] initWithArray:mutArray copyItems:YES];