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>
31 #import <vlc_actions.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)];
285 vlc_object_release(p_input);
290 - (NSString*)nameOfCurrentPlaylistItem
292 intf_thread_t *p_intf = getIntf();
296 input_thread_t *p_input = pl_CurrentInput(p_intf);
300 input_item_t *p_item = input_GetItem(p_input);
302 vlc_object_release(p_input);
306 char *psz_uri = input_item_GetURI(p_item);
308 vlc_object_release(p_input);
312 NSString *o_name = @"";
313 char *format = var_InheritString(getIntf(), "input-title-format");
315 char *formated = vlc_strfinput(p_input, format);
317 o_name = toNSStr(formated);
321 NSURL * o_url = [NSURL URLWithString:toNSStr(psz_uri)];
324 if ([o_name isEqualToString:@""]) {
325 if ([o_url isFileURL])
326 o_name = [[NSFileManager defaultManager] displayNameAtPath:[o_url path]];
328 o_name = [o_url absoluteString];
330 vlc_object_release(p_input);
343 [self backwardShort];
346 - (void)jumpWithValue:(char *)p_value forward:(BOOL)b_value
348 input_thread_t *p_input = pl_CurrentInput(getIntf());
352 int i_interval = var_InheritInteger( p_input, p_value );
353 if (i_interval > 0) {
354 mtime_t val = CLOCK_FREQ * i_interval;
357 var_SetInteger( p_input, "time-offset", val );
359 vlc_object_release(p_input);
362 - (void)forwardExtraShort
364 [self jumpWithValue:"extrashort-jump-size" forward:YES];
367 - (void)backwardExtraShort
369 [self jumpWithValue:"extrashort-jump-size" forward:NO];
374 [self jumpWithValue:"short-jump-size" forward:YES];
377 - (void)backwardShort
379 [self jumpWithValue:"short-jump-size" forward:NO];
382 - (void)forwardMedium
384 [self jumpWithValue:"medium-jump-size" forward:YES];
387 - (void)backwardMedium
389 [self jumpWithValue:"medium-jump-size" forward:NO];
394 [self jumpWithValue:"long-jump-size" forward:YES];
399 [self jumpWithValue:"long-jump-size" forward:NO];
404 intf_thread_t *p_intf = getIntf();
409 playlist_t * p_playlist = pl_Get(p_intf);
410 vout_thread_t *p_vout = getVout();
412 var_Get(p_playlist, "random", &val);
413 val.b_bool = !val.b_bool;
414 var_Set(p_playlist, "random", val);
417 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random On"));
418 vlc_object_release(p_vout);
420 config_PutInt(p_playlist, "random", 1);
425 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Random Off"));
426 vlc_object_release(p_vout);
428 config_PutInt(p_playlist, "random", 0);
434 intf_thread_t *p_intf = getIntf();
438 playlist_t * p_playlist = pl_Get(p_intf);
440 var_SetBool(p_playlist, "repeat", NO);
441 var_SetBool(p_playlist, "loop", YES);
442 config_PutInt(p_playlist, "repeat", NO);
443 config_PutInt(p_playlist, "loop", YES);
445 vout_thread_t *p_vout = getVout();
447 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat All"));
448 vlc_object_release(p_vout);
454 intf_thread_t *p_intf = getIntf();
458 playlist_t * p_playlist = pl_Get(p_intf);
460 var_SetBool(p_playlist, "repeat", YES);
461 var_SetBool(p_playlist, "loop", NO);
462 config_PutInt(p_playlist, "repeat", YES);
463 config_PutInt(p_playlist, "loop", NO);
465 vout_thread_t *p_vout = getVout();
467 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat One"));
468 vlc_object_release(p_vout);
474 intf_thread_t *p_intf = getIntf();
478 playlist_t * p_playlist = pl_Get(p_intf);
480 var_SetBool(p_playlist, "repeat", NO);
481 var_SetBool(p_playlist, "loop", NO);
482 config_PutInt(p_playlist, "repeat", NO);
483 config_PutInt(p_playlist, "loop", NO);
485 vout_thread_t *p_vout = getVout();
487 vout_OSDMessage(p_vout, VOUT_SPU_CHANNEL_OSD, "%s", _("Repeat Off"));
488 vlc_object_release(p_vout);
495 input_thread_t * p_input = pl_CurrentInput(getIntf());
497 timeA = var_GetInteger(p_input, "time");
498 vlc_object_release(p_input);
501 input_thread_t * p_input = pl_CurrentInput(getIntf());
503 timeB = var_GetInteger(p_input, "time");
504 vlc_object_release(p_input);
519 input_thread_t * p_input = pl_CurrentInput(getIntf());
521 mtime_t currentTime = var_GetInteger(p_input, "time");
522 if ( currentTime >= timeB || currentTime < timeA)
523 var_SetInteger(p_input, "time", timeA);
524 vlc_object_release(p_input);
531 intf_thread_t *p_intf = getIntf();
535 playlist_VolumeUp(pl_Get(p_intf), 1, NULL);
540 intf_thread_t *p_intf = getIntf();
544 playlist_VolumeDown(pl_Get(p_intf), 1, NULL);
549 intf_thread_t *p_intf = getIntf();
553 playlist_MuteToggle(pl_Get(p_intf));
558 intf_thread_t *p_intf = getIntf();
562 BOOL b_is_muted = NO;
563 b_is_muted = playlist_MuteGet(pl_Get(p_intf)) > 0;
570 intf_thread_t *p_intf = getIntf();
574 float volume = playlist_VolumeGet(pl_Get(p_intf));
576 return lroundf(volume * AOUT_VOLUME_DEFAULT);
579 - (void)setVolume: (int)i_value
581 intf_thread_t *p_intf = getIntf();
585 if (i_value >= self.maxVolume)
586 i_value = self.maxVolume;
588 float f_value = i_value / (float)AOUT_VOLUME_DEFAULT;
590 playlist_VolumeSet(pl_Get(p_intf), f_value);
595 if (f_maxVolume == 0.) {
596 f_maxVolume = (float)var_InheritInteger(getIntf(), "macosx-max-volume") / 100. * AOUT_VOLUME_DEFAULT;
602 - (void)addSubtitlesToCurrentInput:(NSArray *)paths
604 input_thread_t * p_input = pl_CurrentInput(getIntf());
608 NSUInteger count = [paths count];
610 for (int i = 0; i < count ; i++) {
611 char *mrl = vlc_path2uri([[[paths objectAtIndex:i] path] UTF8String], NULL);
614 msg_Dbg(getIntf(), "loading subs from %s", mrl);
616 int i_result = input_AddSlave(p_input, SLAVE_TYPE_SPU, mrl, true, true);
617 if (i_result != VLC_SUCCESS)
618 msg_Warn(getIntf(), "unable to load subtitles from '%s'", mrl);
621 vlc_object_release(p_input);
626 input_thread_t *p_input = pl_CurrentInput(getIntf());
627 if (p_input != NULL) {
628 vout_thread_t *p_vout = input_GetVout(p_input);
629 if (p_vout != NULL) {
630 var_SetInteger(getIntf()->obj.libvlc, "key-action", ACTIONID_POSITION);
631 vlc_object_release(p_vout);
633 vlc_object_release(p_input);
637 #pragma mark - Drop support for files into the video, controls bar or drop box
639 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
641 NSArray *items = [[[VLCMain sharedInstance] playlist] createItemsFromExternalPasteboard:[sender draggingPasteboard]];
643 if (items.count == 0)
646 [[[VLCMain sharedInstance] playlist] addPlaylistItems:items tryAsSubtitle:YES];
650 #pragma mark - video output stuff
652 - (void)setAspectRatioIsLocked:(BOOL)b_value
654 config_PutInt(getIntf(), "macosx-lock-aspect-ratio", b_value);
657 - (BOOL)aspectRatioIsLocked
659 return config_GetInt(getIntf(), "macosx-lock-aspect-ratio");
662 - (void)toggleFullscreen
664 intf_thread_t *p_intf = getIntf();
668 vout_thread_t *p_vout = getVoutForActiveWindow();
670 BOOL b_fs = var_ToggleBool(p_vout, "fullscreen");
671 var_SetBool(pl_Get(p_intf), "fullscreen", b_fs);
672 vlc_object_release(p_vout);
673 } else { // e.g. lion fullscreen toggle
674 BOOL b_fs = var_ToggleBool(pl_Get(p_intf), "fullscreen");
675 [[[VLCMain sharedInstance] voutController] setFullscreen:b_fs forWindow:nil withAnimation:YES];
679 #pragma mark - uncommon stuff
681 - (BOOL)fixIntfSettings
683 NSMutableString * o_workString;
684 NSRange returnedRange;
686 BOOL b_needsRestart = NO;
688 #define fixpref(pref) \
689 o_workString = [[NSMutableString alloc] initWithFormat:@"%s", config_GetPsz(getIntf(), pref)]; \
690 if ([o_workString length] > 0) \
692 returnedRange = [o_workString rangeOfString:@"macosx" options: NSCaseInsensitiveSearch]; \
693 if (returnedRange.location != NSNotFound) \
695 if ([o_workString isEqualToString:@"macosx"]) \
696 [o_workString setString:@""]; \
697 fullRange = NSMakeRange(0, [o_workString length]); \
698 [o_workString replaceOccurrencesOfString:@":macosx" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
699 fullRange = NSMakeRange(0, [o_workString length]); \
700 [o_workString replaceOccurrencesOfString:@"macosx:" withString:@"" options: NSCaseInsensitiveSearch range: fullRange]; \
702 config_PutPsz(getIntf(), pref, [o_workString UTF8String]); \
703 b_needsRestart = YES; \
708 fixpref("extraintf");
711 return b_needsRestart;
714 #pragma mark - video filter handling
716 - (const char *)getFilterType:(const char *)psz_name
718 module_t *p_obj = module_find(psz_name);
723 if (module_provides(p_obj, "video splitter")) {
724 return "video-splitter";
725 } else if (module_provides(p_obj, "video filter")) {
726 return "video-filter";
727 } else if (module_provides(p_obj, "sub source")) {
729 } else if (module_provides(p_obj, "sub filter")) {
732 msg_Err(getIntf(), "Unknown video filter type.");
737 - (void)setVideoFilter: (const char *)psz_name on:(BOOL)b_on
739 intf_thread_t *p_intf = getIntf();
742 playlist_t *p_playlist = pl_Get(p_intf);
743 char *psz_string, *psz_parser;
745 const char *psz_filter_type = [self getFilterType:psz_name];
746 if (!psz_filter_type) {
747 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_name);
751 msg_Dbg(p_intf, "will turn filter '%s' %s", psz_name, b_on ? "on" : "off");
753 psz_string = var_InheritString(p_playlist, psz_filter_type);
756 if (psz_string == NULL) {
757 psz_string = strdup(psz_name);
758 } else if (strstr(psz_string, psz_name) == NULL) {
759 char *psz_tmp = strdup([[NSString stringWithFormat: @"%s:%s", psz_string, psz_name] UTF8String]);
761 psz_string = psz_tmp;
767 psz_parser = strstr(psz_string, psz_name);
769 if (*(psz_parser + strlen(psz_name)) == ':') {
770 memmove(psz_parser, psz_parser + strlen(psz_name) + 1,
771 strlen(psz_parser + strlen(psz_name) + 1) + 1);
776 /* Remove trailing : : */
777 if (strlen(psz_string) > 0 && *(psz_string + strlen(psz_string) -1) == ':')
778 *(psz_string + strlen(psz_string) -1) = '\0';
784 var_SetString(p_playlist, psz_filter_type, psz_string);
786 /* Try to set non splitter filters on the fly */
787 if (strcmp(psz_filter_type, "video-splitter")) {
788 NSArray<NSValue *> *vouts = getVouts();
790 for (NSValue * val in vouts) {
791 vout_thread_t *p_vout = [val pointerValue];
792 var_SetString(p_vout, psz_filter_type, psz_string);
793 vlc_object_release(p_vout);
800 - (void)setVideoFilterProperty: (char const *)psz_property
801 forFilter: (char const *)psz_filter
802 withValue: (vlc_value_t)value
804 NSArray<NSValue *> *vouts = getVouts();
805 intf_thread_t *p_intf = getIntf();
806 playlist_t *p_playlist = pl_Get(p_intf);
810 bool b_is_command = false;
811 char const *psz_filter_type = [self getFilterType: psz_filter];
812 if (!psz_filter_type) {
813 msg_Err(p_intf, "Unable to find filter module \"%s\".", psz_filter);
817 if (vouts && [vouts count])
819 i_type = var_Type((vout_thread_t *)[[vouts firstObject] pointerValue], psz_property);
820 b_is_command = i_type & VLC_VAR_ISCOMMAND;
823 i_type = config_GetType(psz_property);
825 i_type &= VLC_VAR_CLASS;
826 if (i_type == VLC_VAR_BOOL)
827 var_SetBool(p_playlist, psz_property, value.b_bool);
828 else if (i_type == VLC_VAR_INTEGER)
829 var_SetInteger(p_playlist, psz_property, value.i_int);
830 else if (i_type == VLC_VAR_FLOAT)
831 var_SetFloat(p_playlist, psz_property, value.f_float);
832 else if (i_type == VLC_VAR_STRING)
833 var_SetString(p_playlist, psz_property, EnsureUTF8(value.psz_string));
837 "Module %s's %s variable is of an unsupported type ( %d )",
838 psz_filter, psz_property, i_type);
839 b_is_command = false;
844 for (NSValue *ptr in vouts)
846 vout_thread_t *p_vout = [ptr pointerValue];
847 var_SetChecked(p_vout, psz_property, i_type, value);
849 int i_cur_type = var_Type(p_vout, psz_property);
850 assert((i_cur_type & VLC_VAR_CLASS) == i_type);
851 assert(i_cur_type & VLC_VAR_ISCOMMAND);
856 for (NSValue *ptr in vouts)
857 vlc_object_release((vout_thread_t *)[ptr pointerValue]);
861 #pragma mark Media Key support
863 - (void)resetMediaKeyJump
865 b_mediakeyJustJumped = NO;
868 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
870 intf_thread_t *p_intf = getIntf();
874 b_mediaKeySupport = var_InheritBool(p_intf, "macosx-mediakeys");
875 if (b_mediaKeySupport && !_mediaKeyController)
876 _mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
878 VLCMain *main = [VLCMain sharedInstance];
879 if (b_mediaKeySupport && ([[[main playlist] model] hasChildren] ||
880 [[main inputManager] hasInput])) {
881 if (!b_mediaKeyTrapEnabled) {
882 b_mediaKeyTrapEnabled = YES;
883 msg_Dbg(p_intf, "Enable media key support");
884 [_mediaKeyController startWatchingMediaKeys];
887 if (b_mediaKeyTrapEnabled) {
888 b_mediaKeyTrapEnabled = NO;
889 msg_Dbg(p_intf, "Disable media key support");
890 [_mediaKeyController stopWatchingMediaKeys];
895 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
897 if (b_mediaKeySupport) {
898 assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
900 int keyCode = (([event data1] & 0xFFFF0000) >> 16);
901 int keyFlags = ([event data1] & 0x0000FFFF);
902 int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
903 int keyRepeat = (keyFlags & 0x1);
905 if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
908 if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
909 if (keyState == 0 && keyRepeat == 0)
911 else if (keyRepeat == 1) {
913 b_mediakeyJustJumped = YES;
914 [self performSelector:@selector(resetMediaKeyJump)
920 if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
921 if (keyState == 0 && keyRepeat == 0)
923 else if (keyRepeat == 1) {
924 [self backwardShort];
925 b_mediakeyJustJumped = YES;
926 [self performSelector:@selector(resetMediaKeyJump)
935 #pragma mark Apple Remote Control
937 - (void)startListeningWithAppleRemote
939 [_remote startListening: self];
942 - (void)stopListeningWithAppleRemote
944 [_remote stopListening:self];
947 #pragma mark - menu navigation
948 - (void)menuFocusActivate
950 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
951 if (p_input_thread == NULL)
954 input_Control(p_input_thread, INPUT_NAV_ACTIVATE, NULL );
955 vlc_object_release(p_input_thread);
958 - (void)moveMenuFocusLeft
960 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
961 if (p_input_thread == NULL)
964 input_Control(p_input_thread, INPUT_NAV_LEFT, NULL );
965 vlc_object_release(p_input_thread);
968 - (void)moveMenuFocusRight
970 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
971 if (p_input_thread == NULL)
974 input_Control(p_input_thread, INPUT_NAV_RIGHT, NULL );
975 vlc_object_release(p_input_thread);
978 - (void)moveMenuFocusUp
980 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
981 if (p_input_thread == NULL)
984 input_Control(p_input_thread, INPUT_NAV_UP, NULL );
985 vlc_object_release(p_input_thread);
988 - (void)moveMenuFocusDown
990 input_thread_t *p_input_thread = pl_CurrentInput(getIntf());
991 if (p_input_thread == NULL)
994 input_Control(p_input_thread, INPUT_NAV_DOWN, NULL );
995 vlc_object_release(p_input_thread);
998 /* Helper method for the remote control interface in order to trigger forward/backward and volume
999 increase/decrease as long as the user holds the left/right, plus/minus button */
1000 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1002 intf_thread_t *p_intf = getIntf();
1006 if (b_remote_button_hold) {
1007 switch([buttonIdentifierNumber intValue]) {
1008 case kRemoteButtonRight_Hold:
1011 case kRemoteButtonLeft_Hold:
1014 case kRemoteButtonVolume_Plus_Hold:
1016 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1018 case kRemoteButtonVolume_Minus_Hold:
1020 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1023 if (b_remote_button_hold) {
1025 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1026 withObject:buttonIdentifierNumber
1032 /* Apple Remote callback */
1033 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1034 pressedDown: (BOOL) pressedDown
1035 clickCount: (unsigned int) count
1037 intf_thread_t *p_intf = getIntf();
1041 switch(buttonIdentifier) {
1042 case k2009RemoteButtonFullscreen:
1043 [self toggleFullscreen];
1045 case k2009RemoteButtonPlay:
1048 case kRemoteButtonPlay:
1050 [self toggleFullscreen];
1054 case kRemoteButtonVolume_Plus:
1055 if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1056 [NSSound increaseSystemVolume];
1059 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_UP);
1061 case kRemoteButtonVolume_Minus:
1062 if (config_GetInt(getIntf(), "macosx-appleremote-sysvol"))
1063 [NSSound decreaseSystemVolume];
1066 var_SetInteger(p_intf->obj.libvlc, "key-action", ACTIONID_VOL_DOWN);
1068 case kRemoteButtonRight:
1069 if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1074 case kRemoteButtonLeft:
1075 if (config_GetInt(getIntf(), "macosx-appleremote-prevnext"))
1080 case kRemoteButtonRight_Hold:
1081 case kRemoteButtonLeft_Hold:
1082 case kRemoteButtonVolume_Plus_Hold:
1083 case kRemoteButtonVolume_Minus_Hold:
1084 /* simulate an event as long as the user holds the button */
1085 b_remote_button_hold = pressedDown;
1087 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier];
1088 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1089 withObject:buttonIdentifierNumber];
1092 case kRemoteButtonMenu:
1093 [self showPosition];
1095 case kRemoteButtonPlay_Sleep:
1097 NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1098 [script executeAndReturnError:nil];
1102 /* Add here whatever you want other buttons to do */
1108 #pragma mark Key Shortcuts
1110 /*****************************************************************************
1111 * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1112 * shortcut key. If it is, pass it off to VLC for handling and return YES,
1113 * otherwise ignore it and return NO (where it will get handled by Cocoa).
1114 *****************************************************************************/
1116 - (BOOL)keyEvent:(NSEvent *)o_event
1118 BOOL eventHandled = NO;
1119 NSString * characters = [o_event charactersIgnoringModifiers];
1120 if ([characters length] > 0) {
1121 unichar key = [characters characterAtIndex: 0];
1124 input_thread_t * p_input = pl_CurrentInput(getIntf());
1125 if (p_input != NULL) {
1126 vout_thread_t *p_vout = input_GetVout(p_input);
1128 if (p_vout != NULL) {
1130 if (key == (unichar) 0x1b) {
1131 if (var_GetBool(p_vout, "fullscreen")) {
1132 [self toggleFullscreen];
1136 vlc_object_release(p_vout);
1138 vlc_object_release(p_input);
1142 return eventHandled;
1145 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1147 intf_thread_t *p_intf = getIntf();
1153 unsigned int i_pressed_modifiers = 0;
1156 i_pressed_modifiers = [o_event modifierFlags];
1158 if (i_pressed_modifiers & NSControlKeyMask)
1159 val.i_int |= KEY_MODIFIER_CTRL;
1161 if (i_pressed_modifiers & NSAlternateKeyMask)
1162 val.i_int |= KEY_MODIFIER_ALT;
1164 if (i_pressed_modifiers & NSShiftKeyMask)
1165 val.i_int |= KEY_MODIFIER_SHIFT;
1167 if (i_pressed_modifiers & NSCommandKeyMask)
1168 val.i_int |= KEY_MODIFIER_COMMAND;
1170 NSString * characters = [o_event charactersIgnoringModifiers];
1171 if ([characters length] > 0) {
1172 key = [[characters lowercaseString] characterAtIndex: 0];
1174 /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1175 if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1176 [self toggleFullscreen];
1182 case NSDeleteCharacter:
1183 case NSDeleteFunctionKey:
1184 case NSDeleteCharFunctionKey:
1185 case NSBackspaceCharacter:
1186 case NSUpArrowFunctionKey:
1187 case NSDownArrowFunctionKey:
1188 case NSEnterCharacter:
1189 case NSCarriageReturnCharacter:
1194 val.i_int |= CocoaKeyToVLC(key);
1196 BOOL b_found_key = NO;
1197 for (NSUInteger i = 0; i < [_usedHotkeys count]; i++) {
1198 NSString *str = [_usedHotkeys objectAtIndex:i];
1199 unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1201 if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1202 (i_keyModifiers & NSShiftKeyMask) == (i_pressed_modifiers & NSShiftKeyMask) &&
1203 (i_keyModifiers & NSControlKeyMask) == (i_pressed_modifiers & NSControlKeyMask) &&
1204 (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1205 (i_keyModifiers & NSCommandKeyMask) == (i_pressed_modifiers & NSCommandKeyMask)) {
1212 var_SetInteger(p_intf->obj.libvlc, "key-pressed", val.i_int);
1220 - (void)updateCurrentlyUsedHotkeys
1222 NSMutableArray *mutArray = [[NSMutableArray alloc] init];
1223 /* Get the main Module */
1224 module_t *p_main = module_get_main();
1227 module_config_t *p_config;
1229 p_config = module_config_get (p_main, &confsize);
1231 for (size_t i = 0; i < confsize; i++) {
1232 module_config_t *p_item = p_config + i;
1234 if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1235 && !strncmp(p_item->psz_name , "key-", 4)
1236 && !EMPTY_STR(p_item->psz_text)) {
1237 if (p_item->value.psz)
1238 [mutArray addObject:toNSStr(p_item->value.psz)];
1241 module_config_free (p_config);
1243 _usedHotkeys = [[NSArray alloc] initWithArray:mutArray copyItems:YES];