1 /*****************************************************************************
2 * CoreInteraction.m: MacOS X interface module
3 *****************************************************************************
4 * Copyright (C) 2011-2018 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>
31 #import <vlc_actions.h>
34 #import <vlc_vout_osd.h>
36 #import <vlc_strings.h>
38 #import <vlc_modules.h>
39 #import <vlc_charset.h>
40 #include <vlc_plugin.h>
41 #import "SPMediaKeyTap.h"
42 #import "AppleRemote.h"
43 #import "VLCInputManager.h"
45 #import "NSSound+VLCAdditions.h"
47 static int BossCallback(vlc_object_t *p_this, const char *psz_var,
48 vlc_value_t oldval, vlc_value_t new_val, void *param)
51 dispatch_async(dispatch_get_main_queue(), ^{
52 [[VLCCoreInteraction sharedInstance] pause];
53 [[NSApplication sharedApplication] hide:nil];
60 @interface VLCCoreInteraction ()
62 int i_currentPlaybackRate;
63 vlc_tick_t timeA, timeB;
67 /* media key support */
68 BOOL b_mediaKeySupport;
69 BOOL b_mediakeyJustJumped;
70 SPMediaKeyTap *_mediaKeyController;
71 BOOL b_mediaKeyTrapEnabled;
74 BOOL b_remote_button_hold; /* true as long as the user holds the left,right,plus or minus on the remote control */
76 NSArray *_usedHotkeys;
80 @implementation VLCCoreInteraction
82 #pragma mark - Initialization
84 + (VLCCoreInteraction *)sharedInstance
86 static VLCCoreInteraction *sharedInstance = nil;
87 static dispatch_once_t pred;
89 dispatch_once(&pred, ^{
90 sharedInstance = [VLCCoreInteraction new];
93 return sharedInstance;
100 intf_thread_t *p_intf = getIntf();
102 /* init media key support */
103 b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
104 if (b_mediaKeySupport) {
105 _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
106 [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
107 [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
110 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name:VLCMediaKeySupportSettingChangedNotification object: nil];
112 /* init Apple Remote support */
113 _remote = [[AppleRemote alloc] init];
114 [_remote setClickCountEnabledButtons: kRemoteButtonPlay];
115 [_remote setDelegate: self];
117 var_AddCallback(pl_Get(p_intf), "intf-boss", BossCallback, (__bridge void *)self);
124 intf_thread_t *p_intf = getIntf();
125 var_DelCallback(pl_Get(p_intf), "intf-boss", BossCallback, (__bridge void *)self);
126 [[NSNotificationCenter defaultCenter] removeObserver: self];
130 #pragma mark - Playback Controls
134 playlist_t *p_playlist = pl_Get(getIntf());
136 playlist_Play(p_playlist);
141 input_thread_t *p_input = pl_CurrentInput(getIntf());
142 playlist_t *p_playlist = pl_Get(getIntf());
145 playlist_TogglePause(p_playlist);
146 vlc_object_release(p_input);
149 PLRootType root = [[[[VLCMain sharedInstance] playlist] model] currentRootType];
150 if ([[[VLCMain sharedInstance] playlist] isSelectionEmpty] && (root == ROOT_TYPE_PLAYLIST || root == ROOT_TYPE_MEDIALIBRARY))
151 [[[VLCMain sharedInstance] open] openFileGeneric];
153 [[[VLCMain sharedInstance] playlist] playItem:nil];
159 playlist_t *p_playlist = pl_Get(getIntf());
161 playlist_Pause(p_playlist);
166 playlist_Stop(pl_Get(getIntf()));
171 var_TriggerCallback(pl_Get(getIntf()), "rate-faster");
176 var_TriggerCallback(pl_Get(getIntf()), "rate-slower");
181 var_SetFloat(pl_Get(getIntf()), "rate", 1.);
186 intf_thread_t *p_intf = getIntf();
190 input_thread_t * p_input;
191 p_input = pl_CurrentInput(p_intf);
193 var_ToggleBool(p_input, "record");
194 vlc_object_release(p_input);
198 - (void)setPlaybackRate:(int)i_value
200 playlist_t * p_playlist = pl_Get(getIntf());
202 double speed = pow(2, (double)i_value / 17);
203 int rate = INPUT_RATE_DEFAULT / speed;
204 if (i_currentPlaybackRate != rate)
205 var_SetFloat(p_playlist, "rate", (float)INPUT_RATE_DEFAULT / (float)rate);
206 i_currentPlaybackRate = rate;
213 intf_thread_t *p_intf = getIntf();
217 input_thread_t * p_input;
218 p_input = pl_CurrentInput(p_intf);
220 f_rate = var_GetFloat(p_input, "rate");
221 vlc_object_release(p_input);
225 playlist_t * p_playlist = pl_Get(getIntf());
226 f_rate = var_GetFloat(p_playlist, "rate");
229 double value = 17 * log(f_rate) / log(2.);
230 int returnValue = (int) ((value > 0) ? value + .5 : value - .5);
232 if (returnValue < -34)
234 else if (returnValue > 34)
237 i_currentPlaybackRate = returnValue;
243 playlist_Prev(pl_Get(getIntf()));
248 playlist_Next(pl_Get(getIntf()));
251 - (NSInteger)durationOfCurrentPlaylistItem
253 intf_thread_t *p_intf = getIntf();
257 input_thread_t * p_input = pl_CurrentInput(p_intf);
258 int64_t i_duration = -1;
262 i_duration = var_GetInteger(p_input, "length");
263 vlc_object_release(p_input);
265 return (i_duration / 1000000);
268 - (NSURL*)URLOfCurrentPlaylistItem
270 intf_thread_t *p_intf = getIntf();
274 input_thread_t *p_input = pl_CurrentInput(p_intf);
278 input_item_t *p_item = input_GetItem(p_input);
280 vlc_object_release(p_input);
284 char *psz_uri = input_item_GetURI(p_item);
286 vlc_object_release(p_input);
291 o_url = [NSURL URLWithString:toNSStr(psz_uri)];
293 vlc_object_release(p_input);
298 - (NSString*)nameOfCurrentPlaylistItem
300 intf_thread_t *p_intf = getIntf();
304 input_thread_t *p_input = pl_CurrentInput(p_intf);
308 input_item_t *p_item = input_GetItem(p_input);
310 vlc_object_release(p_input);
314 char *psz_uri = input_item_GetURI(p_item);
316 vlc_object_release(p_input);
320 NSString *o_name = @"";
321 char *format = var_InheritString(getIntf(), "input-title-format");
323 char *formated = vlc_strfinput(p_input, format);
325 o_name = toNSStr(formated);
329 NSURL * o_url = [NSURL URLWithString:toNSStr(psz_uri)];
332 if ([o_name isEqualToString:@""]) {
333 if ([o_url isFileURL])
334 o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
336 o_name = [o_url absoluteString];
338 vlc_object_release(p_input);
351 [self backwardShort];
354 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
356 input_thread_t *p_input = pl_CurrentInput(getIntf());
360 int64_t i_interval = var_InheritInteger( p_input, p_value );
361 if (i_interval > 0) {
362 vlc_tick_t val = vlc_tick_from_sec( i_interval );
365 var_SetInteger( p_input, "time-offset", val );
367 vlc_object_release(p_input);
370 - (void)forwardExtraShort
372 [self jumpWithValue:"extrashort-jump-size" forward:YES];
375 - (void)backwardExtraShort
377 [self jumpWithValue:"extrashort-jump-size" forward:NO];
382 [self jumpWithValue:"short-jump-size" forward:YES];
385 - (void)backwardShort
387 [self jumpWithValue:"short-jump-size" forward:NO];
390 - (void)forwardMedium
392 [self jumpWithValue:"medium-jump-size" forward:YES];
395 - (void)backwardMedium
397 [self jumpWithValue:"medium-jump-size" forward:NO];
402 [self jumpWithValue:"long-jump-size" forward:YES];
407 [self jumpWithValue:"long-jump-size" forward:NO];
412 intf_thread_t *p_intf = getIntf();
417 playlist_t * p_playlist = pl_Get(p_intf);
418 vout_thread_t *p_vout = getVout();
420 var_Get(p_playlist, "random", &val);
421 val.b_bool = !val.b_bool;
422 var_Set(p_playlist, "random", val);
425 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random On"));
426 vlc_object_release(p_vout);
428 config_PutInt("random", 1);
433 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random Off"));
434 vlc_object_release(p_vout);
436 config_PutInt("random", 0);
442 intf_thread_t *p_intf = getIntf();
446 playlist_t * p_playlist = pl_Get(p_intf);
448 var_SetBool(p_playlist, "repeat", NO);
449 var_SetBool(p_playlist, "loop", YES);
450 config_PutInt("repeat", NO);
451 config_PutInt("loop", YES);
453 vout_thread_t *p_vout = getVout();
455 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat All"));
456 vlc_object_release(p_vout);
462 intf_thread_t *p_intf = getIntf();
466 playlist_t * p_playlist = pl_Get(p_intf);
468 var_SetBool(p_playlist, "repeat", YES);
469 var_SetBool(p_playlist, "loop", NO);
470 config_PutInt("repeat", YES);
471 config_PutInt("loop", NO);
473 vout_thread_t *p_vout = getVout();
475 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat One"));
476 vlc_object_release(p_vout);
482 intf_thread_t *p_intf = getIntf();
486 playlist_t * p_playlist = pl_Get(p_intf);
488 var_SetBool(p_playlist, "repeat", NO);
489 var_SetBool(p_playlist, "loop", NO);
490 config_PutInt("repeat", NO);
491 config_PutInt("loop", NO);
493 vout_thread_t *p_vout = getVout();
495 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat Off"));
496 vlc_object_release(p_vout);
503 input_thread_t * p_input = pl_CurrentInput(getIntf());
505 msg_Dbg(getIntf(), "Setting A value");
507 timeA = var_GetInteger(p_input, "time");
508 vlc_object_release(p_input);
511 input_thread_t * p_input = pl_CurrentInput(getIntf());
513 msg_Dbg(getIntf(), "Setting B value");
515 timeB = var_GetInteger(p_input, "time");
516 vlc_object_release(p_input);
524 msg_Dbg(getIntf(), "Resetting A to B values");
532 input_thread_t * p_input = pl_CurrentInput(getIntf());
534 vlc_tick_t currentTime = var_GetInteger(p_input, "time");
535 if ( currentTime >= timeB || currentTime < timeA)
536 var_SetInteger(p_input, "time", timeA);
537 vlc_object_release(p_input);
542 - (void)jumpToTime:(vlc_tick_t)time
544 input_thread_t * p_input = pl_CurrentInput(getIntf());
546 var_SetInteger(p_input, "time", time);
547 vlc_object_release(p_input);
553 intf_thread_t *p_intf = getIntf();
557 playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
562 intf_thread_t *p_intf = getIntf();
566 playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
571 intf_thread_t *p_intf = getIntf();
575 playlist_MuteToggle(pl_Get(p_intf));
580 intf_thread_t *p_intf = getIntf();
584 BOOL b_is_muted = NO;
585 b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
592 intf_thread_t *p_intf = getIntf();
596 float volume = playlist_VolumeGet(pl_Get(p_intf));
598 return (int)lroundf(volume * AOUT_VOLUME_DEFAULT);
601 - (void)setVolume: (int)i_value
603 intf_thread_t *p_intf = getIntf();
607 if (i_value >= self.maxVolume)
608 i_value = self.maxVolume;
610 float f_value = i_value / (float)AOUT_VOLUME_DEFAULT;
612 playlist_VolumeSet(pl_Get(p_intf), f_value);
617 if (f_maxVolume == 0.) {
618 f_maxVolume = (float)var_InheritInteger(getIntf(), "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
624 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
626 input_thread_t * p_input = pl_CurrentInput(getIntf());
630 NSUInteger count = [paths count];
632 for (int i = 0; i < count ; i++) {
633 char *mrl = vlc_path2uri([[[paths objectAtIndex:i] path] UTF8String], NULL);
636 msg_Dbg(getIntf(), "loading subs from %s", mrl);
638 int i_result = input_AddSlave(p_input, SLAVE_TYPE_SPU, mrl, true, true, true);
639 if (i_result != VLC_SUCCESS)
640 msg_Warn(getIntf(), "unable to load subtitles from '%s'", mrl);
643 vlc_object_release(p_input);
648 input_thread_t *p_input = pl_CurrentInput(getIntf());
649 if (p_input != NULL) {
650 vout_thread_t *p_vout = input_GetVout(p_input);
651 if (p_vout != NULL) {
652 var_SetInteger(getIntf()->obj.libvlc, "key-action", ACTIONID_POSITION);
653 vlc_object_release(p_vout);
655 vlc_object_release(p_input);
659 #pragma mark - Drop support for files into the video, controls bar or drop box
661 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
663 NSArray *items = [[[VLCMain sharedInstance] playlist] createItemsFromExternalPasteboard:[sender draggingPasteboard]];
665 if (items.count == 0)
668 [[[VLCMain sharedInstance] playlist] addPlaylistItems:items tryAsSubtitle:YES];
672 #pragma mark - video output stuff
674 - (void)setAspectRatioIsLocked:(BOOL)b_value
676 config_PutInt("macosx-lock-aspect-ratio", b_value);
679 - (BOOL)aspectRatioIsLocked
681 return config_GetInt("macosx-lock-aspect-ratio");
684 - (void)toggleFullscreen
686 intf_thread_t *p_intf = getIntf();
690 vout_thread_t *p_vout = getVoutForActiveWindow();
692 BOOL b_fs = var_ToggleBool(p_vout, "fullscreen");
693 var_SetBool(pl_Get(p_intf), "fullscreen", b_fs);
694 vlc_object_release(p_vout);
695 } else { // e.g. lion fullscreen toggle
696 BOOL b_fs = var_ToggleBool(pl_Get(p_intf), "fullscreen");
697 [[[VLCMain sharedInstance] voutProvider] setFullscreen:b_fs forWindow:nil withAnimation:YES];
701 #pragma mark - uncommon stuff
703 - (BOOL)fixIntfSettings
705 NSMutableString * o_workString;
706 NSRange returnedRange;
708 BOOL b_needsRestart = NO;
710 #define fixpref(pref) \
711 o_workString = [[NSMutableString alloc] initWithFormat:@"%s", config_GetPsz(pref)]; \
712 if ([o_workString length] > 0) \
714 returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
715 if (returnedRange.location != NSNotFound) \
717 if ([o_workString isEqualToString:@"macosx"]) \
718 [o_workString setString:@""]; \
719 fullRange = NSMakeRange(0, [o_workString length]); \
720 [o_workString replaceOccurrencesOfString:@":macosx" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
721 fullRange = NSMakeRange(0, [o_workString length]); \
722 [o_workString replaceOccurrencesOfString:@"macosx:" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
724 config_PutPsz(pref, [o_workString UTF8String]); \
725 b_needsRestart = YES; \
730 fixpref("extraintf");
733 return b_needsRestart;
736 #pragma mark - video filter handling
738 - (const char *)getFilterType:(const char *)psz_name
740 module_t *p_obj = module_find(psz_name);
745 if (module_provides(p_obj, "video splitter")) {
746 return "video-splitter";
747 } else if (module_provides(p_obj, "video filter")) {
748 return "video-filter";
749 } else if (module_provides(p_obj, "sub source")) {
751 } else if (module_provides(p_obj, "sub filter")) {
754 msg_Err(getIntf(), "Unknown video filter type.");
759 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
761 intf_thread_t *p_intf = getIntf();
764 playlist_t *p_playlist = pl_Get(p_intf);
765 char *psz_string, *psz_parser;
767 const char *psz_filter_type = [self getFilterType:psz_name];
768 if (!psz_filter_type) {
769 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
773 msg_Dbg(p_intf, "will turn filter '%s' %s", psz_name, b_on ? "on" : "off");
775 psz_string = var_InheritString(p_playlist, psz_filter_type);
778 if (psz_string == NULL) {
779 psz_string = strdup(psz_name);
780 } else if (strstr(psz_string, psz_name) == NULL) {
781 char *psz_tmp = strdup([[NSString stringWithFormat: @"%s:%s", psz_string, psz_name] UTF8String]);
783 psz_string = psz_tmp;
789 psz_parser = strstr(psz_string, psz_name);
791 if (*(psz_parser + strlen(psz_name)) == ':') {
792 memmove(psz_parser, psz_parser + strlen(psz_name) + 1,
793 strlen(psz_parser + strlen(psz_name) + 1) + 1);
798 /* Remove trailing : : */
799 if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
800 *(psz_string + strlen(psz_string) -1) = '\0';
806 var_SetString(p_playlist, psz_filter_type, psz_string);
808 /* Try to set non splitter filters on the fly */
809 if (strcmp(psz_filter_type, "video-splitter")) {
810 NSArray<NSValue *> *vouts = getVouts();
812 for (NSValue * val in vouts) {
813 vout_thread_t *p_vout = [val pointerValue];
814 var_SetString(p_vout, psz_filter_type, psz_string);
815 vlc_object_release(p_vout);
822 - (void)setVideoFilterProperty: (char const *)psz_property
823 forFilter: (char const *)psz_filter
824 withValue: (vlc_value_t)value
826 NSArray<NSValue *> *vouts = getVouts();
827 intf_thread_t *p_intf = getIntf();
831 playlist_t *p_playlist = pl_Get(p_intf);
834 bool b_is_command = false;
835 char const *psz_filter_type = [self getFilterType: psz_filter];
836 if (!psz_filter_type) {
837 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_filter);
841 if (vouts && [vouts count])
843 i_type = var_Type((vout_thread_t *)[[vouts firstObject] pointerValue], psz_property);
844 b_is_command = i_type & VLC_VAR_ISCOMMAND;
847 i_type = config_GetType(psz_property);
849 i_type &= VLC_VAR_CLASS;
850 if (i_type == VLC_VAR_BOOL)
851 var_SetBool(p_playlist, psz_property, value.b_bool);
852 else if (i_type == VLC_VAR_INTEGER)
853 var_SetInteger(p_playlist, psz_property, value.i_int);
854 else if (i_type == VLC_VAR_FLOAT)
855 var_SetFloat(p_playlist, psz_property, value.f_float);
856 else if (i_type == VLC_VAR_STRING)
857 var_SetString(p_playlist, psz_property, EnsureUTF8(value.psz_string));
861 "Module %s's %s variable is of an unsupported type ( %d )",
862 psz_filter, psz_property, i_type);
863 b_is_command = false;
868 for (NSValue *ptr in vouts)
870 vout_thread_t *p_vout = [ptr pointerValue];
871 var_SetChecked(p_vout, psz_property, i_type, value);
873 int i_cur_type = var_Type(p_vout, psz_property);
874 assert((i_cur_type & VLC_VAR_CLASS) == i_type);
875 assert(i_cur_type & VLC_VAR_ISCOMMAND);
880 for (NSValue *ptr in vouts)
881 vlc_object_release((vout_thread_t *)[ptr pointerValue]);
885 #pragma mark Media Key support
887 - (void)resetMediaKeyJump
889 b_mediakeyJustJumped = NO;
892 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
894 intf_thread_t *p_intf = getIntf();
898 b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
899 if (b_mediaKeySupport && !_mediaKeyController)
900 _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
902 VLCMain *main = [VLCMain sharedInstance];
903 if (b_mediaKeySupport && ([[[main playlist] model] hasChildren] ||
904 [[main inputManager] hasInput])) {
905 if (!b_mediaKeyTrapEnabled) {
906 b_mediaKeyTrapEnabled = YES;
907 msg_Dbg(p_intf, "Enable media key support");
908 [_mediaKeyController startWatchingMediaKeys];
911 if (b_mediaKeyTrapEnabled) {
912 b_mediaKeyTrapEnabled = NO;
913 msg_Dbg(p_intf, "Disable media key support");
914 [_mediaKeyController stopWatchingMediaKeys];
919 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
921 if (b_mediaKeySupport) {
922 assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
924 int keyCode = (([event data1] & 0xFFFF0000) >> 16);
925 int keyFlags = ([event data1] & 0x0000FFFF);
926 int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
927 int keyRepeat = (keyFlags & 0x1);
929 if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
932 if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
933 if (keyState == 0 && keyRepeat == 0)
935 else if (keyRepeat == 1) {
937 b_mediakeyJustJumped = YES;
938 [self performSelector:@selector(resetMediaKeyJump)
944 if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
945 if (keyState == 0 && keyRepeat == 0)
947 else if (keyRepeat == 1) {
948 [self backwardShort];
949 b_mediakeyJustJumped = YES;
950 [self performSelector:@selector(resetMediaKeyJump)
959 #pragma mark Apple Remote Control
961 - (void)startListeningWithAppleRemote
963 [_remote startListening: self];
966 - (void)stopListeningWithAppleRemote
968 [_remote stopListening:self];
971 #pragma mark - menu navigation
972 - (void)menuFocusActivate
974 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
975 if (p_input_thread == NULL)
978 input_Control(p_input_thread, INPUT_NAV_ACTIVATE, NULL );
979 vlc_object_release(p_input_thread);
982 - (void)moveMenuFocusLeft
984 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
985 if (p_input_thread == NULL)
988 input_Control(p_input_thread, INPUT_NAV_LEFT, NULL );
989 vlc_object_release(p_input_thread);
992 - (void)moveMenuFocusRight
994 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
995 if (p_input_thread == NULL)
998 input_Control(p_input_thread, INPUT_NAV_RIGHT, NULL );
999 vlc_object_release(p_input_thread);
1002 - (void)moveMenuFocusUp
1004 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1005 if (p_input_thread == NULL)
1008 input_Control(p_input_thread, INPUT_NAV_UP, NULL );
1009 vlc_object_release(p_input_thread);
1012 - (void)moveMenuFocusDown
1014 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
1015 if (p_input_thread == NULL)
1018 input_Control(p_input_thread, INPUT_NAV_DOWN, NULL );
1019 vlc_object_release(p_input_thread);
1022 /* Helper method for the remote control interface in order to trigger forward/backward and volume
1023 increase/decrease as long as the user holds the left/right, plus/minus button */
1024 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1026 intf_thread_t *p_intf = getIntf();
1030 if (b_remote_button_hold) {
1031 switch([buttonIdentifierNumber intValue]) {
1032 case kRemoteButtonRight_Hold:
1035 case kRemoteButtonLeft_Hold:
1038 case kRemoteButtonVolume_Plus_Hold:
1040 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1042 case kRemoteButtonVolume_Minus_Hold:
1044 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1047 if (b_remote_button_hold) {
1049 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1050 withObject:buttonIdentifierNumber
1056 /* Apple Remote callback */
1057 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1058 pressedDown: (BOOL) pressedDown
1059 clickCount: (unsigned int) count
1061 intf_thread_t *p_intf = getIntf();
1065 switch(buttonIdentifier) {
1066 case k2009RemoteButtonFullscreen:
1067 [self toggleFullscreen];
1069 case k2009RemoteButtonPlay:
1072 case kRemoteButtonPlay:
1074 [self toggleFullscreen];
1078 case kRemoteButtonVolume_Plus:
1079 if (config_GetInt("macosx-appleremote-sysvol"))
1080 [NSSound increaseSystemVolume];
1083 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1085 case kRemoteButtonVolume_Minus:
1086 if (config_GetInt("macosx-appleremote-sysvol"))
1087 [NSSound decreaseSystemVolume];
1090 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1092 case kRemoteButtonRight:
1093 if (config_GetInt("macosx-appleremote-prevnext"))
1098 case kRemoteButtonLeft:
1099 if (config_GetInt("macosx-appleremote-prevnext"))
1104 case kRemoteButtonRight_Hold:
1105 case kRemoteButtonLeft_Hold:
1106 case kRemoteButtonVolume_Plus_Hold:
1107 case kRemoteButtonVolume_Minus_Hold:
1108 /* simulate an event as long as the user holds the button */
1109 b_remote_button_hold = pressedDown;
1111 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1112 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1113 withObject:buttonIdentifierNumber];
1116 case kRemoteButtonMenu:
1117 [self showPosition];
1119 case kRemoteButtonPlay_Sleep:
1121 NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1122 [script executeAndReturnError:nil];
1126 /* Add here whatever you want other buttons to do */
1132 #pragma mark Key Shortcuts
1134 /*****************************************************************************
1135 * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1136 * shortcut key. If it is, pass it off to VLC for handling and return YES,
1137 * otherwise ignore it and return NO (where it will get handled by Cocoa).
1138 *****************************************************************************/
1140 - (BOOL)keyEvent:(NSEvent *)o_event
1142 BOOL eventHandled = NO;
1143 NSString * characters = [o_event charactersIgnoringModifiers];
1144 if ([characters length] > 0) {
1145 unichar key = [characters characterAtIndex: 0];
1148 input_thread_t * p_input = pl_CurrentInput(getIntf());
1149 if (p_input != NULL) {
1150 vout_thread_t *p_vout = input_GetVout(p_input);
1152 if (p_vout != NULL) {
1154 if (key == (unichar) 0x1b) {
1155 if (var_GetBool(p_vout, "fullscreen")) {
1156 [self toggleFullscreen];
1160 vlc_object_release(p_vout);
1162 vlc_object_release(p_input);
1166 return eventHandled;
1169 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1171 intf_thread_t *p_intf = getIntf();
1177 unsigned int i_pressed_modifiers = 0;
1180 i_pressed_modifiers = [o_event modifierFlags];
1182 if (i_pressed_modifiers & NSControlKeyMask)
1183 val.i_int |= KEY_MODIFIER_CTRL;
1185 if (i_pressed_modifiers & NSAlternateKeyMask)
1186 val.i_int |= KEY_MODIFIER_ALT;
1188 if (i_pressed_modifiers & NSShiftKeyMask)
1189 val.i_int |= KEY_MODIFIER_SHIFT;
1191 if (i_pressed_modifiers & NSCommandKeyMask)
1192 val.i_int |= KEY_MODIFIER_COMMAND;
1194 NSString * characters = [o_event charactersIgnoringModifiers];
1195 if ([characters length] > 0) {
1196 key = [[characters lowercaseString] characterAtIndex: 0];
1198 /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1199 if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1200 [self toggleFullscreen];
1206 case NSDeleteCharacter:
1207 case NSDeleteFunctionKey:
1208 case NSDeleteCharFunctionKey:
1209 case NSBackspaceCharacter:
1210 case NSUpArrowFunctionKey:
1211 case NSDownArrowFunctionKey:
1212 case NSEnterCharacter:
1213 case NSCarriageReturnCharacter:
1218 val.i_int |= CocoaKeyToVLC(key);
1220 BOOL b_found_key = NO;
1221 for (NSUInteger i = 0; i < [_usedHotkeys count]; i++) {
1222 NSString *str = [_usedHotkeys objectAtIndex:i];
1223 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1225 if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1226 (i_keyModifiers & NSShiftKeyMask) == (i_pressed_modifiers & NSShiftKeyMask) &&
1227 (i_keyModifiers & NSControlKeyMask) == (i_pressed_modifiers & NSControlKeyMask) &&
1228 (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1229 (i_keyModifiers & NSCommandKeyMask) == (i_pressed_modifiers & NSCommandKeyMask)) {
1236 var_SetInteger(p_intf->obj.libvlc, "key-pressed", val.i_int);
1244 - (void)updateCurrentlyUsedHotkeys
1246 NSMutableArray *mutArray = [[NSMutableArray alloc] init];
1247 /* Get the main Module */
1248 module_t *p_main = module_get_main();
1251 module_config_t *p_config;
1253 p_config = module_config_get (p_main, &confsize);
1255 for (size_t i = 0; i < confsize; i++) {
1256 module_config_t *p_item = p_config + i;
1258 if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1259 && !strncmp(p_item->psz_name , "key-", 4)
1260 && !EMPTY_STR(p_item->psz_text)) {
1261 if (p_item->value.psz)
1262 [mutArray addObject:toNSStr(p_item->value.psz)];
1265 module_config_free (p_config);
1267 _usedHotkeys = [[NSArray alloc] initWithArray:mutArray copyItems:YES];