Shortcut key support

This commit is contained in:
Tom Wheeler
2020-04-17 11:13:57 -06:00
parent 95deb647a6
commit 131cf7117f
14 changed files with 393 additions and 19 deletions

View File

@@ -10,15 +10,19 @@ macOS 10.10 Yosemite or higher version.
## Installation
Download the [latest stable version](https://github.com/CocoaHeadsBrasil/MuteUnmuteMic/releases/download/1.4.2/Un.MuteMic.zip) or clone this repo and build & run.
Download or clone this repo and build & run.
To build as a macos app: In XCode: choose _Product_ -> _Archive_ -> Click _Distribute App_ button -> Choose _Copy App_ -> Choose the export folder and click _Export_
## Usage
![[Un]MuteMic usage](https://cloud.githubusercontent.com/assets/235208/10419593/143171fc-704a-11e5-8270-374ca898685b.gif)
Use the shortcut key, click the icon, or open the menu and click _Toggle Mute_.
## Authors
CocoaHeads Brasil: [http://www.cocoaheads.com.br/](http://www.cocoaheads.com.br/)
* CocoaHeads Brasil: [http://www.cocoaheads.com.br/](http://www.cocoaheads.com.br/)
* [racecarparts](https://github.com/racecarparts)
* Shoutout to: [https://github.com/jaz303/JFHotkeyManager](https://github.com/jaz303/JFHotkeyManager)
## License

View File

@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
D9A9F66F1BC35EDC004395E8 /* AudioMixer.c in Sources */ = {isa = PBXBuildFile; fileRef = D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */; };
ECD9888D2448C7B2000626C4 /* JFHotkeyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = ECD9888C2448C7B2000626C4 /* JFHotkeyManager.m */; };
ECF208BE1BAF2DE6000D3C2C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF208BD1BAF2DE6000D3C2C /* AppDelegate.m */; };
ECF208C11BAF2DE6000D3C2C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = ECF208C01BAF2DE6000D3C2C /* main.m */; };
ECF208C31BAF2DE6000D3C2C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ECF208C21BAF2DE6000D3C2C /* Assets.xcassets */; };
@@ -17,6 +18,8 @@
/* Begin PBXFileReference section */
D9A9F66D1BC35EDC004395E8 /* AudioMixer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = AudioMixer.c; path = "[Un]MuteMic/AudioMixer.c"; sourceTree = "<group>"; };
D9A9F66E1BC35EDC004395E8 /* AudioMixer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioMixer.h; path = "[Un]MuteMic/AudioMixer.h"; sourceTree = "<group>"; };
ECD9888B2448C785000626C4 /* JFHotkeyManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JFHotkeyManager.h; sourceTree = "<group>"; };
ECD9888C2448C7B2000626C4 /* JFHotkeyManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JFHotkeyManager.m; sourceTree = "<group>"; };
ECF208B91BAF2DE6000D3C2C /* [Un]MuteMic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "[Un]MuteMic.app"; sourceTree = BUILT_PRODUCTS_DIR; };
ECF208BC1BAF2DE6000D3C2C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
ECF208BD1BAF2DE6000D3C2C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -73,6 +76,8 @@
ECF208C71BAF2DE6000D3C2C /* Info.plist */,
ECF208BF1BAF2DE6000D3C2C /* Supporting Files */,
D9A9F6701BC5830F004395E8 /* AudioMixer */,
ECD9888B2448C785000626C4 /* JFHotkeyManager.h */,
ECD9888C2448C7B2000626C4 /* JFHotkeyManager.m */,
);
path = "[Un]MuteMic";
sourceTree = "<group>";
@@ -116,6 +121,7 @@
TargetAttributes = {
ECF208B81BAF2DE6000D3C2C = {
CreatedOnToolsVersion = 7.0;
LastSwiftMigration = 1140;
};
};
};
@@ -124,6 +130,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@@ -157,6 +164,7 @@
ECF208C11BAF2DE6000D3C2C /* main.m in Sources */,
ECF208BE1BAF2DE6000D3C2C /* AppDelegate.m in Sources */,
D9A9F66F1BC35EDC004395E8 /* AudioMixer.c in Sources */,
ECD9888D2448C7B2000626C4 /* JFHotkeyManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -257,11 +265,15 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "[Un]MuteMic/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = br.com.cocoaheads.MuteUnmuteMic;
PRODUCT_NAME = "[Un]MuteMic";
SWIFT_OBJC_BRIDGING_HEADER = "[Un]MuteMic-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -269,11 +281,14 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = "[Un]MuteMic/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = br.com.cocoaheads.MuteUnmuteMic;
PRODUCT_NAME = "[Un]MuteMic";
SWIFT_OBJC_BRIDGING_HEADER = "[Un]MuteMic-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;
};

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:[Un]MuteMic.xcodeproj">
location = "self:/Users/tom.wheeler/dev/MuteUnmuteMic/[Un]MuteMic.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -7,14 +7,18 @@
#import "AppDelegate.h"
#import "AudioMixer.h"
#import <Carbon/Carbon.h>
#import "JFHotkeyManager.h"
static NSInteger const kDefaultVolume = 70;
static NSInteger const kDefaultVolume = 80;
@interface AppDelegate ()
@property (nonatomic) NSStatusItem *menuItem;
@property (nonatomic) BOOL muted;
@property (nonatomic) NSInteger inputVolumeToUnmute;
@property JFHotkeyManager *hkm;
//@property __JFHotkey *shortcut;
@end
@@ -25,6 +29,13 @@ static NSInteger const kDefaultVolume = 70;
[self initDefaults];
[self configureStatusBar];
[self updateInputVolume];
// Initialise a new hotkey manager
_hkm = [[JFHotkeyManager alloc] init];
// Bind the hotkey by key code reference number and modifiers:
// want modifiers? use `withModifiers:cmdKey + optionKey + shiftKey`
JFHotKeyRef hk = [_hkm bindKeyRef:80 withModifiers:0 target:self action:@selector(toggleMute)];
}
- (void)initDefaults
@@ -39,7 +50,7 @@ static NSInteger const kDefaultVolume = 70;
NSStatusItem *menuItem =
[statusBar statusItemWithLength:NSVariableStatusItemLength];
[menuItem setToolTip:@"[Un]MuteMic by CocoaHeads Brazil"];
[menuItem setToolTip:@"i haz teh mutez"];
[menuItem setImage:[NSImage imageNamed:@"mic_on"]];
[menuItem setHighlightMode:YES];
@@ -61,6 +72,11 @@ static NSInteger const kDefaultVolume = 70;
}
}
- (IBAction)didToggleMute:(NSMenuItem *)sender
{
[self toggleMute];
}
- (void)toggleMute
{
self.muted = !self.muted;
@@ -96,11 +112,14 @@ static NSInteger const kDefaultVolume = 70;
SetHardwareMute(muted);
// set image
self.menuItem.image = [NSImage imageNamed:imageName];
self.menuItem.button.image = [NSImage imageNamed:imageName];
}
- (void)showMenu
{
// NSMenuItem *mi = [self.menu itemWithTitle:@"Shortcut"];
[self.menuItem popUpStatusItemMenu:self.menu];
}
@@ -115,4 +134,15 @@ static NSInteger const kDefaultVolume = 70;
[self updateInputVolume];
}
- (IBAction)didCallSetShortcut:(NSMenuItem *)sender
{
NSMenuItem *menuItem = (NSMenuItem*) sender;
NSString *menuString = menuItem.title;
if ([menuString isEqualToString:@"Shortcut"])
{
NSLog(@"%@", menuString);
}
}
@end

View File

@@ -1,21 +1,21 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "mic_off.png",
"idiom" : "mac",
"scale" : "1x"
},
{
"idiom" : "mac",
"filename" : "mic_off@2x.png",
"idiom" : "mac",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
"template-rendering-intent" : "original"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 729 B

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9046" systemVersion="14E46" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9046"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -19,6 +20,12 @@
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu id="d1i-xa-wlo">
<items>
<menuItem title="Toggle Mute [F19]" id="jbg-be-dZJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="didToggleMute:" target="Voe-Tx-rLC" id="mqy-oo-eJP"/>
</connections>
</menuItem>
<menuItem title="Input Volume" id="w4W-DI-DEg">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Input Volume" autoenablesItems="NO" id="iVL-f8-mAL">
@@ -35,13 +42,13 @@
<action selector="didSetVolumeInput:" target="Voe-Tx-rLC" id="b1z-78-Fgk"/>
</connections>
</menuItem>
<menuItem title="80" tag="5" id="OoN-lP-2Ll">
<menuItem title="80" state="on" tag="5" id="OoN-lP-2Ll">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="didSetVolumeInput:" target="Voe-Tx-rLC" id="itM-AQ-plW"/>
</connections>
</menuItem>
<menuItem title="70" state="on" id="ZNu-ZY-zwa">
<menuItem title="70" id="ZNu-ZY-zwa">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="didSetVolumeInput:" target="Voe-Tx-rLC" id="Ykp-jU-4Cw"/>
@@ -73,7 +80,7 @@
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="30G-NN-Naz"/>
</connections>
<point key="canvasLocation" x="-268" y="-315.5"/>
<point key="canvasLocation" x="-269" y="-276"/>
</menu>
</objects>
</document>

View File

@@ -0,0 +1,50 @@
//
// JFHotkeyManager.h
// JFHotkeyManager
//
// Created by Jason Frame on 23/02/2010.
// Copyright 2010 Jason Frame. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <Carbon/Carbon.h>
typedef NSUInteger JFHotKeyRef;
@interface JFHotkeyManager : NSObject {
NSMutableDictionary *_hotkeys;
UInt32 _nextId;
}
- (JFHotKeyRef)bind:(NSString *)commandString
target:(id)target
action:(SEL)selector;
- (JFHotKeyRef)bindKeyRef:(UInt32)keyRef
withModifiers:(UInt32)modifiers
target:(id)target
action:(SEL)selector;
- (void)unbind:(JFHotKeyRef)keyRef;
- (void)_dispatch:(NSUInteger)hotkeyId;
@end
@interface __JFHotkey : NSObject {
id _target;
SEL _action;
EventHotKeyRef _ref;
}
- (id)initWithHotkeyID:(UInt32)hotkeyID
keyRef:(UInt32)keyRef
modifiers:(UInt32)modifiers
target:(id)target
action:(SEL)selector;
- (void)invoke;
@end

View File

@@ -0,0 +1,252 @@
//
// JFHotkeyManager.m
// JFHotkeyManager
//
// Created by Jason Frame on 23/02/2010.
// Copyright 2010 Jason Frame. All rights reserved.
//
#import "JFHotkeyManager.h"
static OSStatus hotkeyHandler(EventHandlerCallRef hnd,
EventRef evt,
void *data) {
EventHotKeyID hkID;
GetEventParameter(evt, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hkID), NULL, &hkID);
JFHotkeyManager *hkm = (__bridge JFHotkeyManager *)data;
[hkm _dispatch:hkID.id];
return noErr;
}
static NSMutableDictionary *keyMap;
static NSMutableDictionary *modMap;
static void mapKey(NSString *s, NSUInteger key) {
keyMap[s] = @(key);
}
static void mapMod(NSString *s, NSUInteger mod) {
modMap[s] = @(mod);
}
@implementation __JFHotkey
- (id)initWithHotkeyID:(UInt32)hotkeyID
keyRef:(UInt32)keyRef
modifiers:(UInt32)modifiers
target:(id)target
action:(SEL)selector {
if (self = [super init]) {
EventHotKeyID kID;
kID.id = kID.signature = hotkeyID;
RegisterEventHotKey(keyRef, modifiers, kID, GetApplicationEventTarget(), 0, &_ref);
_target = target;
_action = selector;
}
return self;
}
- (void)dealloc {
UnregisterEventHotKey(_ref);
}
- (void)invoke {
IMP imp = [_target methodForSelector:_action];
void (*func)(id, SEL) = (void *)imp;
func(_target, _action);
}
@end
@implementation JFHotkeyManager
+ (void)initialize {
modMap = [[NSMutableDictionary alloc] init];
keyMap = [[NSMutableDictionary alloc] init];
// Is this guaranteed to be the same across systems/keyboards?
// Probably not.
//
// Keys
mapKey(@"a", 0x00);
mapKey(@"b", 0x0B);
mapKey(@"c", 0x09);
mapKey(@"d", 0x02);
mapKey(@"e", 0x0E);
mapKey(@"f", 0x03);
mapKey(@"g", 0x05);
mapKey(@"h", 0x04);
mapKey(@"i", 0x22);
mapKey(@"j", 0x26);
mapKey(@"k", 0x29);
mapKey(@"l", 0x25);
mapKey(@"m", 0x2E);
mapKey(@"n", 0x2D);
mapKey(@"o", 0x1F);
mapKey(@"p", 0x23);
mapKey(@"q", 0x0C);
mapKey(@"r", 0x0F);
mapKey(@"s", 0x01);
mapKey(@"t", 0x11);
mapKey(@"u", 0x20);
mapKey(@"v", 0x09);
mapKey(@"w", 0x0D);
mapKey(@"x", 0x07);
mapKey(@"y", 0x10);
mapKey(@"z", 0x06);
mapKey(@"0", 0x1D);
mapKey(@"1", 0x12);
mapKey(@"2", 0x13);
mapKey(@"3", 0x14);
mapKey(@"4", 0x15);
mapKey(@"5", 0x17);
mapKey(@"6", 0x16);
mapKey(@"7", 0x1A);
mapKey(@"8", 0x1C);
mapKey(@"9", 0x19);
mapKey(@",", 0x2B);
mapKey(@".", 0x2F);
mapKey(@"/", 0x2C);
mapKey(@"f1", 0x7A);
mapKey(@"f2", 0x79);
mapKey(@"f3", 0x63);
mapKey(@"f4", 0x76);
mapKey(@"f5", 0x60);
mapKey(@"f6", 0x61);
mapKey(@"f7", 0x62);
mapKey(@"f8", 0x64);
mapKey(@"f9", 0x65);
mapKey(@"f10", 0x6D);
mapKey(@"f11", 0x67);
mapKey(@"f12", 0x6F);
mapKey(@"f13", 0x69);
mapKey(@"f14", 0x6B);
mapKey(@"f15", 0x71);
mapKey(@"escape", 0x35);
mapKey(@"esc", 0x35);
mapKey(@"space", 0x31);
mapKey(@"enter", 0x24);
mapKey(@"tab", 0x30);
mapKey(@"backspace",0x33);
mapKey(@"bkspc", 0x33);
mapKey(@"left", 0x7B);
mapKey(@"right", 0x7C);
mapKey(@"down", 0x7D);
mapKey(@"up", 0x7E);
//
// Modifiers
mapMod(@"apple", cmdKey);
mapMod(@"ctrl", controlKey);
mapMod(@"ctl", controlKey);
mapMod(@"control", controlKey);
mapMod(@"opt", optionKey);
mapMod(@"option", optionKey);
mapMod(@"alt", optionKey);
mapMod(@"cmd", cmdKey);
mapMod(@"command", cmdKey);
mapMod(@"apple", cmdKey);
mapMod(@"shift", shiftKey);
}
- (id)init {
if (self = [super init]) {
_hotkeys = [[NSMutableDictionary alloc] init];
_nextId = 1;
EventTypeSpec evtType;
evtType.eventClass = kEventClassKeyboard;
evtType.eventKind = kEventHotKeyPressed;
InstallApplicationEventHandler(&hotkeyHandler,
1,
&evtType,
(__bridge void*)self,
NULL);
}
return self;
}
- (JFHotKeyRef)bind:(NSString *)commandString
target:(id)target
action:(SEL)selector {
UInt32 keyRef = 0;
UInt32 modifiers = 0;
NSArray *bits = [[commandString lowercaseString] componentsSeparatedByString:@" "];
for (NSString *bit in bits) {
NSNumber *code = modMap[bit];
if (code != nil) {
modifiers += [code unsignedIntValue];
continue;
}
code = keyMap[bit];
if (code != nil) {
keyRef = [code unsignedIntValue];
continue;
}
}
return [self bindKeyRef:keyRef
withModifiers:modifiers
target:target
action:selector];
}
- (JFHotKeyRef)bindKeyRef:(UInt32)keyRef
withModifiers:(UInt32)modifiers
target:(id)target
action:(SEL)selector {
UInt32 keyID = _nextId;
_nextId++;
__JFHotkey *hk = [[__JFHotkey alloc] initWithHotkeyID:keyID
keyRef:keyRef
modifiers:modifiers
target:target
action:selector];
_hotkeys[@(keyID)] = hk;
return keyID;
}
- (void)unbind:(JFHotKeyRef)keyRef {
[_hotkeys removeObjectForKey:@(keyRef)];
}
- (void)_dispatch:(NSUInteger)hotkeyId {
[_hotkeys[@(hotkeyId)] invoke];
}
@end